Files
secrets-manager/PROJECT.md
2026-01-30 14:02:52 +01:00

5.1 KiB

Design Proposal: Zero-Knowledge Secrets Manager

1. Architectural Overview

Philosophy: "Thick Client, Blind Server." The system relies on a Zero-Knowledge Architecture. The server (Node.js/PostgREST) and the database never see unencrypted secrets; they act solely as a synchronization engine for encrypted blobs. All encryption/decryption occurs on the client device (Tauri/Flutter) using keys that never leave the device.

High-Level Data Flow

  1. Client: Derives Master Key from (Password + Salt). Encrypts data locally.
  2. Transport: Sends encrypted blob (text/bytea) over HTTPS.
  3. Gateway: Nginx/Traefik handles SSL termination and routing.
  4. Auth: Node.js verifies identity (SRP-6a) and issues JWT.
  5. Persistence: PostgREST accepts JWT, enforces Row-Level Security (RLS), and writes blob to PostgreSQL.

2. Component Design

A. Database Layer (PostgreSQL v18+)

  • Role: Storage and Authorization Engine.
  • Logic: Heavily utilizes Row-Level Security (RLS) policies.
  • Schema Strategy:
    • users: Stores verifier (SRP-6a) or hashed credentials (Argon2id).
    • secrets: Stores encrypted_data (bytea), iv, auth_tag, owner_id, type (manual/rotated).
    • access_policies: Join table mapping users/groups to secrets.
  • Security: RLS policies ensure SELECT * FROM secrets only returns rows where auth.uid() IN (access_policies).

B. Backend API (Node.js v24 + PostgREST)

  • Node.js Service:
    • Auth: Implements SRP-6a (Secure Remote Password) protocol.
    • Business Logic: Handling billing, email alerts, secret rotation jobs, and external webhooks.
    • Orchestration: Manages Redis interactions for ephemeral state.
  • PostgREST:
    • High-performance CRUD layer.
    • Directly exposes secure views/tables.
    • Consumes JWTs issued by the Node.js service for authorization.

C. Caching & State (Redis Cluster v8)

  • Image Source: Official redis:alpine (replacing deprecated Bitnami images).
  • Revocation Lists: Stores revoked JWT JTI claims.
  • Rate Limiting: Distributed counters to block brute-force attacks.
  • Pub/Sub: Real-time event bus to trigger client syncs (e.g., "Vault Updated").

D. Clients (Tauri & Flutter)

  • Desktop (Tauri):
    • Security: Encryption logic (AES-256-GCM / XChaCha20-Poly1305) runs in the Rust backend to prevent memory dumping attacks via browser dev tools.
    • Offline: Encrypted SQLite local replica.
  • Mobile (Flutter):
    • Storage: Uses flutter_secure_storage for session tokens.
    • Bio-auth: Integrates FaceID/Fingerprint to unlock the local vault key.

3. Workflows

A. Manual Secret Entry (Core Feature)

This is the standard workflow for storing static API keys, passwords, or notes.

  1. User Input: User enters data (e.g., API_KEY=12345) into the client form.
  2. Client Encryption: The App (Tauri/Flutter) generates a random IV and encrypts the payload using the user's Master Key in memory.
  3. Payload Construction: Client creates a JSON payload: {"iv": "...", "data": "encrypted_blob...", "type": "manual"}.
  4. Sync: Client POSTs the payload to PostgREST.
  5. Storage: Database stores the blob. The server never sees 12345.

B. Automated Rotation (Enhancement)

  1. Trigger: Cron job in Node.js wakes up.
  2. Execution: Connects to provider (e.g., AWS), generates new keys.
  3. Encryption: Note: This requires a separate "Server-Side Vault" mechanism or Public Key encryption if the server needs to write secrets readable by the user.
  4. Update: Updates the secrets row with the new encrypted blob.

4. Cryptography Standards

  • KDF: Argon2id (memory-hard) for deriving Master Key.
  • Symmetric: AES-256-GCM or XChaCha20-Poly1305 for vault data.
  • Asymmetric: RSA-4096 or ECC (Curve25519) for sharing secrets between users.
  • Transport: TLS 1.3 only.

5. Infrastructure (Docker Compose Prototype)

Note: Switched to official Redis images due to Bitnami Docker Hub deprecation.

version: '3.9'
services:
  # Load Balancer / SSL Termination
  gateway:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    depends_on:
      - api
      - postgrest

  # The Logic Core
  api:
    image: node:24-alpine
    environment:
      - DATABASE_URL=postgres://user:pass@db:5432/secrets_db
      - REDIS_URL=redis://redis:6379
      - JWT_SECRET=${JWT_SECRET}

  # The Data Interface
  postgrest:
    image: postgrest/postgrest:latest
    environment:
      - PGRST_DB_URI=postgres://user:pass@db:5432/secrets_db
      - PGRST_JWT_SECRET=${JWT_SECRET}
      - PGRST_DB_SCHEMA=api
      - PGRST_DB_ANON_ROLE=web_anon

  # The Vault
  db:
    image: postgres:18
    volumes:
      - pg_data:/var/lib/postgresql/data
    environment:
      - POSTGRES_DB=secrets_db
      - POSTGRES_PASSWORD=${DB_PASSWORD}

  # The High-Speed State (Official Image)
  # For a full cluster in dev, you may need a custom entrypoint or sidecar
  # to bootstrap the slots. For simplicity here, we use a standalone instance
  # that mimics the interface.
  redis:
    image: redis:alpine
    command: redis-server --appendonly yes
    volumes:
      - redis_data:/data