Skip to content

Add a sandboxed build for the Mac App Store - #3

Merged
hollandcloud merged 3 commits into
mainfrom
feat/app-store
Oct 13, 2026
Merged

hollandcloud merged 3 commits into
mainfrom
feat/app-store

Conversation

@hollandcloud

Copy link
Copy Markdown
Owner

Gets Cleared ready to distribute through the Mac App Store. Tracked on the board as LAB-9.

Why it needs its own build

The store requires the App Sandbox, and the sandbox rules out the one-click Use my gh CLI login: Cleared can neither run gh there nor read the login it stores. So this adds a third configuration rather than sandboxing everything. Direct builds keep the gh path; the store build connects with a pasted token and says why.

What changed

  • AppStore configuration — Release plus Resources/Cleared.entitlements (the sandbox and outbound network, nothing else). The scheme archives with it, so in Xcode it is Product → Archive, then Distribute App → App Store Connect.
  • Signing team — read from Local.xcconfig (CLEARED_APPSTORE_TEAM), and may differ from the team used day to day. With none set it falls back to ad-hoc, so a fresh clone and CI still build it.
  • Connect screen — in the sandbox the token field is the primary action, with the reason stated. Both builds get Create a token on GitHub…, which opens the new-token page with repo selected.
  • Keychain — the sandboxed build stores its token under its own service name. It is signed differently from a direct build and couldn't read that entry; merely trying would raise a Keychain password prompt at launch.
  • Store metadata — ITSAppUsesNonExemptEncryption = NO, a real copyright line, and a privacy manifest (no tracking, nothing collected, UserDefaults for the app's own settings).
  • make appstore — archives, exports the signed package to dist/appstore/, then unpacks it and fails unless the app inside is sandboxed, validly signed, and free of get-task-allow. Uploads nothing.
  • CI — builds the AppStore configuration and checks for the sandbox entitlement.

Verification

  • make appstore run end to end on the maintainer's Mac. The package is signed with the 3rd Party Mac Developer Installer certificate; the app inside is signed Apple Distribution, hardened runtime, universal, sandboxed, with Xcode's Mac Team Store provisioning profile.
  • The sandboxed build runs: it logs starting (sandboxed), creates its container, and produced no sandbox denials.
  • A throwaway probe signed with the same entitlements confirmed the two things the sandbox could break: HTTPS to api.github.com returns 200, and a Keychain add, read, update and delete round trip succeeds.
  • A fresh clone with no Local.xcconfig builds the configuration ad-hoc and passes the tests (79 in 16 suites).
  • Both connect screens checked in the preview harness, which gained fixtures for them.

Not exercised: connecting with a real token inside the sandboxed app, and anything on the App Store Connect side — no build has been uploaded.

Still to do outside the repo

  • Create the app record for the bundle ID in App Store Connect; the name "Cleared" may or may not be free.
  • Upload from Xcode's Organizer.
  • Store listing: description, screenshots, privacy policy URL, and review notes explaining that the app needs a GitHub token.

🤖 Generated with Claude Code

hollandcloud and others added 3 commits October 13, 2026 10:12
The store requires the App Sandbox. Add an AppStore build configuration
— Release plus an entitlements file granting the sandbox and outbound
network access, which is all Cleared needs — and make it what the scheme
archives with, so Product → Archive in Xcode builds the right thing.
Release stays unsandboxed for direct builds.

The App Store team comes from Local.xcconfig and may differ from the
team that signs day-to-day builds. Without it the configuration falls
back to ad-hoc signing, so it still builds on a fresh clone.

The sandbox rules out the gh CLI login: Cleared can neither run `gh`
there nor read what it stores. The build detects the sandbox at runtime
and makes a pasted token the way in, saying why, instead of showing a
disabled button and "gh CLI not found". Both builds gain a link that
opens GitHub's new-token page with the scope filled in.

The sandboxed build keeps its token under its own Keychain service. It
is signed differently from a direct build and could not read that
build's entry — and trying would raise a Keychain password prompt at
launch.

Also for the store: declare that only exempt encryption is used, a real
copyright line, and a privacy manifest (no tracking, nothing collected,
UserDefaults for the app's own settings).

Checked with a throwaway sandboxed probe carrying the same entitlements:
HTTPS to api.github.com returns 200, and a Keychain add, read, update
and delete round trip succeeds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`make appstore` archives with the AppStore configuration, exports a
package signed for App Store Connect, then unpacks it and refuses to
report success unless the app inside is sandboxed, validly signed, and
free of the get-task-allow entitlement development builds carry. It
uploads nothing. The archive goes where Xcode's Organizer looks, so the
same build can be distributed from there.

`make appstore-build` builds that configuration without archiving and
needs no signing set up. CI runs it and checks for the sandbox
entitlement, so the store build cannot quietly stop compiling.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
How to archive it from Xcode or with `make appstore`, which team
settings it needs, and what differs in the sandboxed build: no gh CLI
login, a separate Keychain entry, settings that start fresh.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@hollandcloud
hollandcloud merged commit c2f31a6 into main Oct 13, 2026
2 checks passed
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.

1 participant