e11

Auth Perimeter for Small Teams: CF Access + HMAC

The Auth Perimeter Readable in One Sitting

Small engineering teams need auth systems they can understand completely. The e11-human-auth workstream splits identity (Cloudflare Access at the edge) from authorization (origin Postgres RBAC), creating a sovereign, auditable auth layer that fits in one file of middleware. For teams of one to five engineers, this approach offers better blast radius control than enterprise SSO SDKs while maintaining the security properties that matter.

Architecture Overview

The perimeter operates on two tiers: routine operations flow through Cloudflare Access on *.eleven11.pro hostnames, while privileged operations require WireGuard access to *.boson.eleven11 hostnames. Origins trust the Cf-Access-Authenticated-User-Email header (preserved only on the CF Tunnel path by caddy configuration) and issue their own HMAC-signed session tokens.

Rendering diagram…

How to Set Up the Auth Perimeter

How to

Setting up the auth perimeter

  1. Configure Cloudflare Access

    Set up CF Access policies for your *.eleven11.pro domains. Configure session TTLs: 11 hours for operator surfaces, 2 hours for client recovery. Add operator emails to the appropriate access policies.

  2. Deploy the vendored middleware

    Copy the single-file middleware into your codebase:

    • Python: libs/cf_access/cf_access.py (347 lines including SQLAlchemy store)

    • TypeScript: libs/cf-access/cf-access.ts (portable across Next.js Edge, Node, Bun, Deno)

    Sync updates via scripts/sync-cf-access.sh. No npm or PyPI packages, no transitive dependencies in the auth path.

  3. Configure caddy header handling

    Set up caddy to preserve Cf-Access-* headers only on the CF Tunnel listener and strip them on all other ingress paths. This header-trust contract is load-bearing — review caddy configs as carefully as auth code.

    /data/docker/apps/caddy/sites/example.caddy
    # CF Tunnel listener - preserves headers
    :8080 {
        reverse_proxy origin:3000
    }
    
    # Public listener - strips CF headers
    :443 {
        header_down -Cf-Access-*
        reverse_proxy origin:3000
    }
  4. Set up origin RBAC schema

    Create Postgres tables for principals, grants, sessions, and handshake_tickets. Each origin owns its own auth data — blast radius of a compromise stays local.

    Roles vocabulary: platform_admin, operator, domain_admin, mailbox_owner. All grants are explicit and recorded with granted_by = <operator email>, except auto-bootstrap: first CF-verified hit from an email in mailbox_recovery_contacts auto-grants mailbox_owner.

  5. Configure WireGuard tier

    Set up WireGuard hub-and-spoke for *.boson.eleven11 hostnames. Configure dnsmasq and caddy-boson. Destructive routes return 403 with X-Privileged-Network-Required: true unless the request arrived on the WG-tier Host.

    Handshake tickets crossing CF-tier to WG-tier are 30 seconds, single-use.

Session Token Design

Origins issue HMAC-signed session tokens after the first CF-verified hit. Token format: base64url(session_id).base64url(hmac_sha256(session_id, origin_secret)) — an opaque pointer to a sessions row in origin Postgres. No claims in the token, no JWT verification at origin.

The origin trusts the Cf-Access-Authenticated-User-Email header because caddy configuration guarantees it only arrives via the CF Tunnel path. This deliberate trade-off eliminates JWT verification complexity while maintaining security through network-level controls.

Trade-offs and Constraints

AspectThis ApproachEnterprise SSO
Code surface347 lines Python, 47 lines TypeScriptSDK + transitive dependencies
Blast radiusSingle file, zero depsFramework magic, supply chain
Team size sweet spot1–5 engineers10+ seats
Break-glassSSH + hardware keyOften HTTP token
Pricing modelCF Access onlyPer-seat enterprise

Break-Glass Design

Break-glass is not an HTTP path. It requires SSH over WireGuard with a hardware-resident ed25519-sk key on a YubiKey or Nitrokey. A phished operator session cannot produce hardware-key presence.

There is no BREAK_GLASS_ADMIN_TOKEN. The earlier HTTP-based break-glass was dropped because the SSH path is strictly stronger — an env-var token is phishable, a hardware key is not.

Supply Chain Posture

Supply-chain posture drives the design. Recent disclosures pushed the perimeter to one file deep with zero framework magic. Each dependency in the auth path gets scrutinized. The middleware is vendored as a single file per language, synced via shell scripts, with no package manager in the critical path.

Production Rollout

Phase rollout completed 2026-04-18: Cloudflare Tunnel, vendored middleware with tests, WireGuard hub-and-spoke, RBAC schema in mail-platform-api, and recover-api service live at recover.eleven11.pro. End-to-end verified with real Gmail ingress producing working password reset and IMAP round-trip.

Client mailbox recovery is the first production surface. Onboarding a client takes roughly ten seconds: add the email to the client-recovery-allowlist Cloudflare Access policy.

Security Properties

A phished Gmail session cannot reach privileged routes. Privileged routes are reachable only via a Host ending in .boson.eleven11, and that Host is resolvable only over WireGuard. The combination of a stolen browser session, a WireGuard private key, and a hardware SSH key is not reproducible by phishing alone.

Limits and Next Steps

This approach works for teams who can review the entire auth perimeter in one sitting. For teams past five engineers or organizations requiring SAML federation, enterprise SSO vendors become the better choice despite their SDK surface area.

The middleware files live at libs/cf_access/cf_access.py and libs/cf-access/cf-access.ts. Review the test suites for usage patterns. The caddy configurations in /data/docker/apps/caddy/sites/*.caddy define the trust boundaries that make the whole system work.