-
Notifications
You must be signed in to change notification settings - Fork 0
Open
Labels
Description
[BE-1] Credential Vault, Binance Proxy & Generic SaaS Proxy
Labels: backend, priority:high, week-1-4
Assignee: Backend Dev
Context
Per Sections 5, 8 of the Source of Truth, the proxy currently forwards to OpenAI/Anthropic but does NOT store or inject real API keys from an encrypted vault. The credential vault is the backbone of the entire product — "the agent never holds real credentials." Additionally, Binance and generic REST API proxying are not implemented.
1. Encrypted Credential Vault (Section 8 — Week 1-2)
Owner: Backend Dev | Files: src/vault/
The entire vault module is missing. This is the highest-priority backend work.
- Encrypted SQLite storage using
libsodium secretbox(sodiumoxidecrate) - Schema:
credentials ( id TEXT PRIMARY KEY, -- uuid service TEXT NOT NULL, -- "openai" | "anthropic" | "binance" | ... name TEXT NOT NULL, -- user-friendly name encrypted_key BLOB NOT NULL, -- libsodium secretbox(real_api_key) nonce BLOB NOT NULL, -- encryption nonce created_at INTEGER NOT NULL, -- unix timestamp last_used_at INTEGER -- updated on each proxy request )
- Master password key derivation via Argon2id — memory cost: 256MB, iterations: 3
- macOS Keychain integration — optionally store derived key in Keychain so user doesn't re-enter on restart
- In-memory handling:
SecretKeystruct withzeroizecrate — memory zeroed on dropmlockto prevent swap to disk- Key exists in memory only for milliseconds during proxy request
use zeroize::Zeroize; struct SecretKey { bytes: Vec<u8> } impl Drop for SecretKey { fn drop(&mut self) { self.bytes.zeroize(); } }
- Vault file permissions:
chmod 600(owner read/write only) - API endpoints:
GET /api/credentials— returns[{id, service, name, created_at, last_used_at}](never return keys)POST /api/credentials— accepts{service, name, key}, encrypts and stores, returns{id, service, name}DELETE /api/credentials/:id— removes credential, returns{deleted: true}
2. Proxy Credential Injection
Wire the vault into the existing proxy pipeline so real keys are injected at request time:
- On each proxied request: lookup service -> decrypt credential from vault -> inject into upstream request header -> zero key after response
- Update
last_used_aton each proxy request - Handle "credential not found" gracefully — return clear error to agent
- OpenAI: inject
Authorization: Bearer <key> - Anthropic: inject
x-api-key: <key>
3. Binance Exchange Proxy (Section 5.2 — Week 3-4)
- Route:
localhost:8472/binance/api/*->api.binance.com/api/* - HMAC-SHA256 auth: Fishnet stores both API key and secret. On each request, compute HMAC signature from request params + secret, append to request. Agent never sees raw secret.
- Hardcoded endpoint blocking:
POST /sapi/v1/capital/withdraw/*— BLOCKED, cannot be overriddenDELETE /api/v3/openOrders— BLOCKED by default, user can override in policy
- Allowed endpoints:
GET /api/v3/ticker/*,GET /api/v3/klines— read-only, always allowedPOST /api/v3/order— allowed with limits (max order value USD, daily volume cap)
- Per-trade volume tracking: daily trade volume counter in spend DB
- Parse order value from request body, check against policy limits before forwarding
4. Generic SaaS API Proxy (Section 5.3 — Week 3-4)
- Route:
localhost:8472/custom/{name}/*-> user-configuredbase_url/* - TOML config parsing:
[custom.github] base_url = "https://api.github.com" auth_header = "Authorization" auth_value_prefix = "Bearer " blocked_endpoints = ["DELETE /repos/*", "PUT /repos/*/admin/*", "DELETE /orgs/*"] rate_limit = 100 rate_limit_window_seconds = 3600
- Wildcard pattern matching for endpoint blocking (e.g.,
DELETE /repos/*) - Auth injection — configurable header name, prefix, credential from vault
- Per-service rate limiting — token bucket using configured
rate_limitandrate_limit_window_seconds - Support any REST API without code changes — fully user-configurable
Acceptance Criteria
- Credentials are encrypted at rest with Argon2-derived key
- Real API keys are never returned by any API endpoint
- Keys exist in memory only during request forwarding, then zeroed
- Binance withdrawals are physically impossible through the proxy (hardcoded block)
- Generic SaaS proxy works with any REST API via TOML config
- All proxy requests inject credentials from vault (not from env vars or hardcoded values)
Reactions are currently unavailable