Skip to content

feat: OnlyKey Vault — hardware-backed secret protection via FIDO2#1

Open
0c-coder wants to merge 1 commit intomainfrom
feat/onlykey-vault-integration
Open

feat: OnlyKey Vault — hardware-backed secret protection via FIDO2#1
0c-coder wants to merge 1 commit intomainfrom
feat/onlykey-vault-integration

Conversation

@0c-coder
Copy link
Owner

Summary

  • Add OnlyKey Vault system: secrets encrypted with per-record AES-256-GCM keys, wrapped using shared secrets derived from OnlyKey's FIDO2/WebAuthn bridge (ok.derive_shared_secret). OneCLI stores NO private decryption keys — recovery requires browser + OnlyKey hardware presence.
  • Rust gateway vault module with HKDF-SHA256 key derivation, in-memory unlock cache (TTL + idle timeout + scoped revocation), and 6 new API endpoints for access/lock/approve flows.
  • Browser-side OnlyKey bridge using real node-onlykey library, with polling-based approval UI in the Next.js dashboard.
  • Vault records integrate into the existing CONNECT resolution — unlocked vault secrets are injected alongside regular secrets automatically.

Architecture

Agent → POST /v1/vault/records/:id/access → Gateway checks cache
  ↓ (if locked)
Gateway creates pending approval → Browser polls /v1/vault/browser/pending
  ↓
Browser + OnlyKey → ok.derive_shared_secret() → POST /v1/vault/browser/approve
  ↓
Gateway: HKDF → unwrap record key → cache in RAM (TTL) → decrypt → serve

New files (22 files, ~3000 lines)

Rust gateway (apps/gateway/src/vault/): mod.rs, models.rs, crypto.rs, cache.rs, db.rs, api.rs

Browser bridge (apps/web/src/lib/vault/): types.ts, onlykey.ts, api.ts, index.ts

Next.js UI (apps/web/src/app/(dashboard)/vault/): page + components

Database: Prisma schema + SQL migration for VaultRecord, VaultApprovalRequest, VaultAuditEvent

Key design decisions

  • Per-record OneCLI public key as input to ok.derive_shared_secret() — deterministic re-derivation without storing private keys
  • HKDF subkey derivation — raw shared secret never used directly as AES key
  • Scoped unlock cache — supports global/agent/session scope with both absolute TTL and idle timeout
  • Revocation events — manual lock, TTL expiry, idle timeout, browser disconnect, policy change, key rotation, server restart
  • Policy per record — configurable TTL, idle timeout, cache scope, high-risk fresh-unlock requirement

Test plan

  • Verify Rust cargo test passes for vault crypto (HKDF, AES-GCM round-trips, wrap/unwrap)
  • Verify Rust cargo test passes for vault cache (TTL, idle, revocation, cleanup)
  • Run pnpm db:migrate to apply the new migration
  • Test vault record creation via the web UI
  • Test end-to-end: agent request → pending approval → OnlyKey approve → secret served
  • Verify existing secrets still work unchanged (backward compatible)

🤖 Generated with Claude Code

… bridge

Secrets encrypted with per-record AES-256-GCM keys, wrapped using shared
secrets derived from OnlyKey's FIDO2/WebAuthn bridge. OneCLI stores NO
private decryption keys — recovery requires browser + OnlyKey hardware.

Rust gateway (apps/gateway/src/vault/):
- HKDF-SHA256 key derivation + AES-256-GCM wrap/unwrap/encrypt/decrypt
- In-memory unlock cache with absolute TTL, idle timeout, scoped unlocks
- Revocation by record/agent/browser-session/all + periodic cleanup
- API: access, lock, pending approvals, browser approve, admin controls
- Vault records checked during CONNECT resolution alongside existing secrets

Browser bridge (apps/web/src/lib/vault/):
- Real node-onlykey integration via ok.derive_shared_secret()
- TypeScript types mirroring Rust models
- Gateway API client with polling for pending approvals

Next.js UI (apps/web/src/app/(dashboard)/vault/):
- OnlyKey connection status + pending approval cards
- Approve/deny/lock controls with touch-to-approve UX

Database (packages/db/prisma/):
- VaultRecord, VaultApprovalRequest, VaultAuditEvent models
- Policy: TTL, idle timeout, cache scope, per-agent permissions
- Versioning for cache invalidation (record/policy/key/unlock generation)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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