Guide: tenant provisioning
Status: capability-state mix. Tenant create, company bootstrap, and onboarding gate are staging-durable. Key-custody attestation and rotation rehearsal walk end-to-end as fixture / staging records. Production admission precheck and policy admit the records but every gate refuses production admission. Real HSM/KMS provider attestation and real production tenant onboarding are not yet substantive — see 022 gap report items 1, 2, and 6.
This guide walks the path of provisioning a new tenant from “nothing” up to “ready to admit work.” Every gate exists to refuse, not to grant.
The provisioning posture
Section titled “The provisioning posture”A tenant is the legal container under which company truth is admitted, audited, and preserved. Provisioning has three moving parts: identity custody (the keys that sign atoms, and the attestation those keys live in a real HSM/KMS); company bootstrap (the registered legal entity); and production admission (the explicit, multi-evidence, signature-gated decision to let real customer data cross the membrane). Every fixture endpoint refuses production admission — the shape of the gates is fully expressible today; the substance behind them is M22+ work.
The flow
Section titled “The flow”client.keyCustodyReadiness— survey what’s already in place.client.keyCustodyAttest— admit a key-custody attestation.client.keyCustodyProviderAttest— bind the key to a named HSM/KMS provider.client.tenantCreate— create the tenant atom.client.companyBootstrap— bind the legal entity.client.tenantOnboardingGate— fold the receipts together.client.productionAdmissionPrecheck— gather the evidence bundle.client.productionAdmissionPolicy— declare the policy. (Will refuseproduction_admission_enabled: trueagainst a fixture posture.)
Steps 1-3 — key custody
Section titled “Steps 1-3 — key custody”Survey, attest, and bind to a provider. The keyCustodyReadiness
GET surveys the tenant’s custody posture; the Koerper should
render its body as a checklist banner before any provisioning
attempt:
const tenant = "tenant_node:rheinwerk_calibration";
const readiness = await client.keyCustodyReadiness();// body.productionKmsHsmReady, .tenantKeyCustodyProviderConfigured,// .privateKeyMaterialExposed — all false on fixture
const attestation = await client.keyCustodyAttest({ tenant, company: "company_geist:rheinwerk_calibration", attestation_ref: "key_custody_attestation:rheinwerk_2026_04", mode: "fixture_staging", provider: "fixture_provider", key_ref: "signing_key:rheinwerk_root", public_key: "ed25519:fixture_public_key_base64", attestation_hash: "sha256:fixture_attestation_hash", fixture: true, production_requested: false,});
const provider = await client.keyCustodyProviderAttest({ tenant, company: "company_geist:rheinwerk_calibration", provider_attestation_ref: "key_custody_provider_attestation:rheinwerk_aws_kms", mode: "fixture_staging", provider: "aws_kms", key_ref: "signing_key:rheinwerk_root", public_key: "ed25519:fixture_public_key_base64", public_key_hash: "sha256:fixture_public_key_hash", attestation_hash: "sha256:fixture_provider_attestation_hash", fixture: true,});Setting production_requested: true against a fixture provider
refuses with key_custody_production_admission_blocked. Setting
private_key_pem or private_key_material on any call in this
family refuses unconditionally. The provider field is enum-checked
but the actual KMS attestation document is not validated today;
that validation is the M22+ substance work.
Steps 4-6 — tenant, company, onboarding gate
Section titled “Steps 4-6 — tenant, company, onboarding gate”const created = await client.tenantCreate({ tenant, requested_tenant: tenant, host: "rheinwerk.gestalt.local", company: "company_geist:rheinwerk_calibration", authority_epoch: "authority_epoch:rheinwerk_root", home_reality: "reality:record", signing_key: "signing_key:rheinwerk_root", encryption_key: "encryption_key:rheinwerk_root", auth_policy: "auth_policy:default", key_custody_attestation: attestation.body.keyCustodyAttestation, fixture: true,});
const bootstrap = await client.companyBootstrap({ tenant, company: "company_geist:rheinwerk_calibration", legal_name_hash: "sha256:rheinwerk_calibration_gmbh", legal_form: "GmbH", jurisdiction: "DE-BE", register_number_hash: "sha256:hrb_fixture", verification_evidence: ["evidence_bundle:rheinwerk_handelsregister"], fixture: true,});
const gate = await client.tenantOnboardingGate({ tenant, tenant_onboarding: "tenant_onboarding:rheinwerk_2026_04", company_bootstrap: bootstrap.body.companyBootstrap, key_custody_attestation: attestation.body.keyCustodyAttestation, fixture: true,});Without the attestation, tenantCreate refuses with
tenant_create_key_custody_missing. legal_name_hash and
register_number_hash are content commitments — raw
legal_name is accepted on fixture but refused against production
posture without contains_customer_data. The onboarding gate is
the “do these receipts compose?” check; missing any input refuses
with the corresponding tenant_onboarding_* reason.
Step 7 — production admission precheck
Section titled “Step 7 — production admission precheck”const precheck = await client.productionAdmissionPrecheck({ tenant, company: "company_geist:rheinwerk_calibration", precheck_ref: "production_admission_precheck:rheinwerk_2026_04", key_custody_attestation: attestation.body.keyCustodyAttestation, edge_access_policy: "edge_access_policy:rheinwerk_default", authority_package: "authority_package:de_vat_2026", proof_issuer: "proof_issuer:rheinwerk_default", backup_restore_receipt: "ops_restore_rehearsal:rheinwerk_2026_04", hostile_suite_receipt: "staging_maturity_report:rheinwerk_2026_04", request_production: false,});request_production: true against fixture posture refuses with
production_admission_precheck_fixture_evidence.
Step 8 — production admission policy
Section titled “Step 8 — production admission policy”const policy = await client.productionAdmissionPolicy({ tenant, policy_ref: "production_admission_policy:rheinwerk_2026_04", jurisdiction: "DE-BE", vertical: "small_business_invoicing", tenant_type: "operator", admitted_connectors: ["bank_observation", "advisor_review"], admitted_authority_packages: ["authority_package:de_vat_2026"], admitted_effects: ["send_invoice_email"], forbidden_data_classes: ["raw_biometric_material"], admission_signer: "human_person:gestalt_root_signer", admission_signer_commitment: "sha256:fixture_admission_signature", production_admission_enabled: false,});production_admission_enabled: true refuses against fixture
posture. The runtime will not honor production admission until
M22+ ships the substantive overlay.
Rotation, rehearsal, and break-glass
Section titled “Rotation, rehearsal, and break-glass”Provisioning is not done when the tenant is created. The four
rehearsal endpoints — keyCustodyRotationRehearse,
keyCustodySigningRehearse, keyCustodyRevoke,
keyCustodyBreakGlass — each admit an audit-citable record of the
custody story. The signing rehearsal admits the intent to sign
against a fixture payload (production_payload: false); the
break-glass call requires both a human_presence_receipt for the
approver and an opaque justification_hash.
await client.keyCustodyRotationRehearse({ tenant, company: "company_geist:rheinwerk_calibration", provider_attestation: provider.body.providerAttestation, key_ref: "signing_key:rheinwerk_root", replacement_key_ref: "signing_key:rheinwerk_2026_04", replacement_public_key: "ed25519:fixture_replacement_public_key", rotation_reason: "scheduled_quarterly_rotation", fixture: true,});
await client.keyCustodyBreakGlass({ tenant, company: "company_geist:rheinwerk_calibration", provider_attestation: provider.body.providerAttestation, key_ref: "signing_key:rheinwerk_root", operator: "human_person:gestalt_oncall", justification_hash: "sha256:fixture_break_glass_justification", approver_receipt: "human_presence_receipt:fixture_break_glass_presence", fixture: true,});Maturity report and pilot gate
Section titled “Maturity report and pilot gate”Before requesting production admission, fold the rehearsal record into a maturity report (trial count, pass count, exposure invariants) and a pilot gate (consent, data-boundary, support runbook, presence):
await client.stagingMaturityReport({ tenant, report_ref: "staging_maturity_report:rheinwerk_2026_04", run_slug: "rheinwerk-2026-04", report_commitment: "sha256:fixture_maturity_report", required_trials: ["invoice_payment_advisor", "human_auth", "key_custody"], trial_count: 3, pass_count: 3, proof_verification_count: 3, proof_verification_pass_count: 3, gate_state: "ready", production_admission_enabled: false, raw_biometric_material_exposed: false, private_key_material_exposed: false,});
await client.pilotAdmissionGate({ tenant, company: "company_geist:rheinwerk_calibration", pilot_ref: "pilot:rheinwerk_2026_04", customer_consent_hash: "sha256:fixture_pilot_consent", data_boundary_hash: "sha256:fixture_pilot_data_boundary", support_runbook_hash: "sha256:fixture_pilot_support_runbook", production_precheck: precheck.body.productionAdmissionPrecheck, tenant_onboarding_gate: gate.body.tenantOnboardingGate, human_presence_receipt: "human_presence_receipt:fixture_pilot_presence", no_public_launch_claim: true, customer_data_live: false,});What this proves
Section titled “What this proves”- Every gate is structurally a record. Missing any input refuses with a structured reason a Koerper can render.
- Custody, identity, and production admission are three separate decisions, not one — each has its own receipt and can be audited independently.
- The maturity report and pilot gate make “we are ready” a signed, citable atom rather than a slack message.
What does not work yet
Section titled “What does not work yet”- Real HSM/KMS provider attestation.
keyCustodyProviderAttestwalks the shape but does not cryptographically validate a real KMS attestation document. The provider enum is honored; the underlying attestation chain is fixture. - Real tenant production onboarding. Every gate refuses
production admission.
production_admission_enabled: true,production_requested: true,request_production: trueall refuse. No tenant in fixture-preview has production substance. - Real signing rehearsals.
keyCustodySigningRehearseadmits the intent to sign; it does not actually exercise the KMS signing path against a real payload.
See 022 items 1, 2, and 6.
Where to read next
Section titled “Where to read next”- API: admission and key custody
- API: observability and misc —
tenant.create,company.bootstrap - Guide: standing and mandates — granting authority inside the new tenant
- Guide: publishing an authority package — landing the legal/operational rules a tenant operates under