Sign in spec

Architecture

One sentence carries most of it: Keycloak identifies the subject. Pigsty owns ComputeDriven truth. The node key identifies the machine.

Where things run

                    IDENTITY
              Keycloak  ·  Fly iad
                       │
              PlanetScale PS-5 HA
              KEYCLOAK DATABASE ONLY
                       │
                       ▼
 ──────────────────────────────────────────────
                  CLOUDFLARE
    marketing · portal BFF · API · authorization
      R2 credential minting · presence (later)
                       │
                   Hyperdrive
                       │
                  Workers VPC
                       │
                Cloudflare Tunnel
                       │
                       ▼
 ──────────────────────────────────────────────
                    HETZNER
                   PIGSTY PG
           queryable ComputeDriven truth
                       │
                       │ refs only — never bytes
                       ▼
 ──────────────────────────────────────────────
                       R2
              machine-sized worlds
            chunks · manifests · data
 ──────────────────────────────────────────────
                       ▲
                       │  direct transfer, scoped credential
                       │
              SUPER / USER MACHINES
           compute + local authority
Three assertions, each of which was nearly lost — see below.

The control plane runs on Workers, not on a server

Cloudflare Workers are V8 isolates: JavaScript and WebAssembly, no BEAM. Our own runtime, Super (CD), is Elixir/OTP — and that is not a reason for the cloud to be. Super being Elixir is a fact about the user's machine. Hyperdrive lets a Worker reach a private PostgreSQL over a Workers VPC and a Cloudflare Tunnel, so Pigsty keeps its guarantees without exposing a Postgres port to the internet.

Bytes never pass through the Worker

Super / client
      │  authenticate  ───────────►  Worker
      │                                ├── resolve principal + organization
      │                                ├── consult Pigsty
      │                                ├── authorize the requested object scope
      │                                └── mint a scoped, short-lived credential
      │  ◄──────────────────────────────┘
      │
      └──────────── chunks ───────────►  R2      direct
Not client → Worker → 400 GB → R2. The credential already carries the authorization; a proxy in the byte path buys nothing and costs everything.

Pigsty holds references, never payloads

Bytes in Postgres cost roughly 18.7× what they cost in object storage. The database holds the hash, the size, the ownership and the linkage; R2 holds the chunks. That split is what makes a 10 TB customer arithmetic rather than a catastrophe.

Three identities that must never collapse

The failure this prevents is mundane and expensive: someone rebuilds a virtual machine and history quietly rewrites itself.

principal_id       the human or service asking
node_id            the ComputeDriven execution identity
provider_id        the infrastructure incarnation  (e.g. a Hetzner server id)
A provider's server id, an IP address, a hostname or an IdP alias is never the authority identity.

So a hosted allocation — "keep one 16-core runner available" — is durable customer intent, and the machine currently serving it is an incarnation of that intent. Destroy and rebuild the machine and the allocation survives with a new incarnation. The receipts that named the old one stay true, because they named something that really did exist.

An identity provider is a binding, not an identity

principals and organizations are ComputeDriven's own id space. Keycloak's subject and its organization id live beside them in principal_identities and organization_identities, each with a status and a history.

This is not a defence against backups — a faithful restore of the same Keycloak database keeps its ids. It is a defence against a clean realm rebuild, a realm migration, an identity-provider change, or leaving Keycloak entirely. With bindings, each of those is a rebind. With a keycloak_sub column on the user row, each is a data-loss event with no recovery path.