This was generated by AI during triage.
Background
The local CA private key currently lives in the login keychain as a non-extractable software ECDSA P-256 key. We pivoted there from the Secure Enclave because the SE path requires keychain-access-groups / application-id entitlements, and an unsigned cargo-built adj binary can't carry them — every SE persist returned errSecMissingEntitlement. See the design note at the top of crates/adj/src/tls/keychain.rs.
The login-keychain path is acceptable (encrypted at rest, kSecAttrIsExtractable=false, no export via Keychain Access). The Secure Enclave is strictly stronger — the key never exists in user-space memory, signing runs on a separate co-processor, extraction is hardware-prevented.
What we want
Stand up CI that produces a signed, notarized release binary and ships it via Homebrew. Once adj is signed with a Developer ID and carries the right entitlement, the SE keychain ACL will let us persist and use an SE-backed key again. Source builds keep the current login-keychain backend.
Scope
- CI workflow that cross-builds
adj for macOS (arm64 + x86_64) on release tag.
- Developer ID signing + notarization in CI (P12 + app-specific password as encrypted secrets).
- Homebrew tap / formula so users get the signed binary instead of
cargo install-ing from source.
- Embedded entitlements (
keychain-access-groups, application-identifier) so SE storage is permitted.
- Runtime backend selection in
tls/keychain.rs:
- signed binary with SE entitlement →
Token::SecureEnclave
- unsigned / source build → login keychain (current path)
install-ca reports which backend it picked.
Out of scope
- Windows / Linux signing.
- Auto-update.
Why not SE-only
Local-dev users running cargo run -- or building from source still need a working CA path. Two backends behind the same RemoteKeyPair interface keep source builds first-class.
Background
The local CA private key currently lives in the login keychain as a non-extractable software ECDSA P-256 key. We pivoted there from the Secure Enclave because the SE path requires
keychain-access-groups/ application-id entitlements, and an unsignedcargo-builtadjbinary can't carry them — every SE persist returnederrSecMissingEntitlement. See the design note at the top ofcrates/adj/src/tls/keychain.rs.The login-keychain path is acceptable (encrypted at rest,
kSecAttrIsExtractable=false, no export via Keychain Access). The Secure Enclave is strictly stronger — the key never exists in user-space memory, signing runs on a separate co-processor, extraction is hardware-prevented.What we want
Stand up CI that produces a signed, notarized release binary and ships it via Homebrew. Once
adjis signed with a Developer ID and carries the right entitlement, the SE keychain ACL will let us persist and use an SE-backed key again. Source builds keep the current login-keychain backend.Scope
adjfor macOS (arm64 + x86_64) on release tag.cargo install-ing from source.keychain-access-groups, application-identifier) so SE storage is permitted.tls/keychain.rs:Token::SecureEnclaveinstall-careports which backend it picked.Out of scope
Why not SE-only
Local-dev users running
cargo run --or building from source still need a working CA path. Two backends behind the sameRemoteKeyPairinterface keep source builds first-class.