fix(agent): per-request signing authorization (#354)#356
Conversation
The signing agent held unlocked keys and would sign for any same-user process that reached its socket during the unlock window. Add a per-request SignAuthorizer the agent consults before every signature, and a PerCallerAuthorizer that approves a peer once per connecting process and pins it (Linux keys by uid+pid; macOS has no peer pid and falls back to a time-bucketed re-auth). The socket path now reads the peer (uid, pid) and injects the authorizer into each session. The shipped default stays permissive (AllowAllSigning) so signing behavior is unchanged; enforcement turns on when the host injects an approval prompt. Adds a failing-then-passing test that a denied peer gets no signature, plus a per-caller pinning test. Auths-Id: did:keri:EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd Auths-Device: did:keri:EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd Auths-Anchor-Seq: 1
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Auths Commit Verification
Result: ❌ 0/1 commits verified How to fixCommit 1. Install auths macOS: 2. One-time setup (creates your identity and configures Git) auths init3. Sign this branch and push auths sign origin/main..HEAD
git push --force-with-leaseFor CI to verify the signer, commit an identity bundle: auths id export-bundle --alias main --output .auths/ci-bundle.json --max-age-secs 31536000 |
…al for the interactive agent (#354) Thread the SignAuthorizer through the agent serve path (start_agent_listener_with_handle) as injected policy instead of a hardcoded permissive default. The CLI now selects the policy by context: an interactive (TTY) foreground agent gates each new connecting process behind a terminal approval prompt and pins it (PerCallerAuthorizer); a daemonized / headless agent stays permissive, since there is no human present to approve. CI is unaffected (it signs via the direct keychain path, not the agent). The macOS Touch ID / Linux GUI prompt for the daemonized case is the remaining backend; the LocalAuthentication infra to build it on already exists. Auths-Id: did:keri:EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd Auths-Device: did:keri:EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd Auths-Anchor-Seq: 1
Auths Commit Verification
Result: ❌ 0/2 commits verified How to fixCommit 1. Install auths macOS: 2. One-time setup (creates your identity and configures Git) auths init3. Sign this branch and push auths sign origin/main..HEAD
git push --force-with-leaseFor CI to verify the signer, commit an identity bundle: auths id export-bundle --alias main --output .auths/ci-bundle.json --max-age-secs 31536000 |
|
Update (
Verified: full chain (cli → sdk → core) compiles clean; 7/7 agent tests pass including the socket integration test now routed through the injected authorizer. Remaining for full enforcement on the common (daemonized) deployment: the macOS Touch ID / Linux GUI approval backend. The infra exists to build it on ( |
Addresses #354 (partial — lands the enforceable mechanism + tested policy; the platform approval prompt that flips enforcement to on is the remaining follow-up).
Problem
The signing agent (the SSH-agent used for git commit signing) held unlocked keys and would sign for any same-user process that reached its socket during the unlock window — no per-request authorization, no biometric. The existing peer-UID check gates other users, not another process you run.
What this PR does (TDD)
agent_refuses_to_sign_when_authorizer_deniesfailed against current code (the agent signs for anyone); now passes via a new gate.SignAuthorizerhook consulted inAgentSession::signbefore every signature — deny ⇒ refuse.PerCallerAuthorizer(the chosen policy): approves a peer once per connecting process and pins it, so the legitimate caller isn't re-prompted per signature while a different process triggers a fresh approval. Tested (per_caller_pins_approved_and_reprompts_a_different_process). Keyed by(uid, pid); macOS has no peer pid → falls back to time-bucketed re-auth.PeerAuthorizedAgentreads the peer(uid, pid)and injects the authorizer per session. Authorizer types re-exported as public API for host injection.Impact (why it's a policy, not a hard prompt)
auths sign(direct keychain decrypt), never the agent.Honest limitation
The shipped default is permissive (
AllowAllSigning) — signing behavior is unchanged. Real enforcement requires the platform approval prompt (macOS LocalAuthentication / Touch ID, or a Linux prompt) injected as thePerCallerAuthorizerapprovalFnat the CLI boundary. That platform piece is deliberately a follow-up rather than faked.Tests
All 14 agent tests green (2 new + 12 existing);
auths-corecompiles clean. Full guard (clippy--workspace, fmt-all) not yet run — left to CI.