Skip to main content
← Back to list
01Issue
FeatureShippedSwamp CLIPublic
Assigneesstack72

Relationships

#2189 Auth gate: server-side Ed25519 key management and signed verification proofs

Opened by stack72 · 9/16/2026· Shipped 9/16/2026

Problem

The swamp CLI is moving to require authentication for every command (swamp-club/swamp#2182). The server needs to provide Ed25519 signed verification proofs so the CLI can verify identity offline when swamp-club is unreachable.

Design principle: You must prove who you are. Once you have, we won't punish you for our infrastructure being down.

Design doc: https://claude.[REDACTED-SECRET-1]

What the Server Needs to Do

1. Ed25519 key management via better-auth jwt plugin

Add the jwt plugin to better-auth config in lib/auth.ts. Configure for Ed25519 (EdDSA). Key pair lifecycle only — generation, encrypted storage in the DB, rotation tracking. No JWT issuance, no JWKS endpoint. The apiKey plugin stays for credential validation.

2. Canonical JSON proof signing utility

Create lib/verification-proof.ts that constructs a proof payload as canonical JSON (deterministic alphabetical key ordering), signs with Ed25519 using the current private key from better-auth's key store, and returns the payload string, base64url signature, and public key info.

3. Return signed proof from /api/whoami

On authenticated /api/whoami calls, add three new fields (additive, backward-compatible):

  • verificationProof — canonical JSON payload string
  • verificationSignature — base64url Ed25519 signature
  • publicKeys — array of { kid, key } (base64url raw Ed25519 public keys)

4. Signin token for collective tokens

When creating collective tokens (swamp_org_*), also return a signin token — a verification proof with no expiry, for CI environments. Format: base64url-proof.base64url-signature (dot-separated).

Proof Payload Structure

Canonical JSON (alphabetical keys):

  • exp: Unix timestamp, 14 days for interactive, omitted for signin tokens (CI)
  • fpr: SHA-256 hex digest of the full API key string (NOT the existing first-12-chars display fingerprint)
  • iat: Unix timestamp when proof was issued
  • kid: signing key ID from better-auth
  • org: array of collective slugs
  • scopes: array of resolved scopes (e.g. vault:, serve:)
  • sub: user ID (interactive) or collective ID (collective token)

Key Rotation

  • Planned: publicKeys returns both old and new keys during 30-day grace period
  • Emergency (key leak): Return only the new key, skip grace period — all cached proofs become unverifiable

Files to Modify

  • lib/auth.ts — add jwt plugin
  • lib/verification-proof.ts (new) — canonical JSON + Ed25519 signing
  • routes/api/whoami — add proof fields
  • routes/api/collective-tokens — add signin token

Testing

  • Canonical JSON determinism, Ed25519 sign/verify round-trips
  • Proof payload field combinations (interactive/CI, scopes, collectives)
  • Fingerprint computation matching
  • Whoami backward compatibility
  • Key rotation grace period behavior
02Bog Flow
OPENTRIAGEDIN PROGRESSSHIPPED+ 1 MOREASSIGNED+ 5 MOREREVIEW+ 4 MOREPR_MERGED+ 2 MORESESSION_SUMMARIZED

Shipped

9/16/2026, 10:22:51 PM

Click a lifecycle step above to view its details.

03Sludge Pulse
stack72 assigned stack729/16/2026, 6:33:07 PM

Sign in to post a ripple.