Skip to content

Fixture mode

Status: stable. Fixture mode is supported in the TypeScript SDK via createFixtureFetch() and in the Rust SDK by construction (GestaltClient::fixture()).

Fixture mode lets you exercise the Gestalt SDK without running the cloud. Calls return shape-correct, deterministic responses. No network. No durable state. No real consequence.

It is useful for:

  • Unit tests that exercise your Koerper’s calls without standing up infrastructure.
  • Offline development when the cloud is not reachable.
  • CI environments where booting the cloud would slow the build.
  • Reproducible demos where you need the same response every time.
  • SDK shape verification — confirming your code consumes the membrane’s response shape correctly.

It is not useful for:

  • Verifying that the cloud accepts your envelopes (use a real cloud in fixture mode for that).
  • Proving anything about real consequence (everything is fixture, always).
  • Performance testing (fixture mode is fast for the wrong reasons).

Pass it as the fetchImpl option:

import { GestaltClient, createFixtureFetch } from "@gestalt/sdk";
const client = new GestaltClient({
baseUrl: "http://fixture",
fetchImpl: createFixtureFetch(),
});
const tenant = await client.tenantSelf();
// {
// operation: "tenant.self",
// outcome: "verified",
// body: { tenant: "tenant_node:rheinwerk_calibration", productionAdmission: false },
// receipt: { ref: "receipt:fixture_tenant_self", outcome: "verified", fixture: true, ... }
// }

What createFixtureFetch() returns for each route

Section titled “What createFixtureFetch() returns for each route”

Every fixture response carries fixture: true on the receipt and productionAdmission: false (or equivalent) on the body. A few illustrative shapes:

RouteOutcomeBody shape
GET /v1/tenant/selfverified{ tenant, productionAdmission: false }
POST /v1/intents/precheckrefused{ failedGate: "required_evidence_missing", missingEvidence: ["invoice_payload"] }
POST /v1/shop/prepareadmitted{ prepareToken: "fixture-prepare-token" }
POST /v1/shop/commitadmitted{ signer: { kind: "hosted_operator_delegate" }, durableCommit: { status: "fixture" } }
POST /v1/proofs/requestrefused{ failedGate: "missing_entitlement" }
POST /v1/proofs/bundleverified{ bundle: { id: "proof_bundle:m6_...", productionAdmission: false } }
POST /v1/authority/packages/*admitted{ manifestHash: "sha256:fixture_manifest", sourceHash: "sha256:fixture_source", selfActivation: "refused", productionAdmission: false }
POST /v1/reality/forkprojected{ productionAdmission: false }
POST /v1/reality/{commit|diff|promote|discard}admitted{ productionAdmission: false }
POST /v1/zeitgestalt/queryverified{ receiptBacked: true }
POST /v1/human-auth/challengeverified{ challenge: "human_auth_challenge:...", biometricMaterialSeenByGestalt: false }
POST /v1/human-auth/passkey/verifyadmitted{ humanPresenceReceipt: "...", standingCreated: false, biometricMaterialSeenByGestalt: false }
POST /v1/effects/intentqueued{ effectIntent, effectOutbox, effectExecutionClaimed: false, rawAdapterPayloadExposed: false }
POST /v1/effects/dispatchexecuted{ effectAttempt, effectReceipt, externalActDuplicated: false }
POST /v1/economy/invoiceadmitted{ invoice, receivableObligation, closureSurface, rawConnectorPayloadExposed: false }
POST /v1/economy/period-closeadmitted{ periodClose, productionAdmission: false }

Routes not listed return 404 fixture route not found.

The Rust SDK is fixture-only by construction:

use gestalt_sdk::GestaltClient;
let client = GestaltClient::fixture();

It returns serde_json::Value shapes that mirror the cloud’s fixture responses. See rust.md for examples.

You can talk to the live cloud in fixture mode (the cloud itself is in fixture mode today):

const client = new GestaltClient({
baseUrl: "http://127.0.0.1:3011",
token: "fixture-session-token",
});

Or you can use the SDK’s built-in fixture fetch with no cloud:

const client = new GestaltClient({
baseUrl: "http://fixture",
fetchImpl: createFixtureFetch(),
});

Both will return responses that match the cloud’s contract shape. The first sees actual cloud-side fixture logic (Gravity gates, route handlers, authority resolution). The second is pure local stubs.

For end-to-end fixture tests, prefer the live cloud. For unit tests, prefer createFixtureFetch().

The fixture cloud accepts these bearer tokens (constants in crates/gestalt-cloud/src/lib.rs):

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 let you write tests that exercise the auth refusal paths.

How fixture mode reflects production discipline

Section titled “How fixture mode reflects production discipline”

Fixture mode is not a relaxation of the membrane. It is a shape-faithful rehearsal:

  • Refusals are emitted with structured fields exactly like production refusals will be.
  • Receipts carry the fixture: true marker so they cannot be confused with real receipts.
  • productionAdmission: false is asserted in every response body so no Koerper can accidentally rely on a fixture as if it were authentic.
  • Operations that close runtime_owner: cloud_geist boundaries (key custody, package activation) refuse self-activation, refuse unscoped disclosure, refuse cross-tenant access — all in fixture shape.

A Koerper built against fixture mode that handles refusals, projections, pending actions, and closure surfaces correctly will work the day the cloud becomes authentic.

If you build a Koerper that uses fixture mode:

  1. Don’t strip the fixture: true marker. Anywhere a receipt is surfaced, the marker should be visible.
  2. Don’t display fixture data as real. Mark fixture-derived UI states clearly.
  3. Don’t fork production logic on fixture status. A Koerper should call the membrane the same way; the cloud’s posture is the cloud’s responsibility.
  4. Don’t extend the fixture vocabulary. If a refusal code or capability ref is missing, file an issue — don’t invent it client-side.