Skip to content

Guide: human-auth flows

Status: shape-only for cryptographic real-WebAuthn; otherwise fixture-rehearsed. See 022 gap report item 15.

This guide walks the two human-presence lanes Gestalt supports: private passkey and CPU face-match fallback. Both produce a human presence receipt without Gestalt ever seeing biometric material.

Gestalt distinguishes:

  • Identity — who someone is in the world.
  • Standing — what they may bind on behalf of a company.
  • Presence — that they were here, now, attesting in person.

A human presence receipt is not standing. It does not authorize anything by itself. It is consumed by other operations that require it (sensitive approvals, sensitive effect intents).

This separation is constitutional. A clever attacker who steals a session token cannot upgrade themselves to “the human is here right now.” Presence is its own thing.

const challenge = await client.humanAuthChallenge({
tenant: "tenant_node:rheinwerk_calibration",
subject: "human_person:anna",
relying_party_id: "gestalt.local",
scopes: ["sensitive_approval"],
});
console.log(challenge.body.challenge);
// "human_auth_challenge:fixture_private_human_auth"
console.log(challenge.body.biometricMaterialSeenByGestalt); // false

The challenge is consumed by the user’s authenticator (WebAuthn-style). The browser / native vessel performs the passkey ceremony locally; biometric material never leaves the device.

After the user’s authenticator signs the challenge:

const verified = await client.humanAuthVerifyPasskey({
tenant: "tenant_node:rheinwerk_calibration",
challenge: challenge.body.challenge,
passkey_binding: "passkey_binding:anna_platform_passkey_fixture",
relying_party_id: "gestalt.local",
user_verified: true,
credential_id_hash: "sha256:fixture_credential_id_hash_private",
});
console.log(verified.body.humanPresenceReceipt);
// "human_presence_receipt:fixture_private_presence"
console.log(verified.body.standingCreated); // false
console.log(verified.body.biometricMaterialSeenByGestalt); // false

standingCreated: false and biometricMaterialSeenByGestalt: false are asserted explicitly — they are part of the privacy contract.

The humanPresenceReceipt is now usable for follow-on operations.

For environments without passkey support. Requires explicit consent.

const fallback = await client.humanAuthFaceMatch({
tenant: "tenant_node:rheinwerk_calibration",
subject: "human_person:anna",
consent_ref: "consent:fixture_cpu_face_match",
scenario: "same-person", // | "different-person" | "low-quality" | "spoof"
});
console.log(fallback.body.cpuFaceMatchReceipt);
console.log(fallback.body.standingCreated); // false
console.log(fallback.body.rawImagesStored); // false
console.log(fallback.body.biometricTemplatesStored); // false
console.log(fallback.body.oneToManySearch); // false

The body asserts the privacy invariants explicitly:

  • rawImagesStored: false — no raw face images persist.
  • biometricTemplatesStored: false — no template storage.
  • oneToManySearch: false — never used for 1:N search.
  • standingCreated: false — presence does not create standing.

Without consent_ref, the call refuses with human_auth_face_match_consent_missing. Consent is structurally required.

A presence receipt is consumed by:

Bind presence to actor + vessel for sensitive approval:

const approval = await client.m13PresenceApproval({
tenant: "tenant_node:rheinwerk_calibration",
actor: "human_person:anna",
vessel: "vessel:fixture_sdk",
human_presence_receipt: "human_presence_receipt:fixture_private_presence",
create_standing_from_presence: false, // explicit; presence cannot create standing
});
console.log(approval.body.sensitiveApprovalSatisfied); // true
console.log(approval.body.standingCreated); // false
console.log(approval.body.delegationAuthorityCreated); // false

If you set create_standing_from_presence: true, the call refuses with m13_human_presence_cannot_create_standing. This refusal is load-bearing — it prevents an attacker who phished a passkey ceremony from upgrading themselves to a Geschäftsführer.

const intent = await client.effectIntent({
tenant: "tenant_node:rheinwerk_calibration",
subject: "company_geist:rheinwerk_calibration",
effect_kind: "send_invoice_email",
adapter: "fixture_email_adapter",
mode: "fire_and_record",
idempotency_key: "invoice-2026-04-001-email",
evidence: ["evidence_bundle:invoice_payload"],
human_presence_receipt: "human_presence_receipt:fixture_private_presence",
});

Without the presence receipt, sensitive effect intents refuse with effect_human_presence_required.

// the same challenge cannot be re-used
const replay = await client.humanAuthVerifyPasskey({
...same args as before...
});
// outcome: "refused"
// body.refusalReason: "human_auth_challenge_replayed"

Challenges expire after a short window. Expired challenges refuse with human_auth_challenge_expired.

Building a Koerper that does presence correctly

Section titled “Building a Koerper that does presence correctly”

A bearer session token proves who the API caller is. A presence receipt proves that a human was physically here recently. The two are different. Don’t downgrade one into the other.

A presence receipt is a one-shot artifact. It is consumed by a specific follow-on operation. Don’t store it for re-use.

Every presence-flow UI should display:

  • “Gestalt did not see your face / fingerprint / biometric.”
  • “This presence verification does not change your role.”

These statements are backed by the response fields (biometricMaterialSeenByGestalt, standingCreated, rawImagesStored).

4. Make sensitive flows require fresh presence

Section titled “4. Make sensitive flows require fresh presence”

If your Koerper wraps a sensitive effect (a refund, a sensitive data export, a contract signature), require a fresh presence flow each time. Don’t reuse presence across multiple sensitive acts.

  • Real WebAuthn validation. Passkey verify uses fixture flags rather than actually validating an assertion against a registered credential.
  • Real face-match. The face-match fallback returns a receipt based on the scenario enum, not a real biometric pipeline.
  • Real session-binding. Presence cannot today be bound to a specific cloud-issued session in production.

For the gap, see 022 item 15.