Deploying to a host

The path from a folder on your laptop to a running site on a host you control.

A host is a running crisptastic host process that serves sealed Capsules, selects a site by domain, and injects secrets into memory. Deploying means: build a Capsule, put it where the host can read it, and tell the host to activate it.

The host can be another port on your laptop, a machine on your LAN, or a production box — the commands are identical. Only the addresses and which certificates you hold change.

Keys, once

# Age X25519 recipient: the host's identity. Capsules are encrypted TO it.
age-keygen -o server.agekey            # keep secret on the host

# Ed25519 deployment signer: proves who built a Capsule.
crisptastic capsule keygen --private release.pk8 --public release.pub
# keep release.pk8 with whoever deploys; give release.pub to every host

Build and seal

crisptastic deploy ./my-site \
  --recipient age1xxxxxxxx... \
  --signing-key ./release.pk8 \
  --out ../capsules/my-site.age

deploy runs the deterministic build, moves the values of your declared [env] / [env_from_process] names into a separate encrypted section, signs the payload, and encrypts the whole Capsule to the host's recipient. The output is opaque ciphertext; its SHA-256 digest is its identity.

Get the Capsule to the host

Copied file — point the host straight at the .age file.

Object storage — publish once; hosts pull by digest. Publishing is idempotent because the key is the digest.

crisptastic capsule publish ../capsules/my-site.age --store-dir ../capsule-store
# -> published capsule digest <sha256>

# or S3-compatible storage (Capsule stays ciphertext):
export CRISPTASTIC_CAPSULE_S3_BUCKET=my-capsules
export CRISPTASTIC_CAPSULE_S3_REGION=eu-west-1
export CRISPTASTIC_CAPSULE_S3_ACCESS_KEY=...
export CRISPTASTIC_CAPSULE_S3_SECRET_KEY=...
crisptastic capsule publish ../capsules/my-site.age --s3

# deploy + publish in one step:
crisptastic deploy ./my-site --recipient age1... --signing-key ./release.pk8 \
  --out ../capsules/my-site.age --publish-s3

Run a host and activate

Start a host with a management listener (mutual TLS). Generate development certificates with lab/scripts/gen-mtls-certs.sh, or use your own PKI.

crisptastic host \
  --identity ./server.agekey \
  --trusted-signer ./release.pub \
  --host-root /var/lib/crisptastic \
  --deployments-root /var/lib/crisptastic/deployments \
  --store-dir /var/lib/crisptastic/capsule-store \
  --bind 127.0.0.1:6174 \
  --management-bind 127.0.0.1:7443 \
  --management-cert ./certs/server.crt \
  --management-key ./certs/server.key \
  --management-client-ca ./certs/client-ca.crt \
  --development-process-profile          # laptop only; omit in production

Then, from wherever you deploy from:

crisptastic notify \
  --peer 127.0.0.1:7443 \
  --digest <sha256> \
  --client-cert ./certs/client.crt \
  --client-key ./certs/client.key \
  --server-ca ./certs/server.crt

notify calls POST /v1/deployments/activate over mTLS. The host pulls the Capsule (if using a store), verifies digest, byte length, signature, schema, domains and secret-name set, unpacks public files atomically under a fresh deployment UUID, moves secrets into a tenant-scoped memory generation, and makes the domains routable only when everything is ready. A client without a trusted certificate is rejected and the running routes are left untouched.

Static digests instead of a management call

A host can also start with fixed Capsules or digests — useful for immutable, GitOps-style deploys:

crisptastic host ../capsules/a.age ../capsules/b.age --identity ... --trusted-signer ...
crisptastic host --digest <sha256-a> --digest <sha256-b> --store-dir ... --identity ... --trusted-signer ...

Redeploying and rollback

Re-activating a domain builds the new runtime fully, swaps the routing pointer under a write lock, and drops the retired runtime after in-flight requests finish (its secrets zeroize). To roll back, activate the previous Capsule digest — it is still in the store. An unknown or unregistered domain always fails closed with HTTP 421.

Same laptop, second port

To rehearse the whole flow locally: run a host on :6174 / :7443 as above with --development-process-profile, deploy your site to a --store-dir, and notify 127.0.0.1:7443. A separate computer is the same, with its LAN address in --peer and matching certificate SANs.