Skip to main content

Device Registration, mTLS Provisioning, and Hardware-Key Tunnel Authorization

This page describes how a user registers a device, how the platform issues that device a short-lived mTLS client certificate from a CLI-generated CSR (or accepts a tenant BYOK CA), and how the device-paired reverse tunnel is gated by a hardware key (FIDO2).

The system of record is the IAM hub, alphaswarm_auth. The reverse-tunnel relay lives in alphaswarm_controller. The connector + CSR generation live in alphaswarm_cli. The operator UI lives in alphaswarm_ui.

End-to-end flow​

Layered security model​

Three independent factors gate a tunnel:

  • Device certificate (machine identity) — a short-lived (~24h) X.509 client cert whose SAN URI is spiffe://<trust_domain>/device/<device_id>. Rides the tunnel transport on /tunnel/agent.
  • Device credential (DC) — the existing HS256 token (token_use=device_credential, aud=alphaswarm-tunnel) that authenticates the connector socket. Minted by redeeming a pairing code.
  • Hardware key (FIDO2) — gates enrollment (pairing + CSR signing, when the step-up flags are on) and use (/tunnel/proxy requires a fresh hwk step-up on the user's token T, RFC 9470).

The mTLS certificate's private key is generated on the device and never leaves it — only the PEM CSR is transmitted. The CA private key is held envelope-encrypted (Vault Transit when VAULT_ADDR is set, else a local AES-256-GCM fallback).

Operator surfaces​

  • CLI: alphaswarm-cli connect redeem <code> -> auth yubikey login -> connect provision-cert -> connect up. Certs auto-renew before expiry on connect up (best-effort; a missing hardware-key step-up prints a hint).
  • UI: Settings -> Devices & Keys lists devices + their certificates, enrolls hardware keys (reusing the existing passkey ceremony), and revokes certs (revocation is hardware-key step-up gated).
  • Kill-switch: fans out to POST /tunnel/halt (closes live tunnels) and POST /auth/devices/halt (revokes every device credential + certificate) alongside the existing halt endpoints.

REST surface (alphaswarm_auth)​

  • POST/GET/DELETE /auth/devices, POST /auth/devices/pair, POST /auth/devices/pair/redeem, POST /auth/devices/halt.
  • POST /auth/devices/{id}/certificate (CSR -> signed cert), GET /auth/devices/{id}/certificate, DELETE /auth/devices/{id}/certificate/{serial}.
  • GET /auth/ca/trust-bundle, GET /auth/ca/revocations (public PKI artifacts), POST/GET /auth/ca/trust-anchors (BYOK).
  • WebAuthn: GET/POST /auth/yubikey/{register,authenticate}/{options,verify} (fido2 2.x; the verify body carries an opaque state).

Configuration​

alphaswarm_auth (ALPHASWARM_AUTH_*):

  • POSTGRES_DSN — async DSN (empty -> local sqlite; RLS is Postgres-only).
  • WEBAUTHN_RP_ID / WEBAUTHN_RP_NAME / WEBAUTHN_ORIGINS — MUST match the served domain (e.g. app.alpha-swarm.ai). The legacy default alphaswarm.local only works for a local CLI.
  • WEBAUTHN_REQUIRE_USER_VERIFICATION / WEBAUTHN_REQUIRE_RESIDENT_KEY (default true), WEBAUTHN_REQUIRE_HARDWARE_KEY (reject platform passkeys), WEBAUTHN_ALLOWED_AAGUIDS (optional allow-list).
  • PAIRING_REQUIRE_STEP_UP (default false), CERTIFICATE_REQUIRE_STEP_UP (default true), STEP_UP_MAX_AGE_SECONDS (default 300).
  • CA_ENABLED, CA_TRUST_DOMAIN (default alpha-swarm.ai), CA_COMMON_NAME, CA_ROOT_TTL_DAYS, CA_CERT_TTL_SECONDS (default 24h), CA_KEY_ALGORITHM (ec|rsa), BYOK_ENABLED.
  • VAULT_ADDR + LOCAL_ENVELOPE_KEY — CA-key-at-rest envelope.

alphaswarm_controller (ALPHASWARM_CP_*):

  • TUNNEL_REQUIRE_MTLS — require a valid device cert on /tunnel/agent.
  • TUNNEL_CLIENT_CERT_HEADER — verified-client-cert header set by an mTLS-terminating ingress (nginx $ssl_client_escaped_cert / Envoy XFCC). Absent -> the relay falls back to an app-level proof-of-possession challenge over the WebSocket.
  • TUNNEL_REQUIRE_HARDWARE_KEY + TUNNEL_STEP_UP_MAX_AGE_SECONDS — gate /tunnel/proxy on a fresh hwk step-up.
  • TUNNEL_CA_BUNDLE_TTL_SECONDS — CA-bundle/CRL cache TTL.

alphaswarm_cli: ALPHASWARM_CLI_WEBAUTHN_ORIGIN (defaults to https://alphaswarm.local; set to the served origin in hosted deployments).

Persistence​

alphaswarm_auth owns the schema (Alembic 0001_devices_webauthn_ca): devices, pairing_codes, device_credentials, webauthn_credentials, ca_keys, device_certificates, auth_audit_events. The browseable tables (devices, webauthn_credentials, device_certificates) carry FORCE ROW LEVEL SECURITY keyed on the per-transaction app.current_workspace_id GUC; the secret-keyed tables (pairing_codes, device_credentials) are looked up by their high-entropy secret. The application MUST connect as a non-superuser role without BYPASSRLS.

Session revocation integration​

POST /auth/devices/halt is the integration point for the monolith's session-revocation cleanup (hard rule 53): on session revoke / user delete, call it (with the user's token) to revoke every device credential + cert, and call the control-plane POST /tunnel/halt to drop live sockets.

Caveats​

  • The reverse-tunnel registry is single-replica (process-local). Horizontal scaling needs sticky routing by device_id or shared pub/sub.
  • A deleted device cascade-removes its certificates; explicit DELETE .../certificate/{serial} keeps the row and lists it on the CRL until expiry.