Skip to content

[BE-1] Credential Vault, Binance Proxy & Generic SaaS Proxy #28

@iamyxsh

Description

@iamyxsh

[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 (sodiumoxide crate)
  • 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:
    • SecretKey struct with zeroize crate — memory zeroed on drop
    • mlock to 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_at on 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 overridden
    • DELETE /api/v3/openOrders — BLOCKED by default, user can override in policy
  • Allowed endpoints:
    • GET /api/v3/ticker/*, GET /api/v3/klines — read-only, always allowed
    • POST /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-configured base_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_limit and rate_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)

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions