Technical
Architecture
A deep dive into how Keychains.dev works under the hood — identity, permissions, proxy validation, and the security model.
Four layers of trust
Access flows through four layers. Each builds on the previous — compromise at one layer doesn't break the others.
1
Machine Identity
Ed25519 SSH keypairs + rolling challenge-response + hardware fingerprinting. Proves which machine is acting. Challenge nonces are single-use — replayed signatures fail.
2
Permission Request
Stateful, user-approved authorization stored in Redis. Scoped or wildcard mode. Carries version tracking — any mutation bumps the version, invalidating in-flight tokens.
3
Delegate Identity
Immutable bootstrap identity with its own Ed25519 keypair and proof-of-possession. Allows remote systems to act with a reduced scope ceiling derived from the parent.
4
Permission Token
Short-lived JWT (5-15 min) for execution. Contains request ID and version — no scopes. Scopes are resolved from Redis at proxy time, ensuring revocations take effect instantly.
Proxy validation algorithm
Every proxied request goes through this pipeline. All checks are stateful — the proxy is the final authority.
1. Verify JWT signature
2. Fetch Permission Request from Redis by requestId
3. Validate state: status === active, version matches, not expired
4. If delegateId: validate delegate chain + parent status
5. Validate machine challenge state
6. Resolve approvedScopes from Redis (not from token)
7. Check required scopes vs approved:
→ All present: proceed
→ Wildcard + missing: 403 + approval_url
→ Scoped + missing: 403 no recourse
→ Delegate ceiling exceeded: 403 scope_refused
8. Inject credentials server-side (OAuth tokens / API keys / basic auth)
9. Execute upstream call
10. Write audit log (non-blocking via after())
Wildcard permissions
Wildcard permissions start with zero scopes and grow as the user approves each one. Every scope still requires explicit consent.
1.Agent calls proxy requiring github::repo
2.Proxy returns 403 with approval URL
3.User visits URL, approves github::repo
4.Scope added to approvedScopes, version bumps
5.Agent retries — proxy re-reads Redis — success
Core data model
| Entity | Storage | Key fields |
|---|---|---|
| Machine | Redis | id, userId, publicKey, fingerprint, challengeNonce |
| PermissionRequest | Redis | id, machineId, mode, status, requestedScopes, approvedScopes, version |
| Delegate | Redis | id, parentPermissionId, mode, publicKey, approvedScopes, ttl |
| PermissionToken | JWT (stateless) | requestId, machineId, delegateId, version, exp |
| AuditLog | MongoDB | userId, machineId, permissionId, provider, method, endpoint, status |
Security properties
No secrets in tokens
Permission tokens contain IDs and versions — never scopes, never credentials. Scopes are resolved server-side.
Version pinning
Every mutation bumps the permission version. Tokens with stale versions are rejected. Revocation takes effect in under 1 second.
Cascade revocation
Revoking a parent automatically revokes all delegates. No orphaned access, no cleanup needed.
Rolling challenges
Each machine auth uses a single-use nonce. Concurrent auth from two locations triggers automatic revalidation.
Scope immutability (scoped)
Once approved, scoped permissions can never expand. The set is frozen — auditable and predictable.
Audit everything
Every proxied call is logged in MongoDB with full context. Dashboard provides real-time visibility.
Credential injection
The proxy resolves and injects credentials based on the target API's requirements. Supported methods:
OAuth 2.0 / 2.1
Access tokens refreshed automatically. Token rotation handled server-side.
API Keys
Injected as headers or query params per provider spec.
Basic Auth
Username/password pairs injected as Authorization headers.