The full no-Developer-ID production acceptance set is defined in Docs/ACCEPTANCE_CRITERIA.md.
For operator-facing installation steps and common local macOS pitfalls, see Docs/INSTALLATION.md.
Run the standard contract gate:
./scripts/ci.shRun the macOS Tahoe package/signing gate:
./scripts/tahoe_compatibility_check.shCheck release readiness split by distribution track:
swift run agentic-secrets release-gates
swift run agentic-secrets ipc-conformance
./scripts/check_secret_authority.shcanRunLocal is the production gate for the default self-build track. canDistributeBinary is only for optional future Developer ID releases.
Install from the current checkout:
./scripts/install_local.sh --prefix "$HOME/Library/Application Support/AgenticSecrets/LocalInstall"Update by running the install command again from the desired commit. The script rebuilds, ad-hoc signs, validates, stops any running Agentic Secrets UI, copies the app bundle, refreshes command symlinks, rewrites the install manifest, restarts the broker daemon when it was already loaded or --load is passed, waits for broker IPC health after restart, and opens the installed app when using the default user-local prefix or when the UI was running before the update. Use --no-open for scripted updates.
The app update button first asks whether to open the release page or update now. Update Now downloads the GitHub release source archive into a per-run temporary directory, validates that version.env matches the selected release version, and runs the downloaded scripts/install_local.sh with --load. The updater passes --cleanup-path so the installer removes the downloaded temporary directory after a successful install, even though the running app may close during replacement. It intentionally does not pass --configure-shell; update installs should not append duplicate PATH blocks to shell startup files. Use the guided local install or repair flow when shell configuration needs to be changed.
Smoke-test installed IPC:
PREFIX="$HOME/Library/Application Support/AgenticSecrets/LocalInstall"
SOCKET="/tmp/agentic-secrets-core-smoke.sock"
"$HOME/Applications/AgenticSecrets.app/Contents/MacOS/agentic-secrets-brokerd" serve-once \
--socket "$SOCKET" \
--manifest "$PREFIX/var/agentic-secrets/install-manifest.json" \
--state-dir "$PREFIX/var/agentic-secrets" &
"$PREFIX/bin/agentic-secrets-shim" --ipc-health \
--socket "$SOCKET" \
--manifest "$PREFIX/var/agentic-secrets/install-manifest.json"The native app can remove the local install from Diagnostics → Removal → Remove Local Install. The confirmation dialog removes managed shell PATH entries by default and deletes local Agentic Secrets state only when Delete local Agentic Secrets state is explicitly selected.
Uninstall from the command line without deleting local state or local secret records:
./scripts/uninstall_local.sh --prefix "$HOME/Library/Application Support/AgenticSecrets/LocalInstall" --keep-secretsUninstall and remove local Agentic Secrets state:
./scripts/uninstall_local.sh --prefix "$HOME/Library/Application Support/AgenticSecrets/LocalInstall" --purge-local-stateUninstall removes the per-user LaunchAgent, installed app bundle, helper links, command shims under the local install prefix, runtime files, the socket directory, and Agentic Secrets-managed PATH blocks from known shell startup files. With state purge selected, it also removes local state files and known Agentic Secrets Keychain integrity sidecars for that state directory. Local secret record deletion is intentionally not implicit. Use --purge-local-state only as an explicit operator action, not as a side effect of package removal.
Non-interactive contract check:
./scripts/interactive_keychain_prompt_check.shInteractive prompt-producing check:
AGENTIC_SECRETS_INTERACTIVE=1 ./scripts/interactive_keychain_prompt_check.shInteractive cancellation check:
AGENTIC_SECRETS_INTERACTIVE=1 AGENTIC_SECRETS_EXPECT_CANCEL=1 ./scripts/interactive_keychain_prompt_check.shFor the cancellation check, press Deny or Cancel in the macOS prompt. The command passes only when core reports userCanceled and no secret is resolved.
The interactive path first packages the app and then runs the packaged agentic-secrets-brokerd binary. This matters on macOS Tahoe because restricted Keychain entitlements are not valid for ad-hoc self-build binaries. The default self-build path uses an owner-only local encrypted secret store gated by LocalAuthentication, without shared Keychain access.
The script creates a temporary device-local encrypted secret record, reads it through the decision-bound LocalAuthentication reason, and deletes it. It never prints the generated secret value.
LocalAuthentication may surface as Touch ID, Apple Watch, or the local account password. Treat all of these as valid local user-presence prompts unless the test is specifically verifying cancellation.
The prompt-producing path runs in agentic-secrets-brokerd; CLI and helper targets are guarded by scripts/check_secret_authority.sh from directly using production secret resolution.
Register a CLI app with one secret-backed environment variable:
PREFIX="$HOME/Library/Application Support/AgenticSecrets/LocalInstall"
"$PREFIX/bin/agentic-secrets" cli register supabase \
--env SUPABASE_DB_PASSWORD \
--secret-promptThe database password is read by agentic-secrets-brokerd through a hidden prompt. The front-end CLI process does not parse or persist the value, and the value must not be passed as SUPABASE_DB_PASSWORD=value in argv.
For clipboard or automation use, pipe the value explicitly:
pbpaste | "$PREFIX/bin/agentic-secrets" cli register supabase \
--env SUPABASE_DB_PASSWORD \
--secret-stdinFor multiple environment variables, pass a JSON object over stdin:
printf '%s\n' '{"SUPABASE_DB_PASSWORD":"<redacted>"}' | "$PREFIX/bin/agentic-secrets" cli register supabase \
--env SUPABASE_DB_PASSWORD \
--secrets-json-stdinRun the registered CLI with target arguments after --:
"$PREFIX/bin/agentic-secrets" cli run supabase -- db pullUse --quiet before -- for scripts that do not want AgenticSecrets diagnostic lines on stderr:
"$PREFIX/bin/agentic-secrets" cli run supabase --quiet -- db pullPass non-secret per-run environment values before --:
"$PREFIX/bin/agentic-secrets" cli run supabase --env FOO=bar -- projects listcli run builds a fresh child environment. Inherited secret-like variables are scrubbed, explicit --env NAME=VALUE entries override inherited values, and registered secret deliveries override both for their bound environment names. This means an ambient SUPABASE_* value does not block approved delivery; the broker rewrites that variable for the child process only.
Unregister the CLI app and delete its local secret records:
"$PREFIX/bin/agentic-secrets" cli unregister supabase --delete-secretsRegistration stores non-secret metadata in var/agentic-secrets/cli-registry.json and encrypted secret records under var/agentic-secrets/secrets/. Registry files are owner-only and must not contain plaintext token material. The registry is also paired with var/agentic-secrets/cli-registry.integrity.json; that sidecar is signed with an HMAC-SHA256 key stored in the user's macOS Keychain using WhenUnlockedThisDeviceOnly accessibility. If the registry or sidecar is edited outside Agentic Secrets, cli run fails before local authentication and before any secret-store read.
During cli run, the front-end CLI still does not resolve the secret; agentic-secrets-brokerd resolves it after local authentication, scrubs inherited secret-like environment variables, and injects the registered environment variables only into the child process. Registered non-generic CLIs may receive env delivery and reuse approval according to the configured authorization mode even when no versioned command adapter classifies the exact command. Generic runners such as shells, package managers, and language runtimes still deny raw env secret delivery by default.
After a successful local authentication prompt, Secret Broker writes an HMAC-signed CLI authorization grant under Agentic Secrets state. The default mode is always, which does not expire. remember-24h expires after 24 hours, short uses the 300 second default TTL with a 900 second maximum, and once disables reuse. Grants contain no secret material. Persistent grants are signed with a device-local macOS Keychain key and scoped to CLI name, target identity, workspace hash, config context, untrusted origin hint, provenance confidence, delivery mode, and secret alias. Short grants additionally bind action class, command digest, and risk. Matching runs reuse the grant and skip the LocalAuthentication prompt; non-matching runs prompt again. Each command is still policy-checked before secret delivery, and destructive commands require fresh approval.
The LocalAuthentication prompt shows the parent app display name when available. Environment-derived names are display context only; they do not make the origin trusted.
Per-run authorization mode:
"$PREFIX/bin/agentic-secrets" cli run supabase --authorization-mode remember-24h -- db pull
"$PREFIX/bin/agentic-secrets" cli run supabase --authorization-mode short --delivery-grant-ttl-seconds 60 -- db pull
"$PREFIX/bin/agentic-secrets" cli run supabase --authorization-mode once -- db pullLegacy TTL override still selects short authorization mode:
"$PREFIX/bin/agentic-secrets" cli run supabase --delivery-grant-ttl-seconds 0 -- db pullAgentic Secrets does not require per-CLI shims in the default self-build flow. Installed Agentic Secrets executables are symlinked under $PREFIX/bin, while registered apps are stored as metadata in cli-registry.json. When a CLI is auto-discovered from PATH, the registry keeps the stable invocation path such as /opt/homebrew/bin/supabase, plus the target binary identity captured at registration time. Each cli run resolves the current target, validates it against the captured macOS designated requirement when available, and otherwise falls back to SHA-256 identity pinning. Homebrew CLI upgrades therefore fail closed until the CLI target trust is refreshed after you verify the new binary. Because this changes trusted identity metadata, it requires LocalAuthentication:
"$PREFIX/bin/agentic-secrets" cli trust-refresh supabasetrust-refresh updates only target identity metadata and re-seals the registry integrity sidecar; it does not read, rewrite, or ask for the token again. If local authentication is canceled or the target changes between the authentication prompt and the registry write, the command fails closed and leaves the previous trust metadata intact. A manually registered versioned path such as /opt/homebrew/Cellar/supabase/1.65.0/bin/supabase is also pinned and must be trust-refreshed or registered again after the version is removed.
Optional shim mode is available when users want supabase ... to route through Agentic Secrets directly:
agentic-secrets cli shim install supabase --configure-shellThis creates $PREFIX/shims/supabase as a symlink to the installed agentic-secrets-shim binary and prepends the shim directory to future shell sessions. It does not edit or replace /opt/homebrew/bin/supabase.
Normal shimmed commands route to agentic-secrets cli run supabase -- .... Global help/version commands pass through to the registered target without secret resolution or injection:
supabase --help
supabase db --help
supabase versionThe pass-through environment is still scrubbed of inherited secret-like variables.
Pass-through help/version reads only non-secret registry metadata and avoids the registry Keychain integrity key. Secret-bearing commands still verify registry integrity inside core before resolving local secret material.
If supabase db pull works through agentic-secrets cli run supabase -- ...
but fails inside Codex App with no active context or token, verify that Codex
resolves supabase to the Agentic Secrets shim:
command -v supabase
agentic-secrets cli shim install supabase --forceDo not fix this by adding SUPABASE_DB_PASSWORD to ~/.codex/.env; keep provider
secrets out of Codex process environment and route through Agentic Secrets.
List built-in adapter metadata:
swift run agentic-secrets adapter listInstall a verified payload into a registry document:
swift run agentic-secrets adapter install-payload payload.json state/policyPacks.jsonRevoke an adapter:
swift run agentic-secrets command policy pack revoke com.example.adapter state/policyPacks.jsonExternal command policy packs must be signed and verified before they are accepted by production policy. Adapter id, version, and hash are part of remembered lease scope.
Rollback mismatch puts policy into locked mode. Allowed operator actions:
- export diagnostic summary
- reset local policy
- import recovery bundle
- rebind providers
Blocked actions:
- secret delivery
- remembered approvals
- provider leases
- MCP bridge sessions
Accepting an old policy database never preserves remembered approvals.
Rotation order:
- create new Bitwarden token
- store new token through the broker-owned local secret store under the configured Bitwarden alias
- test exact approved secret access
- switch binding
- invalidate provider leases
- revoke old token
- write redacted audit event
Production Bitwarden provider profiles require per-fetch approval by default.
API session profiles must define:
- upstream origin
- allowed path prefixes
- allowed methods
- secret alias
- token TTL
Request and response bodies are not logged by default.
MCP proxy profiles must define:
- pinned original URL
- auth header
- auth token secret alias
The bridge propagates MCP-Session-Id when supplied by the upstream server. Tool filtering is only a guardrail; upstream authorization scope remains the real security boundary.
The native app is the preferred recovery path for ordinary users:
- Open Diagnostics.
- Review the Daemon section.
- Use Install Local Daemon when the local self-build daemon has not been installed.
- Use Repair Local Daemon when helper links, the install manifest, or the LaunchAgent need to be refreshed.
- Use Restart Daemon when the LaunchAgent exists but IPC is unavailable.
The UI shows the app copy, helper directory, state directory, run directory, install manifest, LaunchAgent, and socket before changing files. It does not read or move local secret material. If the app was launched from a temporary build location, open the installed copy after installation so IPC authorization matches the install manifest.
Run these checks before accepting a local production release:
./scripts/ci.sh
./scripts/tahoe_compatibility_check.sh
./scripts/check_secret_authority.sh
./scripts/check_entitlements_diff.sh "build/AgenticSecrets.app"
swift run agentic-secrets release-gates
swift run agentic-secrets ipc-conformance
swift run agentic-secrets mcp-conformance
./scripts/create_release_evidence.shDiagnostics must not include raw provider tokens, Keychain values, or full Authorization headers. Use agentic-secrets redact for ad-hoc log review.
Local package validation:
./scripts/package_release.sh
./scripts/validate_release_artifact.sh "build/AgenticSecrets.app"
./scripts/check_entitlements_diff.sh "build/AgenticSecrets.app"
./scripts/create_release_evidence.shThe default supported distribution path is source checkout plus local ad-hoc signing. Developer ID signing and notarization are optional future maintainer steps for downloadable binaries.
Optional distribution signing and notarization:
CODESIGN_IDENTITY="Developer ID Application: ..." \
NOTARYTOOL_PROFILE="agentic-secrets-notary" \
./scripts/sign_notarize_release.shCredentials must be stored in the macOS keychain via xcrun notarytool store-credentials. Do not put signing or notarization secrets in repository files.
See Docs/FUTURE_DEVELOPER_ID.md for the optional Developer ID roadmap.