Clustering with Fabric

A deployment targets the Fabric, not a machine. The Fabric keeps sites running across replaceable nodes.

What the Fabric is

The Fabric makes multi-tenant site runtimes highly available across replaceable Crisptastic nodes. A deployment targets the Fabric, not a machine. The Fabric continuously reconciles the desired state (which Capsules, how many replicas) against healthy replicas, and heals after a node is lost.

It is a private, volatile, in-memory cluster:

  • no plaintext application secret ever enters persistent Fabric state;
  • a total Fabric outage may lose volatile secrets permanently, by design;
  • nodes are cattle — a restarted process gets a fresh 128-bit boot ID and rejoins as a learner before it can vote.

The Fabric is not the proxy cluster

Two independent clusters:

Reverse proxy (e.g. Kismet)Crisptastic Fabric
Terminates public TLSPlaces Capsules on nodes
Accepts public connectionsRuns and repairs site replicas
Picks a healthy Crisptastic originOwns readiness, secret continuity, sessions

The proxy must not be the only store of placement, deployment or session state — a working proxy cannot make a single site process highly available.

Topologies

NodesProfileBehaviour on one loss
1development / non-HAsite down until re-activated
2minimum site HAsurvivor keeps serving as serving_degraded; all control-plane changes freeze
3 voterssmallest production HAtolerates one loss, keeps a majority for control changes
morescale outsites still default to 2 replicas each

Default: two replicas per site, two required readiness acknowledgements.

Running a clustered node

crisptastic host \
  --identity /run/credentials/capsule.agekey \
  --trusted-signer /run/credentials/release.pub \
  --host-root /var/lib/crisptastic \
  --fabric-config /etc/crisptastic/fabric.toml \
  --s3

--fabric-config is mutually exclusive with positional Capsules and --digest. With Fabric configured, the mTLS management listener becomes the authenticated clustered deployment entry point; it never activates a local route directly.

A joiner config names this process's identity and at least one live seed:

cluster_id       = "1111...."
placement_salt   = "0123....(64 hex)"
boot_id          = "aaaa....(32 hex)"
credential_serial = 1
bind             = "10.20.0.11:7443"
advertise        = "10.20.0.11:7443"
certificate_der  = "node-a.der"
private_key_der  = "node-a.pkcs8.der"
ca_certificate_der = "fabric-ca.der"
admission_authority_env = "CRISPTASTIC_FABRIC_ADMISSION_AUTHORITY"

[[peers]]
boot_id = "bbbb....(32 hex)"
address = "10.20.0.12:7443"
credential_serial = 2

Admitting a new node

New nodes are not trusted by CA validity alone. An operator on an authority-bearing machine issues a short-lived grant bound to that exact boot, certificate fingerprint and endpoint:

macrun run crisptastic authority -- \
  crisptastic fabric issue-join \
    --cluster-id 1111.... \
    --boot-id dddd.... \
    --credential-serial 4 \
    --endpoint quic://10.20.0.14:7443 \
    --certificate-der node-d.der \
    --request-id 3333.... \
  | macrun set crisptastic joiner CRISPTASTIC_FABRIC_JOIN_GRANT --stdin

The grant defaults to a 10-minute lifetime. The joining node presents it once, is added to the roster through consensus, then reconnects as a normal trusted member and catches up as a non-voting replica before being promoted.

Deploying to a Fabric

crisptastic deploy . \
  --recipient age1... \
  --signing-key /run/credentials/release.pk8 \
  --out ./site.capsule.age \
  --publish-s3 \
  --peer deploy.internal.example:7443 \
  --client-cert ./operator.crt --client-key ./operator.key \
  --server-ca ./management-ca.crt \
  --desired-replicas 3 --minimum-ready-replicas 2

The response reports preparing. Routing appears only after the assigned replicas independently verify the exact Capsule digest, byte length, domains and secret-name set, and activation commits through Fabric consensus. By default the stable application ID is derived from the Capsule's domain set; --application-id pins it when a site must change its whole domain set without becoming a new application.

Reading cluster state

crisptastic fabric status --peer <host:port> \
  --client-cert ... --client-key ... --server-ca ...

Returns secret-free membership, placement and readiness from the mTLS management endpoint.

Invariants you can rely on

  • A domain resolves to at most one active application per cluster epoch.
  • A replica serves only the exact Capsule digest and secret generation it advertises, and is not routable before local validation and health pass.
  • Losing one node does not interrupt a site whose availability policy was satisfied just before the loss.
  • Session correctness never depends on routing a client back to the same node.
  • A node that cannot prove safe state reports not-ready rather than serving a stale generation.