Skip to content

Ship the privacycommand CLI inside the app and put it on PATH - #28

Merged
AdamXweb merged 3 commits into
mainfrom
feat/jolly-wing-ca51fd
Oct 8, 2026
Merged

AdamXweb merged 3 commits into
mainfrom
feat/jolly-wing-ca51fd

Conversation

@adamXbot

@adamXbot adamXbot commented Oct 8, 2026 •

Copy link
Copy Markdown
Collaborator

Why

Someone who installs privacycommand.app has no way to get the CLI without cloning the repo and running swift build. This PR ships the CLI inside the app and gives both install paths a way to put it on PATH.

It also renames the CLI from auditctl to privacycommand, matching the app and the cask, while nobody has it on PATH yet. "auditctl" is also the name of Linux's audit control tool, which made the command hard to search for.

What changes

Rename.

  • The SwiftPM executable product is privacycommand.
  • Its targets become privacycommandCLI and privacycommandCLIKit (+ privacycommandCLIKitTests). A target named privacycommand would collide with the app's module and its Sources/privacycommand folder.
  • Usage text, --version, the TUI title, CI's smoke test, the issue template and every doc use the new name.

Embedding (Xcode app target). A new Embed command-line tool run-script phase calls privacycommand/Scripts/embed-cli.sh, which follows the existing guest-agent phase:

  • builds the SwiftPM product for the app's ARCHS (universal for Release/Archive, the active arch for Debug), using its own scratch path under DerivedData. Sharing .build with the host-only guest build made each one rebuild the whole package every time.
  • writes the app's version and the identifier org.privacykey.privacycommand.cli into the binary's __TEXT,__info_plist, so privacycommand --version prints the real version.
  • strips the copy (strip -S -x), which takes the universal binary from 15.0 MB to 7.8 MB. strip re-signs a linker-signed arm64 slice, so unsigned builds still run.
    • Stripping at link time with -Xlinker -S -x broke on Xcode 26.6, whose SwiftPM passes -Xlinker values to clang unwrapped. That was this PR's first App CI failure.
  • copies the binary to Contents/Helpers/privacycommand and signs it with EXPANDED_CODE_SIGN_IDENTITY, --options runtime, and --timestamp when signing with a real identity. When CODE_SIGNING_ALLOWED=NO (App CI) it skips signing.

Homebrew. packaging/homebrew/privacycommand.rb gains binary "#{appdir}/privacycommand.app/Contents/Helpers/privacycommand".

DMG installs. A new app-menu item, Install Command Line Tool…, sits below Check for Updates… and above Services. It goes in the app's own .commands, because SurfaceCommands is hash-locked shared code.

  • It symlinks the embedded binary into /usr/local/bin. If that folder isn't writable (most Macs), it shows the equivalent sudo mkdir -p … && sudo ln -sf … command with a Copy Command button.
  • Once the link exists, the item reads Uninstall Command Line Tool…. The item re-checks state whenever the app becomes active.
  • It refuses to link from a disk image or an App-Translocated copy.
  • It recognises Homebrew's link in /opt/homebrew/bin.
  • It replaces a stale link to another copy of the app.
  • It never touches a file or link that it didn't create.

The filesystem logic is in Core, in CommandLineTool, with 12 unit tests that use temp directories. CommandLineToolInstaller is the menu and alert layer. Both files are hand-added to the pbxproj.

Docs. README "Get it → Command line" describes the two install paths. The in-app manual, CONTRIBUTING (the build-from-source section stays), privacycommand/README.md, BUILDING.md and .github/ARCHITECTURE.md are updated too.

Verification (on this Mac, ad-hoc signed; no Developer ID cert here)

  • swift build, then .build/debug/privacycommand /System/Applications/Calculator.app (CI's smoke test): exits 0.
  • swift test: 290 Core tests (4 skipped, as before) and 47 CLI-kit tests, 0 failures.
  • xcodebuild of a fresh universal Debug bundle:
    • Contents/Helpers/privacycommand is Mach-O universal (x86_64 arm64) with flags=0x10002(adhoc,runtime) and identifier org.privacykey.privacycommand.cli.
    • The app passes codesign --verify --deep --strict.
    • --version prints privacycommand 0.1.5, both natively and under Rosetta.
  • Before the rename, the same pipeline also passed:
    • a Release xcodebuild archive and scripts/verify-archive.py.
    • an unsigned universal build, CI's path.
    • preview against the real outdated casks on this machine.
    • CI's Xcode 26.6 backend, reproduced with swift build --build-system xcode.
  • Drove the running app over Accessibility:
    • menu order is About → Settings… → Check for Updates… → Install Command Line Tool… → Services.
    • Install showed "Install the command-line tool from Terminal" with the sudo command for /usr/local/bin/privacycommand. Copy Command put it on the clipboard.
  • just lint and just docs-check pass.

Not verified here

  • A Developer ID–signed, notarized build. The phase uses the guest agent's identity/flags path, which already ships notarized.
  • The in-app Uninstall flow and the Homebrew-link alert, because /usr/local/bin is root-owned here. Both are covered by the CommandLineTool unit tests.
  • A real brew install --cask with the new stanza. That needs a release.

Note for in-flight work

Uncommitted CLI work in another worktree (feat/privacy-cli-brew-preview-f18825: upgrade, pkg support) still edits Sources/auditctl/…. Git records this PR's moves as renames, so merging main into that branch should carry its edits into Sources/privacycommandCLI/. New user-facing text there needs the new name.

🤖 Generated with Claude Code

adamXbot and others added 3 commits October 8, 2026 20:32
People who install privacycommand.app could only get the auditctl CLI by
cloning the repo and building it. The app now carries it.

- New "Embed auditctl" run-script phase (Scripts/embed-auditctl.sh)
  builds the SwiftPM product for the app's architectures (universal in
  Release), stamps the app's version and an identifier into its
  __info_plist section, strips it at link time, copies it to
  Contents/Helpers/auditctl and signs it with the app's identity and
  hardened runtime, following the guest-agent phase. It uses its own
  SwiftPM scratch path so it doesn't ping-pong rebuilds with the
  host-only guest build in .build.
- The cask template links it with a binary stanza, so
  `brew install --cask` puts auditctl on PATH.
- DMG installs get an app-menu item, Install Command Line Tool…, below
  Check for Updates…. It symlinks the embedded binary into
  /usr/local/bin, or shows the equivalent sudo command (with Copy) when
  the folder isn't writable, and flips to Uninstall Command Line Tool….
  It refuses to link from a disk image or translocated copy, recognises
  Homebrew's link, replaces links to other app copies, and never touches
  a file or link it didn't create. The filesystem logic is
  CommandLineTool in Core, with unit tests.
- README "Get it → Command line", the in-app manual, CONTRIBUTING and
  the source-tree README describe the two install paths.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Xcode 26.6's SwiftPM (used by App CI) builds multi-arch products through
its xcode build system, which hands -Xlinker values to clang unwrapped:
-x became clang's language flag and the link failed. Keep only
-sectcreate, which clang also understands, and run `strip -S -x` on the
embedded copy instead; strip re-signs a linker-signed arm64 slice, so
unsigned builds still run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The CLI hasn't shipped to users yet, so rename it now, before a release
puts it on anyone's PATH. "auditctl" is also the name of Linux's audit
control tool, which made the command hard to search for and suggested
it controls a service.

- The SwiftPM executable product is now `privacycommand`. Targets move
  to privacycommandCLI / privacycommandCLIKit (+ Tests), since a target
  named privacycommand would collide with the app's module and source
  folder.
- The app embeds it at Contents/Helpers/privacycommand with identifier
  org.privacykey.privacycommand.cli; the build phase is now "Embed
  command-line tool" (Scripts/embed-cli.sh). The cask's binary stanza
  and the Install Command Line Tool… item link the new path, and the
  alerts say "the command-line tool".
- Usage text, --version, the TUI title, CI's smoke test, the issue
  template and every doc use the new name.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@adamXbot adamXbot changed the title Ship auditctl inside the app and put it on PATH Ship the privacycommand CLI inside the app and put it on PATH Oct 8, 2026
@AdamXweb
AdamXweb merged commit aef5e16 into main Oct 8, 2026
7 checks passed
adamXbot added a commit that referenced this pull request Oct 9, 2026
This work was finished on 2026-10-08 against the old `Sources/auditctl`
layout and never landed before the CLI was renamed to `privacycommand`
(#28). Same change, re-homed under `Sources/privacycommandCLI` with the
new name in help and docs:

- `privacycommand upgrade` = `preview --fetch`, with its own help.
- `--greedy` includes casks only `brew upgrade --greedy` touches.
- `HomebrewCaskInventory.outdatedScan(greedy:)` replaces
  `outdatedCaskTargets()` and also reports what preview can't analyze:
  casks that install no app (with the reason), outdated formulae, and
  greedy-only casks, so the preview never silently covers less than the
  upgrade does.
- `.pkg` casks: the installed app is found through the `uninstall
  pkgutil:` receipts, the incoming one through `pkgutil --expand-full`
  (also for a .pkg inside a dmg/zip, located by the cask's `pkg` stanza).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
adamXbot added a commit to privacykey/docs-privacycommand that referenced this pull request Oct 9, 2026
* docs: document the privacycommand CLI and how to install it

The CLI was renamed from auditctl to privacycommand and now ships inside
the app (privacykey/privacycommand#28). The docs still said it had to be
built from source.

- auditctl.mdx becomes cli.mdx ("Command line"), with a redirect from
  /auditctl. It covers the Homebrew install, the app menu's Install
  Command Line Tool… (and its sudo fallback and safety rules), building
  from source for 0.1.5 and earlier, usage with the new name, and the
  audit exit codes.
- Every page that ran `swift run -c release auditctl …` or linked
  /auditctl now uses `privacycommand …` and /cli.
- Installation and Updating mention the tool, Troubleshooting gains
  "command not found" and "asks you to move the app" entries, and the
  Develop pages use the new target names and the embed phase.
- llms.txt and llms-full.txt regenerated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: add tab completion setup for the privacycommand CLI

Documents `privacycommand completion <zsh|bash|fish>`
(privacykey/privacycommand#29): what completes, that Homebrew sets it up,
the one line per shell otherwise, and a zsh troubleshooting entry
(compinit order, Homebrew's fpath, a stale ~/.zcompdump). llms files
regenerated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: cover privacycommand upgrade, --max-risk, --greedy and .pkg

The CLI on main gained `upgrade`, a `--max-risk` gate, `--greedy` and
.pkg artifact support, so cli.mdx's "`.pkg` is skipped" and "will not
upgrade anything" were no longer the whole story.

- cli.mdx: preview's new flags, the casks and formulae it lists but
  can't analyse, the brew calls it makes now, exit code 3, .pkg handling,
  and a new "Upgrading within a risk limit" section (cleared/held,
  --dry-run, --no-input, exit codes 0/1/2/3, the daily alias).
- Security lists `brew upgrade` as the sixth outbound call; the
  Introduction and FAQ follow.
- llms files regenerated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
adamXbot added a commit to privacykey/website-privacycommand that referenced this pull request Oct 9, 2026
* copy: add the privacycommand command-line tool

The app now carries a command-line tool of the same name, with tab
completion (privacykey/privacycommand#28, #29). The site still called it
auditctl in one place and said nothing else about it.

- New "Command line" section between Features and FAQ, with a terminal
  mock built from real output, linked from the header and footer nav.
- Homebrew and DMG install cards say how each puts the tool on PATH;
  the source card uses the new name and its "Build guide" link points
  at CONTRIBUTING.md (the README has no #build-from-source anchor).
- New FAQ entry "Can I use it from Terminal or in CI?", mirrored in the
  FAQPage JSON-LD, plus a featureList entry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Keep the nav on one line with the Command line link; mention upgrade

- The extra "Command line" nav link made index.html scroll sideways
  between 761 and 797px (found while fixing #13). The links now collapse
  below 841px instead of 761px, and nav labels no longer wrap ("Command
  line" broke onto two lines at 841px). Measured: no horizontal overflow
  at any of 33 widths from 320 to 1440px, and the links stay on one line
  from 841px up.
- The CLI section said the tool never runs the upgrade itself;
  `privacycommand upgrade --max-risk` now can, so the copy describes it,
  and the featureList entry mentions risk-gated upgrades.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants