Skip to content

Operations: local fixture

Status: stable for fixture work. Production admission is gated; see reference/fixture-vs-production.md.

This page covers running the fixture cloud locally beyond a one-shot demo: storage profiles, restart persistence, smoke trials, and common patterns.

See getting-started.md for the minute-by-minute walk. The TL;DR:

Terminal window
cargo run -p gestalt-cli -- cloud serve-fixture --addr 127.0.0.1:3011

In another terminal:

Terminal window
curl -s http://127.0.0.1:3011/health
curl -s http://127.0.0.1:3011/v1/tenant/self \
-H "Authorization: Bearer fixture-session-token"

The gestalt-cloud crate has its own binary you can run without the CLI wrapper:

Terminal window
cargo run -p gestalt-cloud -- --fixture --addr 127.0.0.1:3011

Flags:

--fixture fixture mode (no production admission)
--no-root-key disable root signing; require hosted operator delegate
--storage-profile <profile> surreal-memory | surreal-remote
--addr <ip:port> bind address

The default. State lives in process memory and is lost on restart. Useful for quick demos and unit tests.

Connects to a SurrealDB instance and persists state across restarts. Required for commit.recent, m7.state, and other staging-durable operations to retain data.

Set the environment variables (you can also write them to /etc/gestalt-fixture.env for systemd; locally export them in your shell):

Terminal window
export GESTALT_STORAGE_PROFILE=surreal-remote
export GESTALT_SURREAL_REMOTE_ENDPOINT=ws://127.0.0.1:8000
export GESTALT_SURREAL_REMOTE_NAMESPACE=gestalt_aion_cloud
export GESTALT_SURREAL_REMOTE_DATABASE=staging
export GESTALT_SURREAL_REMOTE_TLS_REQUIRED=false
export GESTALT_SURREAL_REMOTE_USERNAME=root
export GESTALT_SURREAL_REMOTE_PASSWORD=root

Run a disposable local SurrealDB:

Terminal window
surreal start --user root --pass root --bind 127.0.0.1:8000 memory

Then start the cloud:

Terminal window
cargo run -p gestalt-cloud -- \
--fixture \
--no-root-key \
--storage-profile surreal-remote \
--addr 127.0.0.1:3011

--no-root-key requires a durable hosted-operator grant to be present; the local trial scripts seed one.

The deploy/hetzner-cloudflare/ folder contains end-to-end trial scripts that you can run locally. They exercise the membrane against a real (local) cloud + SurrealDB and produce proof bundles under target/.

Terminal window
deploy/hetzner-cloudflare/cloud-authority-trial.sh
deploy/hetzner-cloudflare/gestalt-worldline-trial.sh
deploy/hetzner-cloudflare/m7-durable-human-auth-trial.sh
deploy/hetzner-cloudflare/m9-connector-evidence-trial.sh
deploy/hetzner-cloudflare/m10-m11-effects-economic-trial.sh
deploy/hetzner-cloudflare/m12-worldline-runtime-trial.sh
deploy/hetzner-cloudflare/m13-identity-delegation-key-trial.sh
deploy/hetzner-cloudflare/m14-capability-policy-trial.sh

Each script:

  • starts (or re-uses) a local cloud + SurrealDB,
  • runs the milestone-specific scenario through the membrane,
  • writes a proof bundle to target/gestalt-m<N>-trial/.

Set GESTALT_BASE_URL to point at a different cloud (default http://127.0.0.1:3011).

To prove that a staging-durable operation actually persists across restarts:

Terminal window
# 1. start cloud + surreal as above
# 2. commit something durable
curl -X POST http://127.0.0.1:3011/v1/commits/recent ...
# 3. stop the cloud
# 4. start it again
cargo run -p gestalt-cloud -- --fixture --no-root-key --storage-profile surreal-remote --addr 127.0.0.1:3011
# 5. verify the prior commit is still there
curl -s http://127.0.0.1:3011/v1/commits/recent \
-H "Authorization: Bearer fixture-session-token"

The M-trial scripts automate this as part of their proof bundles.

The fixture cloud logs to stdout in fixture-shaped JSON. Treat logs as observability, not as receipts. The receipts (returned in MembraneResponse.receipt) are the audit-bearing artifacts.

fixture-session-token valid primary
fixture-session-token-secondary valid alternate
expired-fixture-session-token refuses with session_expired
wrong-scope-fixture-session-token refuses with scope_mismatch
tampered-fixture-session-token refuses with token_tampered

These exist so you can deliberately exercise auth refusal paths.

  • Port already in use. Try a different --addr.
  • --no-root-key refuses commits. The world needs a hosted operator grant first; run a trial script that seeds one.
  • Surreal connection refused. Make sure surreal start is running on the configured endpoint.
  • Receipts have fixture: true. This is correct. Always.