Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 15 additions & 10 deletions .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,25 @@
## Prerequisites

You need macOS, Xcode 26.6 or later, and Swift 6.3 or later. You also need
SwiftLint 0.65.0 and [actionlint](https://github.com/rhysd/actionlint).
XcodeGen 2.46.0, SwiftLint 0.65.0, actionlint 1.7.12, Node.js, and npm.
Install the development tools with:

```bash
brew install actionlint swiftlint
brew install actionlint node swiftlint xcodegen
```

The future companion app also requires
[XcodeGen](https://github.com/yonaskolb/XcodeGen).

## Setup

Fork the repository, clone your fork, and create a branch from `main`. Resolve
the package dependencies once after cloning:
Fork the repository, clone your fork, and create a branch from `main`. Generate
the Xcode project and resolve the package dependencies after cloning:

```bash
xcodegen generate
swift package resolve
```

Do not commit `Clamshell.xcodeproj`. XcodeGen creates it from `project.yml`.

Do not run tests against live privileged paths. Tests must use fake process and
filesystem boundaries; they must never change `pmset`, invoke `sudo`, or write
to `/Library/PrivilegedHelperTools` or `/etc/sudoers.d`.
Expand All @@ -32,6 +32,7 @@ Run the complete local gate before each pull request:

```bash
scripts/check.sh
npx --yes markdownlint-cli2 '**/*.md' '#.build'
```

The script checks formatting, SwiftLint, tests, debug and release builds, and
Expand Down Expand Up @@ -63,6 +64,10 @@ include product names, commands, code identifiers, API names, quoted interface
text, and standard names. Do not replace a precise technical term with an
ambiguous word.

Keep guides in `docs/`. Keep standard community files in `.github/`. Do not
commit AI plans or internal process notes. The repository ignores
`docs/plans/`.

## Commits

Every commit and pull-request title must use:
Expand Down Expand Up @@ -93,9 +98,9 @@ change to command output, exit status, system paths, permissions, sudoers
policy, or the helper allow-list. Include the commands you ran and update user
documentation when behaviour changes.

Maintainer approval and all required checks are needed before merge. The
repository uses squash merging, so the pull-request title becomes the release
commit subject.
A maintainer must approve the pull request before merge, and all required
checks must pass. The repository uses squash merging, so the pull-request title
becomes the release commit subject.

## Security

Expand Down
24 changes: 14 additions & 10 deletions .github/SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

## Supported versions

Only the latest published release receives security fixes. The project has not
published its first release yet.
The maintainer provides security fixes only for the latest published release.
Update to the latest release before you report a problem that is fixed there.

## Report a vulnerability

Expand All @@ -18,11 +18,15 @@ and keep you informed while we validate and fix it.
## Security-sensitive surfaces

Changes to the root-owned helper, sudoers policy, installation paths, ownership
or permissions, process execution, and unsigned app distribution require extra
review. The helper must continue to accept only the exact `enable` and
`disable` actions. The sudoers policy must not grant password-free access to
the public CLI, `pmset`, a shell, or a user-writable executable.

The planned companion app and DMG will use ad-hoc signing without Apple
notarisation. Release documentation must state that boundary and provide the
specific Gatekeeper approval steps without implying Apple review.
or permissions, process execution, Control Centre request boundary, release
workflow, checksum, and unsigned app distribution require extra review. The
helper must continue to accept only the exact `enable` and `disable` actions.
The sudoers policy must not grant password-free access to the public CLI,
`pmset`, a shell, or a user-writable executable.

The companion app and DMG use ad-hoc signing without Apple notarisation.
Release documentation must state that boundary and provide the specific
Gatekeeper approval steps without implying Apple review.

Read the [privilege model](../docs/privilege-model.md) for the intended paths,
permissions, commands, and Battery Power boundary.
11 changes: 11 additions & 0 deletions .github/SUPPORT.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Support

Start with the public guides:

- [Installation](../docs/installation.md)
- [Usage](../docs/usage.md)
- [Troubleshooting](../docs/troubleshooting.md)
- [Privilege model](../docs/privilege-model.md)

- Report reproducible defects with the [bug form](https://github.com/LMLiam/clamshellctl/issues/new?template=bug.yml).
- Propose a change with the [feature form](https://github.com/LMLiam/clamshellctl/issues/new?template=feature.yml).
- Report vulnerabilities through [private vulnerability reporting](https://github.com/LMLiam/clamshellctl/security/advisories/new).
Expand All @@ -8,6 +15,10 @@
This is a community project. Opening an issue does not guarantee individual
support or a response time.

You can use the issue tracker for `clamshellctl` on its documented macOS
versions. The maintainer does not provide general macOS, hardware, display,
dock, or battery support.

Safe diagnostics include `clamshellctl --version` and `clamshellctl status`.
Review output before posting it. Do not publish sudoers contents when they
contain unexpected local changes; use private vulnerability reporting if those
Expand Down
8 changes: 8 additions & 0 deletions .markdownlint-cli2.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
config:
default: true
MD013: false
MD033: false
MD041: false
ignores:
- .build/**
- docs/plans/**
9 changes: 2 additions & 7 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
# Changelog

## 0.1.0 (2026-07-29)
Release Please records notable changes to `clamshellctl` here.

## 0.1.0 (2026-07-29)

### Features

* **cli:** add secure clamshell control foundation ([#11](https://github.com/LMLiam/clamshellctl/issues/11)) ([b019e1b](https://github.com/LMLiam/clamshellctl/commit/b019e1bef18a9294090da25037b0ba774ede9cba))


### Fixes

* **release:** start at version 0.1.0 ([#14](https://github.com/LMLiam/clamshellctl/issues/14)) ([ae17621](https://github.com/LMLiam/clamshellctl/commit/ae17621f2ab06ae004f4dee0876790cb6fa12ed1))


### Documentation

* **design:** add Control Centre companion ([7e59069](https://github.com/LMLiam/clamshellctl/commit/7e590690663cf60a4139683f389daa2d83f13f53))
Expand All @@ -21,7 +20,3 @@
* **plan:** add companion app delivery ([85b9eaf](https://github.com/LMLiam/clamshellctl/commit/85b9eaf3ef301455a36f8fd763eae8631f040312))
* **plan:** define clamshellctl implementation ([23ee89d](https://github.com/LMLiam/clamshellctl/commit/23ee89d707c4191bb61b90293a5a23a0af44fbd6))
* **project:** add public repository foundation ([075ee59](https://github.com/LMLiam/clamshellctl/commit/075ee595110c683ece57c005cb4aa2e469fbc403))

## Changelog

Notable changes to `clamshellctl` are recorded here by release-please.
105 changes: 51 additions & 54 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,80 +9,77 @@

Control battery clamshell mode on macOS.

`clamshellctl` lets a Mac notebook use an external display while the notebook
is closed and on battery power. You can use a terminal command on macOS 13 or
later. On macOS 26 or later, you can also use a Control Centre control.

> [!IMPORTANT]
> `clamshellctl` is under active development and is not ready to install. The
> repository does not yet publish a Homebrew formula or companion-app DMG.
> The first public release is not available. Do not use an asset from an
> untrusted source. You can build the project from source while release work
> continues.

## What it will provide
## Features

- A terminal-first `clamshellctl` command installed through Homebrew.
- Verified `status`, `enable`, `disable`, and `toggle` operations.
- Optional temporary enablement with automatic disablement.
- A self-contained macOS companion app with a stateful Control Centre toggle.
- One explicit administrator-authorised setup step, followed by narrowly
restricted password-free operations.
- Read, enable, disable, or toggle battery clamshell mode.
- Enable the mode for up to 30 days.
- Use a stateful Control Centre control on macOS 26 or later.
- Install one restricted helper for password-free routine changes.
- Keep the AC Power setting unchanged.

The CLI will support macOS 13 and later. The optional native companion will
require macOS 26 or later because that is where WidgetKit controls became
available on Mac.
## Install

## Temporary enablement
The project will provide two installation methods:

Use `enable --for` with a whole number of minutes, hours, or days:
- **Homebrew:** Installs the terminal command and helper payload on macOS 13 or
later.
- **DMG:** Installs a self-contained app, Control Centre extension, terminal
command, and helper payload on macOS 26 or later.

```bash
clamshellctl enable --for 30m
clamshellctl enable --for 2h
clamshellctl enable --for 1d
```
Read the [installation guide](docs/installation.md) for the requirements,
checksum check, Gatekeeper approval, and setup steps.

The maximum duration is 30 days. The command stores an absolute deadline and
uses a user LaunchAgent to disable battery clamshell mode. Sleep does not reset
the duration. A new timed command replaces the current timer. `enable` without
`--for` and `disable` cancel the current timer.
## Use the command

## Safety model
Run setup once after installation:

The project changes only the Battery Power `disablesleep` setting. It does not
change the corresponding AC Power setting.

Normal mutations will pass through a root-owned helper that accepts only the
exact `enable` and `disable` operations. The sudoers policy will grant no
password-free access to the public CLI, app bundle, arbitrary `pmset`
arguments, or a shell. After each mutation, `clamshellctl` will reread `pmset`
before reporting success.
```bash
sudo clamshellctl setup
```

## Project status
Then use the command without a password:

The CLI, privileged helper, companion app, and Control Centre control are in
active development. Automated tests cover each published behaviour.
```bash
clamshellctl status
clamshellctl enable
clamshellctl enable --for 2h
clamshellctl disable
clamshellctl toggle
```

## Development
Read the [usage guide](docs/usage.md) for timers, Control Centre, and removal.

Requirements:
## Safety

- macOS
- Xcode 26.6 or later
- Swift 6.3 or later
The restricted helper accepts only `enable` and `disable`. It changes only the
Battery Power `disablesleep` setting. The sudoers policy does not give
password-free access to the public command, the app, a shell, or other
`pmset` arguments.

Run the current checks with:
Read the [privilege model](docs/privilege-model.md) for the installed paths,
file permissions, and command boundary.

```bash
scripts/check.sh
```
## Help

See [CONTRIBUTING.md](.github/CONTRIBUTING.md) for setup, style, testing, and
commit conventions.
- Use the [troubleshooting guide](docs/troubleshooting.md) for common problems.
- Read [SUPPORT.md](.github/SUPPORT.md) before you ask a usage question.
- Report a security problem through the process in
[SECURITY.md](.github/SECURITY.md).

## Community
## Contribute

- Use the [issue forms](https://github.com/LMLiam/clamshellctl/issues/new/choose)
for bugs and feature proposals.
- Read [SUPPORT.md](.github/SUPPORT.md) before asking a usage question.
- Report vulnerabilities through the private process in
[SECURITY.md](.github/SECURITY.md).
- Follow the [Code of Conduct](.github/CODE_OF_CONDUCT.md) when participating.
Read [CONTRIBUTING.md](.github/CONTRIBUTING.md) for the development requirements,
checks, style rules, and pull request process.

## Licence

`clamshellctl` is available under the [MIT License](LICENSE).
`clamshellctl` is available under the [MIT Licence](LICENSE).
Loading