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
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions