From 79bc7801dfdfa26fed241b32d87bc9efb17cac2a Mon Sep 17 00:00:00 2001 From: LMLiam <46268350+TheRealEmissions@users.noreply.github.com> Date: Wed, 29 Jul 2026 22:57:56 +0100 Subject: [PATCH 1/2] docs(project): complete public installation guidance --- .markdownlint-cli2.yaml | 8 +++ CHANGELOG.md | 9 +--- CONTRIBUTING.md | 92 +++++++++++++++++++++++++++++++++ README.md | 104 ++++++++++++++++++------------------- SECURITY.md | 42 +++++++++++++++ SUPPORT.md | 41 +++++++++++++++ docs/installation.md | 111 ++++++++++++++++++++++++++++++++++++++++ docs/privilege-model.md | 78 ++++++++++++++++++++++++++++ docs/troubleshooting.md | 90 ++++++++++++++++++++++++++++++++ docs/usage.md | 88 +++++++++++++++++++++++++++++++ 10 files changed, 602 insertions(+), 61 deletions(-) create mode 100644 .markdownlint-cli2.yaml create mode 100644 CONTRIBUTING.md create mode 100644 SECURITY.md create mode 100644 SUPPORT.md create mode 100644 docs/installation.md create mode 100644 docs/privilege-model.md create mode 100644 docs/troubleshooting.md create mode 100644 docs/usage.md diff --git a/.markdownlint-cli2.yaml b/.markdownlint-cli2.yaml new file mode 100644 index 0000000..f61f3ab --- /dev/null +++ b/.markdownlint-cli2.yaml @@ -0,0 +1,8 @@ +config: + default: true + MD013: false + MD033: false + MD041: false +ignores: + - .build/** + - docs/plans/** diff --git a/CHANGELOG.md b/CHANGELOG.md index c07015e..0fe7d37 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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)) @@ -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. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..0661c40 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,92 @@ +# Contributing + +Thank you for helping with `clamshellctl`. + +## Before you start + +- Use a bug report for incorrect behaviour. +- Use a feature request to explain a user problem before you propose a large + change. +- Use private vulnerability reporting for a security problem. +- Keep each pull request focused on one issue. + +## Development requirements + +You need: + +- macOS +- Xcode 26.6 or later +- Swift 6.3 or later +- XcodeGen 2.46.0 +- SwiftLint 0.65.0 +- actionlint 1.7.12 +- Node.js and npm for Markdown checks + +Clone the repository and generate the Xcode project: + +```bash +git clone https://github.com/LMLiam/clamshellctl.git +cd clamshellctl +xcodegen generate +``` + +Do not commit `Clamshell.xcodeproj`. XcodeGen creates it from `project.yml`. + +## Code structure + +Put each file in the directory that owns its responsibility. Do not put a file +in a directory only because another type uses it. + +Prefer one primary top-level Swift declaration per file. Small private support +declarations can stay with the primary declaration when this makes the code +easier to read. Use clear names instead of comments that repeat the code. Add a +comment only when it explains a constraint, security reason, or non-obvious +decision. + +Follow the +[Google Swift Style Guide](https://google.github.io/swift/). The repository +uses Swift Format, SwiftLint, and `.editorconfig` to enforce its local rules. + +## Documentation + +Write all user and contributor documentation in ASD-STE100 Simplified +Technical English. You can use required product names, command names, API +names, file paths, and other technical terms. + +Keep documentation in `docs/` unless it is a standard repository file such as +`README.md`, `CONTRIBUTING.md`, `SECURITY.md`, or `SUPPORT.md`. Do not commit AI +plans or internal process notes. The repository ignores `docs/plans/`. + +## Tests + +Add a failing behavioural test before you change production behaviour. Tests +must not change the live power state or install privileged files. + +Run the full check before you open a pull request: + +```bash +scripts/check.sh +npx --yes markdownlint-cli2 '**/*.md' '#.build' +``` + +The check runs format validation, SwiftLint, tests, debug and release builds, +DMG packaging tests, workflow lint, and an Xcode app build. + +## Commits and pull requests + +Use this subject format for every commit and pull request: + +```text +verb(area): description +``` + +Use a supported verb such as `feat`, `fix`, `docs`, `test`, `build`, `ci`, +`refactor`, or `chore`. Use a lower-case area. Do not add a full stop at the end +of the subject. + +Complete the pull request template. Link the issue, describe behaviour and +security effects, and list the commands that you ran. Mark the pull request +ready for review only when it is ready for CodeRabbit and maintainer review. + +A maintainer must approve a pull request before merge. Repository owners can +use an administrator merge for their own pull requests. diff --git a/README.md b/README.md index 4935d52..e658f8e 100644 --- a/README.md +++ b/README.md @@ -9,80 +9,76 @@ Control battery clamshell mode on macOS. -> [!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. +`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. -## What it will provide +> [!IMPORTANT] +> 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. -- 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. +## Features -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. +- 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. -## Temporary enablement +## Install -Use `enable --for` with a whole number of minutes, hours, or days: +The project will provide two installation methods: -```bash -clamshellctl enable --for 30m -clamshellctl enable --for 2h -clamshellctl enable --for 1d -``` +- **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. -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. +Read the [installation guide](docs/installation.md) for the requirements, +checksum check, Gatekeeper approval, and setup steps. -## Safety model +## Use the command -The project changes only the Battery Power `disablesleep` setting. It does not -change the corresponding AC Power setting. +Run setup once after installation: -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](SUPPORT.md) before you ask a usage question. +- Report a security problem through the process in [SECURITY.md](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](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). diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..868364b --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,42 @@ +# Security policy + +## Supported versions + +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 + +Do not open a public issue for a security problem. Use +[private vulnerability reporting](https://github.com/LMLiam/clamshellctl/security/advisories/new). + +Include: + +- the affected version and installation method +- the macOS version and Mac architecture +- concise reproduction steps +- the expected and actual security boundary +- a proof of concept when it is safe to share +- a proposed fix, if you have one + +Remove passwords, tokens, private keys, account names, and unrelated personal +data. Do not test against another person's computer or data. + +The maintainer will use the private report to confirm the problem, discuss a +fix, and plan coordinated disclosure. Do not publish the details before the +maintainer and reporter agree on disclosure. + +## Important security boundaries + +Security reports can include: + +- helper argument validation +- sudoers policy expansion or bypass +- replacement of files outside the managed paths +- unsafe handling of root-owned files +- command or path injection +- Control Centre requests that bypass the helper boundary +- release asset, checksum, or workflow integrity + +Read the [privilege model](docs/privilege-model.md) for the intended boundary. +General bugs and support questions belong in the public issue forms. diff --git a/SUPPORT.md b/SUPPORT.md new file mode 100644 index 0000000..2e01086 --- /dev/null +++ b/SUPPORT.md @@ -0,0 +1,41 @@ +# Support + +## Read the guides + +Start with: + +- [Installation](docs/installation.md) +- [Usage](docs/usage.md) +- [Troubleshooting](docs/troubleshooting.md) +- [Privilege model](docs/privilege-model.md) + +## Ask a question + +Use the +[support question form](https://github.com/LMLiam/clamshellctl/issues/new?template=question.yml) +for a usage question. Include the macOS version, installation method, and safe +diagnostic output. + +## Report a bug + +Use the +[bug report form](https://github.com/LMLiam/clamshellctl/issues/new?template=bug.yml). +Search open and closed issues first. Include concise steps that reproduce the +problem. + +## Request a feature + +Use the +[feature request form](https://github.com/LMLiam/clamshellctl/issues/new?template=feature.yml). +Describe the user problem, desired result, alternatives, and privilege impact. + +## Report a security problem + +Follow [SECURITY.md](SECURITY.md). Do not put vulnerability details in a public +issue. + +## Scope + +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. There is no guaranteed response time. diff --git a/docs/installation.md b/docs/installation.md new file mode 100644 index 0000000..ec46e42 --- /dev/null +++ b/docs/installation.md @@ -0,0 +1,111 @@ +# Installation + +The first public release is not available. The commands in this guide will +work after the project publishes the release assets and Homebrew formula. + +## Choose an installation method + +| Method | macOS | Includes Control Centre | Includes terminal command | +| --- | --- | --- | --- | +| Homebrew | 13 or later | No | Yes | +| DMG | 26 or later | Yes | Optional link | + +Both methods include the restricted helper payload. Installation does not add +privileged files. You approve privileged setup in a separate step. + +## Install with Homebrew + +You need macOS 13 or later and a current Homebrew installation. + +1. Install the formula from the project tap: + + ```bash + brew install LMLiam/tap/clamshellctl + ``` + +2. Install the restricted helper and sudoers policy: + + ```bash + sudo clamshellctl setup + ``` + +3. Check the installation: + + ```bash + clamshellctl --version + clamshellctl status + ``` + +The formula does not install privileged files during `brew install`. The +`setup` command is the only installation step that needs administrator access. + +Homebrew documents how a direct install from a +[third-party tap](https://docs.brew.sh/How-to-Create-and-Maintain-a-Tap#installing) +limits trust to the selected formula. + +## Install the companion app from a DMG + +You need macOS 26 or later. The app and all command payloads contain `arm64` +and `x86_64` code. + +### Verify the download + +Download the DMG and its `.sha256` file from the same GitHub release. Replace +`X.Y.Z` in this command with the release version: + +```bash +cd ~/Downloads +shasum -a 256 -c clamshellctl-vX.Y.Z.dmg.sha256 +``` + +Continue only if the command reports `OK`. + +### Copy and approve the app + +1. Open the DMG. +2. Drag `Clamshell.app` to the Applications link. +3. Eject the DMG. +4. Open Clamshell from the Applications folder or Spotlight. + +The app has an ad-hoc signature and is not notarised. macOS blocks its first +launch. This warning is expected, but it means that Apple did not verify the +developer or scan this build during notarisation. + +If you trust the GitHub release and its checksum: + +1. Try to open Clamshell once. +2. Open **System Settings** and select **Privacy & Security**. +3. Go to **Security** and select **Open Anyway**. +4. Confirm that you want to open Clamshell. + +Apple explains this process and its risks in +[Open a Mac app from an unknown developer](https://support.apple.com/guide/mac-help/open-a-mac-app-from-an-unknown-developer-mh40616/mac). + +### Complete setup + +1. Select **Set Up** in the Clamshell window. +2. Approve the administrator prompt. +3. Open Control Centre and select **Edit Controls**. +4. Add **Battery Clamshell Mode**. + +Enable **Install the Terminal command** before setup if you also want +`/usr/local/bin/clamshellctl`. You can select **Install Terminal Command** +after setup if you did not enable it at first. + +## Update + +For Homebrew, run: + +```bash +brew update +brew upgrade clamshellctl +``` + +For the DMG, download and verify the new release. Replace the app in +`/Applications`, then open it. Select **Set Up** if the app reports that the +helper needs repair. This step replaces an old helper with the app payload. + +## Remove + +Follow the [removal steps](usage.md#remove-clamshellctl). Disable battery +clamshell mode before you remove the helper or app. diff --git a/docs/privilege-model.md b/docs/privilege-model.md new file mode 100644 index 0000000..8a50eb6 --- /dev/null +++ b/docs/privilege-model.md @@ -0,0 +1,78 @@ +# Privilege model + +Routine state changes need root access because macOS restricts `pmset` changes. +`clamshellctl` uses a small helper and an exact sudoers allow-list for these +changes. + +## Setup boundary + +The Homebrew command uses this explicit setup step: + +```bash +sudo clamshellctl setup +``` + +The companion app shows one macOS administrator prompt for the same operation. +Setup does these actions: + +1. Copy the helper payload to its managed path. +2. Set the helper owner to `root:wheel` and its mode to `0755`. +3. Create a sudoers policy for the user who started `sudo`. +4. Set the policy owner to `root:wheel` and its mode to `0440`. +5. Validate the staged policy with `visudo`. +6. Replace the managed policy and helper. +7. Verify the files and both permitted commands. + +Repeated setup does not replace valid files. + +## Managed privileged paths + +| Path | Purpose | Owner and mode | +| --- | --- | --- | +| `/Library/PrivilegedHelperTools/clamshellctl-helper` | Runs one state change | `root:wheel`, `0755` | +| `/etc/sudoers.d/clamshellctl` | Allows two helper commands | `root:wheel`, `0440` | + +The optional app command link is `/usr/local/bin/clamshellctl`. Setup does not +replace an existing file or a link to another command. + +## Runtime boundary + +The sudoers policy allows only these command forms for the setup user: + +```text +/Library/PrivilegedHelperTools/clamshellctl-helper enable +/Library/PrivilegedHelperTools/clamshellctl-helper disable +``` + +The helper rejects all other arguments. It maps the accepted actions to these +system changes: + +```text +/usr/bin/pmset -b disablesleep 1 +/usr/bin/pmset -b disablesleep 0 +``` + +The `-b` option limits the change to Battery Power. The helper does not change +the AC Power setting. + +The policy does not allow password-free access to: + +- `clamshellctl` +- `Clamshell.app` +- a shell +- general `pmset` commands +- another executable or argument + +The public command uses `sudo -n` for a permitted helper action. It does not +show an administrator prompt during a routine state change. If setup is not +valid, the command stops and tells you to run setup. + +## DMG trust boundary + +The release DMG and nested app files use ad-hoc signatures. The packaging +process verifies these signatures, but Apple does not identify the developer +or notarise the app. Verify the SHA-256 file before you approve the app in +Privacy & Security. + +The planned Homebrew formula will build the command and helper from the tagged +source. It will not install privileged files during `brew install`. diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md new file mode 100644 index 0000000..b22e4fc --- /dev/null +++ b/docs/troubleshooting.md @@ -0,0 +1,90 @@ +# Troubleshooting + +Start with these commands when the terminal command is available: + +```bash +clamshellctl --version +clamshellctl status +``` + +For a DMG installation without the optional terminal link, use: + +```bash +/Applications/Clamshell.app/Contents/MacOS/clamshellctl status +``` + +## macOS blocks the app + +The DMG is not notarised. Verify the downloaded checksum first. Then try to +open the app and use **Privacy & Security > Open Anyway**. Read the detailed +[Gatekeeper steps](installation.md#copy-and-approve-the-app). + +## Battery Clamshell Mode is not in Control Centre + +Check each requirement: + +- The Mac uses macOS 26 or later. +- `Clamshell.app` is in `/Applications`. +- You opened the installed app at least once. +- The app contains `Contents/PlugIns/ClamshellControl.appex`. + +Open Clamshell from Spotlight. Complete setup, then open Control Centre and +select **Edit Controls** again. + +## The control returns to the clear state + +The control shows the system state, not the last requested state. Run +`clamshellctl status`. If setup is missing or invalid, open Clamshell and select +**Set Up**. The app can replace an invalid helper. + +If the state still does not change, include the safe error text in a bug report. +Do not include passwords, account names, or a complete environment dump. + +## The app reports incomplete files + +The app needs its bundled command, helper, and Control Centre extension. + +If the window shows **Remove Privileged Setup**, select it first. Then remove +the incomplete app and install a complete copy in `/Applications`. + +If the removal button is not available, install a complete app first. Open it +and remove or repair the privileged setup from the new copy. + +## Setup reports a command link conflict + +The app creates `/usr/local/bin/clamshellctl` only when that path is free or +already links to the installed app command. It does not replace another file. + +Inspect the path before you change it: + +```bash +ls -l /usr/local/bin/clamshellctl +``` + +Keep the existing command, or move it to a safe backup location. Then run setup +again. + +## Status reports that a timer deadline passed + +Run: + +```bash +clamshellctl disable +``` + +This command disables battery clamshell mode and removes the stale timer files. + +## An update reports that setup needs repair + +The installed helper does not match the new payload. For Homebrew, run: + +```bash +sudo clamshellctl setup +``` + +For the companion app, open Clamshell and select **Set Up**. + +## Get more help + +Read [SUPPORT.md](../SUPPORT.md) and use the correct issue form. Use private +vulnerability reporting for a security problem. diff --git a/docs/usage.md b/docs/usage.md new file mode 100644 index 0000000..5c3137e --- /dev/null +++ b/docs/usage.md @@ -0,0 +1,88 @@ +# Usage + +Run `clamshellctl` without a subcommand to show the current state. + +## Read the state + +```bash +clamshellctl status +``` + +The command reports `enabled` or `disabled`. It also reports the end time for +temporary enablement. + +## Change the state + +```bash +clamshellctl enable +clamshellctl disable +clamshellctl toggle +``` + +`enable` allows clamshell mode while the Mac uses battery power. `disable` +prevents it. `toggle` changes the current state. + +The command reads the system state after a change. It reports success only if +the requested state is active. + +Use `--quiet` with a state change, setup, or removal command to suppress +successful output. Errors still use standard error. The `status` command does +not accept `--quiet` because its purpose is to print the state. + +## Use temporary enablement + +Add `--for` and a whole number of minutes, hours, or days: + +```bash +clamshellctl enable --for 30m +clamshellctl enable --for 2h +clamshellctl enable --for 1d +``` + +The maximum duration is 30 days. The command records an absolute deadline and +uses a user LaunchAgent to disable the mode. Sleep does not restart the +duration. + +A new temporary enablement replaces the current timer. `enable` without +`--for` and `disable` remove the current timer. + +## Use Control Centre + +The Control Centre control requires macOS 26 or later and the DMG installation. + +1. Open Control Centre and select **Edit Controls**. +2. Add **Battery Clamshell Mode**. +3. Select the control to enable or disable the mode. + +The filled state means that battery clamshell mode is enabled. The clear state +means that it is disabled. The control reads the system state again after each +request. + +The companion app runs in the background for control requests. It has no Dock +icon. If you open Clamshell from Spotlight, the existing app instance shows the +setup window. + +## Remove clamshellctl + +Disable the mode before removal. This action also removes an active timer. + +### Homebrew installation + +```bash +clamshellctl disable +sudo clamshellctl uninstall +brew uninstall clamshellctl +``` + +### DMG installation + +Run these commands while the app is still in `/Applications`: + +```bash +/Applications/Clamshell.app/Contents/MacOS/clamshellctl disable +sudo /Applications/Clamshell.app/Contents/MacOS/clamshellctl uninstall --remove-command +``` + +Then move `Clamshell.app` to the Bin. The removal command deletes only the +managed helper, sudoers policy, and app-owned terminal link. It does not delete +an unrelated item at `/usr/local/bin/clamshellctl`. From d96100d1ae14cb53f5982f923cbbacf4f0065e06 Mon Sep 17 00:00:00 2001 From: LMLiam <46268350+TheRealEmissions@users.noreply.github.com> Date: Wed, 29 Jul 2026 23:03:01 +0100 Subject: [PATCH 2/2] refactor(docs): keep community policies in GitHub metadata --- .github/CONTRIBUTING.md | 25 ++++++----- .github/SECURITY.md | 24 ++++++----- .github/SUPPORT.md | 11 +++++ CONTRIBUTING.md | 92 ----------------------------------------- README.md | 7 ++-- SECURITY.md | 42 ------------------- SUPPORT.md | 41 ------------------ docs/troubleshooting.md | 4 +- 8 files changed, 46 insertions(+), 200 deletions(-) delete mode 100644 CONTRIBUTING.md delete mode 100644 SECURITY.md delete mode 100644 SUPPORT.md diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index cfda072..3daca25 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -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`. @@ -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 @@ -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: @@ -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 diff --git a/.github/SECURITY.md b/.github/SECURITY.md index 80b67fc..d138500 100644 --- a/.github/SECURITY.md +++ b/.github/SECURITY.md @@ -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 @@ -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. diff --git a/.github/SUPPORT.md b/.github/SUPPORT.md index 843bae3..e7436e9 100644 --- a/.github/SUPPORT.md +++ b/.github/SUPPORT.md @@ -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). @@ -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 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index 0661c40..0000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,92 +0,0 @@ -# Contributing - -Thank you for helping with `clamshellctl`. - -## Before you start - -- Use a bug report for incorrect behaviour. -- Use a feature request to explain a user problem before you propose a large - change. -- Use private vulnerability reporting for a security problem. -- Keep each pull request focused on one issue. - -## Development requirements - -You need: - -- macOS -- Xcode 26.6 or later -- Swift 6.3 or later -- XcodeGen 2.46.0 -- SwiftLint 0.65.0 -- actionlint 1.7.12 -- Node.js and npm for Markdown checks - -Clone the repository and generate the Xcode project: - -```bash -git clone https://github.com/LMLiam/clamshellctl.git -cd clamshellctl -xcodegen generate -``` - -Do not commit `Clamshell.xcodeproj`. XcodeGen creates it from `project.yml`. - -## Code structure - -Put each file in the directory that owns its responsibility. Do not put a file -in a directory only because another type uses it. - -Prefer one primary top-level Swift declaration per file. Small private support -declarations can stay with the primary declaration when this makes the code -easier to read. Use clear names instead of comments that repeat the code. Add a -comment only when it explains a constraint, security reason, or non-obvious -decision. - -Follow the -[Google Swift Style Guide](https://google.github.io/swift/). The repository -uses Swift Format, SwiftLint, and `.editorconfig` to enforce its local rules. - -## Documentation - -Write all user and contributor documentation in ASD-STE100 Simplified -Technical English. You can use required product names, command names, API -names, file paths, and other technical terms. - -Keep documentation in `docs/` unless it is a standard repository file such as -`README.md`, `CONTRIBUTING.md`, `SECURITY.md`, or `SUPPORT.md`. Do not commit AI -plans or internal process notes. The repository ignores `docs/plans/`. - -## Tests - -Add a failing behavioural test before you change production behaviour. Tests -must not change the live power state or install privileged files. - -Run the full check before you open a pull request: - -```bash -scripts/check.sh -npx --yes markdownlint-cli2 '**/*.md' '#.build' -``` - -The check runs format validation, SwiftLint, tests, debug and release builds, -DMG packaging tests, workflow lint, and an Xcode app build. - -## Commits and pull requests - -Use this subject format for every commit and pull request: - -```text -verb(area): description -``` - -Use a supported verb such as `feat`, `fix`, `docs`, `test`, `build`, `ci`, -`refactor`, or `chore`. Use a lower-case area. Do not add a full stop at the end -of the subject. - -Complete the pull request template. Link the issue, describe behaviour and -security effects, and list the commands that you ran. Mark the pull request -ready for review only when it is ready for CodeRabbit and maintainer review. - -A maintainer must approve a pull request before merge. Repository owners can -use an administrator merge for their own pull requests. diff --git a/README.md b/README.md index e658f8e..bea7a25 100644 --- a/README.md +++ b/README.md @@ -71,12 +71,13 @@ file permissions, and command boundary. ## Help - Use the [troubleshooting guide](docs/troubleshooting.md) for common problems. -- Read [SUPPORT.md](SUPPORT.md) before you ask a usage question. -- Report a security problem through the process in [SECURITY.md](SECURITY.md). +- 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). ## Contribute -Read [CONTRIBUTING.md](CONTRIBUTING.md) for the development requirements, +Read [CONTRIBUTING.md](.github/CONTRIBUTING.md) for the development requirements, checks, style rules, and pull request process. ## Licence diff --git a/SECURITY.md b/SECURITY.md deleted file mode 100644 index 868364b..0000000 --- a/SECURITY.md +++ /dev/null @@ -1,42 +0,0 @@ -# Security policy - -## Supported versions - -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 - -Do not open a public issue for a security problem. Use -[private vulnerability reporting](https://github.com/LMLiam/clamshellctl/security/advisories/new). - -Include: - -- the affected version and installation method -- the macOS version and Mac architecture -- concise reproduction steps -- the expected and actual security boundary -- a proof of concept when it is safe to share -- a proposed fix, if you have one - -Remove passwords, tokens, private keys, account names, and unrelated personal -data. Do not test against another person's computer or data. - -The maintainer will use the private report to confirm the problem, discuss a -fix, and plan coordinated disclosure. Do not publish the details before the -maintainer and reporter agree on disclosure. - -## Important security boundaries - -Security reports can include: - -- helper argument validation -- sudoers policy expansion or bypass -- replacement of files outside the managed paths -- unsafe handling of root-owned files -- command or path injection -- Control Centre requests that bypass the helper boundary -- release asset, checksum, or workflow integrity - -Read the [privilege model](docs/privilege-model.md) for the intended boundary. -General bugs and support questions belong in the public issue forms. diff --git a/SUPPORT.md b/SUPPORT.md deleted file mode 100644 index 2e01086..0000000 --- a/SUPPORT.md +++ /dev/null @@ -1,41 +0,0 @@ -# Support - -## Read the guides - -Start with: - -- [Installation](docs/installation.md) -- [Usage](docs/usage.md) -- [Troubleshooting](docs/troubleshooting.md) -- [Privilege model](docs/privilege-model.md) - -## Ask a question - -Use the -[support question form](https://github.com/LMLiam/clamshellctl/issues/new?template=question.yml) -for a usage question. Include the macOS version, installation method, and safe -diagnostic output. - -## Report a bug - -Use the -[bug report form](https://github.com/LMLiam/clamshellctl/issues/new?template=bug.yml). -Search open and closed issues first. Include concise steps that reproduce the -problem. - -## Request a feature - -Use the -[feature request form](https://github.com/LMLiam/clamshellctl/issues/new?template=feature.yml). -Describe the user problem, desired result, alternatives, and privilege impact. - -## Report a security problem - -Follow [SECURITY.md](SECURITY.md). Do not put vulnerability details in a public -issue. - -## Scope - -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. There is no guaranteed response time. diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index b22e4fc..7406927 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -86,5 +86,5 @@ For the companion app, open Clamshell and select **Set Up**. ## Get more help -Read [SUPPORT.md](../SUPPORT.md) and use the correct issue form. Use private -vulnerability reporting for a security problem. +Read [SUPPORT.md](../.github/SUPPORT.md) and use the correct issue form. Use +private vulnerability reporting for a security problem.