A
?
← Handbook

Credentials and connections

The system must run unattended for months. Everything here was learned by getting it wrong first.

Connections must survive without a human

Authorise once; never again. The test is literal: switch off every laptop, come back a week later, and it still works.

OAuth refresh tokens rotate

Higgsfield issues a new refresh token on every refresh and kills the old one immediately. This is not unusual — assume any OAuth MCP does it.

Consequences, each of which broke something here:

  • Persist every rotation to disk. Holding it in memory works until the process restarts, then fails with nothing obvious to point at — roughly a day after deploy.
  • The file wins over the environment variable. The env var is the bootstrap handoff only; the file holds the live chain.
  • One chain per consumer. Two processes sharing a refresh token will fight, and whichever refreshes second is dead. Dev and production hold separate authorisations for this reason.
  • Collapse concurrent refreshes. Two simultaneous refreshes burn two rotations and the first result is already invalid.

src/lib/mcp/connection.ts implements all of this. Adding another MCP server is a config object — do not write a second copy of this logic.

Failed persistence must be loud

A write failure logs an explicit error and does not fail the request: the current run still has a working token, and the damage lands at the next restart. Silence is what makes that undiagnosable — a /data permission bug hid behind a working system for exactly this reason.

An API key is not a session

Higgsfield's API key authenticates the transport. Catalogue calls succeed with it; anything touching the account returns "session expired". A 200 on initialize is not evidence that authentication is solved.

Two wallets, one login

Higgsfield's subscription credits and its API-platform credits are billed separately. The MCP path spends the subscription; the REST path spends a wallet that has to be funded independently.

Where secrets live

  • .env on the box, mode 600, never committed
  • Rotated tokens in /data, mode 600, on a volume owned by the app user
  • The portal's own MCP endpoint fails closed — an unset MCP_TOKEN disables it rather than serving it open, and the comparison is constant-time

Anyone with root on the droplet can use all of it. That is inherent to unattended operation and is why the box is hardened: key-only SSH, three open ports, fail2ban, unattended security updates.

Google service account (Drive, read-only)

GOOGLE_SA_KEY_B64 in both droplet .env files — the service-account JSON, base64-wrapped because multi-line JSON cannot live in dotenv format. Full provenance, click-paths and the org-policy trap: ../sop/gcp-service-account.md. Scope is drive.readonly via domain-wide delegation; writes come only from Shared Drive membership. This key does not rotate itself — revocation and rotation are one click in the GCP Keys tab plus one set-secret.sh run.