Skip to main content

Identity & the platform envelope

Kamiwaza owns browser authentication. Kaizen consumes a validated forwarded identity envelope and does not maintain a second browser session, a second user directory, or a second password.

Browser-session lifecycle​

An unauthenticated Kaizen URL hands the browser to Kamiwaza sign-in with a validated return target. The target must share Kaizen's configured public origin and remain inside its application base path; external and sibling-app targets fall back to the Kaizen root. This keeps deep links working under /runtime/apps/<name> without creating an open redirect.

Manual sign-out and Kaizen inactivity expiry use one lifecycle path: authenticated UI disappears first, member-scoped browser caches are cleared, and Kamiwaza's server-side and front-channel logout complete the browser session. The logout signal is synchronized across tabs on the same origin. Kaizen stores only timing and lifecycle signals for this coordination, never tokens or identity claims.

That synchronization applies only to tabs running the same Kaizen 0.4 browser contract. The identity migration deliberately does not retain the previous browser-event namespace. Operators must have members close pre-0.4 tabs and reopen Kaizen from its current App Garden entry before 0.4 traffic is enabled; the retired application route prevents an old bundle from reaching Product Kaizen, while closing the tab removes member data it had already rendered.

MEMBER_SESSION_IDLE_TIMEOUT_MINUTES is an optional browser-side inactivity policy. Its default is None, so Kaizen runs no timer and Kamiwaza alone owns session expiry. When configured, the SPA warning and logout flow can end the visible Kaizen session earlier, but the backend does not reject an otherwise valid platform session at that deadline. A modified client can ignore this UI timer. Background polling and streams do not count as member activity. Any platform 401 remains authoritative and immediately removes the authenticated surface.

Preserving a deep link sends its full Kaizen URL to Kaizen's login endpoint as a return_to value. Kaizen validates it, then hands the accepted target to Kamiwaza through the platform's redirect_uri contract. That value may appear in platform access logs, so Kaizen URLs must not carry secrets. External origins and paths outside the Kaizen base path remain rejected. A portless trusted app root may inherit the exact browser-facing port preserved by Kaizen's immediate web proxy, but only when that authority's hostname matches the configured hostname. Explicit configured ports remain authoritative, and an authority naming any other hostname never becomes redirect authority — the configured hostname is what bounds this inheritance, not the header the value arrived in. Kaizen's web proxy carries that authority as X-Forwarded-Host, because Host addresses the proxied hop itself and a service mesh routes on it; it sets the header unconditionally, so a caller cannot supply its own value through the proxy. Deployments whose ingress rewrites the browser authority before Kaizen sees it should configure the public port explicitly. Kaizen synthesizes its validation root from the configured hostname text and borrows only the validated request port; an accepted exact browser deep link remains otherwise unchanged.

What Kaizen accepts​

The platform forwards identity headers, which Kaizen extracts into a principal:

FieldSourceRole
subx-user-idStable Kamiwaza user id
emailx-user-emailThe document-ACL principal, lowercased
display_namex-user-nameSafe display only
platform_rolesx-user-rolesRole checks, normalized to lowercase
platform_workroom_idenvelopeWorkroom scoping
auth headersenvelopeForwarded to the platform on the member's behalf

Where the platform supplies claims rather than headers, roles are read from realm_access.roles and normalized the same way.

What Kaizen forwards for model discovery and inference​

Behind sealed Envoy routing, model discovery uses the platform-issued X-User-Model-Discovery-Token capability. Kaizen sends it only to GET /api/serving/extension-models on the configured platform API (KAMIWAZA_API_URL). Certificate verification follows the platform TLS configuration; redirects are refused. Core encrypts this opaque capability, limits it to discovery and expires it after 30 seconds. Core revalidates the original caller's authentication and read policy when it is used. Kaizen cannot decrypt it or mint one with its deployment signing key.

The capability remains within the current request. Kaizen neither persists it nor sends it to inference. Refused or malformed discovery responses report the provider as unavailable rather than pretending the member has no models.

When no discovery capability is present, legacy model discovery and inference carry transport only: the bearer or session credential (authorization, x-auth-token, or the access_token cookie — whichever arrived is kept, and an Authorization header is derived from it when the envelope did not already carry one) plus the x-request-id correlation id. Nothing that asserts who the caller is travels with it — not x-user-id, not x-user-email, not x-user-roles, and not the workroom assertion.

Excluding the workroom is deliberate rather than incidental. Kamiwaza's runtime model routes are not workroom-scoped: the invoke decision resolves without a workroom at all, so the header can never widen what a member may reach. It can only narrow it. An explicit workroom header with no verified session binding behind it makes the platform fail closed, and a detached turn samples the model long after the originating binding is gone — so replaying the assertion denies exactly the calls that should succeed.

Kaizen holds the member's credential server-side for the life of the turn and re-presents it; it never mints one, and it never re-signs an identity claim the platform issued.

What Kaizen refuses​

Identity is never taken from:

  • a request body,
  • a query parameter,
  • a chat tool argument,
  • a queue message,
  • a stored snapshot,
  • model output.

There is exactly one construction chokepoint for a principal. Member runtime modules never manufacture an identity from request or room data — a queued turn rehydrates its actor from a server-authored snapshot created when the input was accepted, and then re-authorizes it before running.

Linked federation identities​

A Kamiwaza shared_idp federation admits a cross-cluster call only with a token from the federation's shared identity realm. Kaizen holds such a token as an outbound credential keyed by the member, never as a second identity: the forwarded envelope still decides who the member is, which Kaizen data they reach, and which tools they get.

  • Linking is explicit. The member signs in to the shared realm and approves an OAuth device grant in their own browser. Kaizen never asks for or sees the password, and the model only relays the sign-in link and code. Kaizen records which realm identity approved (linked_subject and linked_username on oauth.consent.granted), so an operator can tie the receiving cluster's records to the member and notice an approval made with another account.
  • Storage. The issuer's offline refresh token is field-encrypted with the deployment field key in the per-member OAuth token store, one row per member and shared realm, written only through the explicit-consent service. When the realm rotates refresh tokens, each replacement is stored the same way, and a member's refreshes run one at a time so a concurrent call never replays a rotated token (within one api-backend replica).
  • Use. A federated request mints a short-lived access token and sends it to Core's mesh proxy as the only credential on that call. The member's envelope is not attached, and the token never reaches the model, the transcript, the audit summary or the sandbox.
  • Audience. The receiving cluster filters what it returns for the linked identity, so the federation tools run only in a chat whose sole participant is the member and are withheld in a shared chat, where another participant would see results their own identity may not.
  • Revocation. If the issuer refuses the stored token (invalid_grant), Kaizen deletes it and the member must link again. The member can withdraw Kaizen's access from their own account in the shared realm, a realm administrator can revoke the member's offline session, and the receiving cluster can withdraw the member's onboarding; any of these stops further calls. Kaizen caches the short-lived access token until shortly before it expires, and only for the stored link that minted it, so relinking takes effect on the next call and a revocation at the realm takes effect within one access-token lifetime (the realm's access token lifespan, five minutes by default).
  • Audit. Granting a link writes oauth.consent.granted, a revoked link writes oauth.refresh.revoked, and every federated_read or federated_write call writes chat.tool.federated_request, including the calls Kaizen refused before sending and a call that got no answer and may still have run (see the audit reference).

Residual risks. An offline token is a long-lived credential for the member's shared-realm identity, held encrypted in Kaizen's database. A reader of both the database and the field key could call the platform's mesh proxy as that identity until the token is revoked or idles out under the realm's offline policy. The receiving cluster's own onboarding and gates still bound what that identity can do.

A member's circle is readable by their co-members​

A member's uploaded circle picture is the one piece of member-authored content Kaizen serves to any authenticated member who asks for it by owner subject, rather than to an audience derived from a room. That is the point of a circle: it is drawn beside a name in rooms the reader is already in, and gating each picture on a shared-room lookup would add a per-circle authorization query to every roster render without protecting anything. The roster already hands a co-member the verified email, which is strictly more identifying than an opaque platform subject.

Two properties keep that from being an enumeration surface. The write derives its owner only from the forwarded envelope, so no request can set or replace another member's picture. And the read answers one identical 404 — same status, same body — for a member with no picture and for a subject that names nobody, so the route cannot distinguish an account that exists from one that does not.

The bytes served are never the bytes uploaded. The server decodes the upload, center-crops it, and re-encodes it to WebP, which discards the metadata a camera or phone wrote into the original — including recorded coordinates — so a holiday photograph cannot carry a member's location onto a roster.

Email is the ACL principal​

Kaizen's document ACL is email-based. That has a hard consequence: an authenticated platform identity without an email cannot be mapped safely, so Kaizen fails closed and emits an access-denied audit event with the reason kamiwaza_identity_missing_email.

A missing envelope entirely produces missing_kamiwaza_identity_envelope and an unauthenticated error. An ambiguous raw envelope, such as repeated authorization or identity fields, produces invalid_kamiwaza_identity_envelope and the same error instead of choosing one value. Repeated x-request-id tracing metadata is omitted instead: it never establishes authority, and Kaizen does not choose one ambiguous correlation id. None of these paths guesses.

Administrative authority​

Two trusted paths grant Kaizen administrator authority.

  • Platform Admin comes from the trusted platform admin role. It permits the complete Kaizen operator surface plus bounded, read-only platform inventory.
  • Kaizen App Admin comes from owner in the forwarded current-workroom role. It permits /api/ops/* configuration of that Kaizen deployment without granting platform inventory.

Frontend visibility is advisory; every endpoint rechecks the effective authority and fails closed. Neither grant permits private member-content access.

Identity in a shared room​

A shared conversation has one journal but many possible actors. Kaizen keeps three identities separate:

  • Collaboration scope — who is currently a member, at which audience version;
  • Conversation stream — the room's order, lane, and shared context;
  • Actor scope — whose credentials, connectors, approvals, and sandbox a given turn runs under.

A turn always runs as its initiating participant. Membership never intersects privileges across the room, and no turn borrows the owner's authority.

Identity in unattended work​

A scheduled task has no browser and no live session, so it cannot present a member's credential. Kaizen does not paper over that.

Task ownership comes only from the validated envelope at creation time and is stored as a non-transferable server-owned actor binding plus safe display metadata. Activation creates a Core consent intent from that authenticated session. Approval produces an automation grant bound to the member sub, the registered Kaizen client and role, the exact task revision, resources, limits, policy, and expiry. Kaizen retains only its opaque grant reference.

At execution, Core keeps two principals distinct:

  • sub is the member whose current authority, charge ownership, and consent bound the grant;
  • act is the registered Kaizen workload revision, role, deployment, and instance doing the work.

The API uses the task-control role; background workers use task-executor. Each authenticates with a short-lived projected Kubernetes service-account assertion and a fresh P-256 DPoP proof. The assertion proves only the registered workload. A sender-constrained run or effect capability proves only the closed operation Core authorized for that member-plus-workload pair. Neither can impersonate the member or widen the other.

Core selects the strongest healthy registered attestation profile, preferring online TokenReview and using the registered offline projected-token verifier only when that portable fallback is healthy. Profile changes never change the fixed audience, workload revision, or closed role permissions.

Task rows and queue payloads never store browser bearer tokens, projected assertions, DPoP private keys or proofs, run/effect capabilities, connector secrets, MFA state, or copied role claims. The one exception is a safe test's draft test model grant on a deployment without a platform service identity: the member's bearer, sealed with AES-GCM, bound to that one test run, and cleared when the run spends it. Missing current member authority, workload registration, proof, or Core readiness blocks; no failure path substitutes a snapshot, a local grant, or workload identity alone.

See Task automation security.

Interactive inference with sealed browser sessions​

On Envoy installations, the platform keeps the browser credential sealed. After validating the signed member envelope, Kaizen retains an opaque app admission only in transient input execution context. Before each model request it asks Core for a 30-second credential bound to the selected deployment and operation. Core rechecks the browser session, app deployment, workroom binding, current model identity and model-invoke permissions at exchange and consumption. Revocation or changed authority stops subsequent calls. App admission lasts at most 15 minutes and never beyond the originating session credential; it is not a background task credential.

Operators enable this capability per trusted Kaizen runtime identifier with Core's AUTH_MODEL_INFERENCE_TRUSTED_RUNTIME_TARGETS setting. Set it to the comma-separated identifiers of the admitted app instances. The default is empty. Removing an identifier revokes outstanding admission for that app. This setting does not enable reusable bearer exposure.

The metadata discovery capability cannot authorize inference. Neither capability can authorize arbitrary platform APIs, and the app's identity signing key cannot open or mint them. Existing bearer, Desktop and delegated Task flows keep their own authorization contracts.

Identity across the A2A boundary​

An inbound A2A request cannot select a member, a role, or an approval. The payload is untrusted content. Registration and the shared secret authenticate the caller; they do not authorize an action.

Kaizen 0.4 establishes a new A2A security identity. The upgrade migration deliberately deletes the retired product's persisted A2A feature overrides, public base URL, inbound-agent mappings, shared secret, and every prior-release Agent Studio connection key; it never promotes those credentials into Kaizen. Administrators must create a new secret or fresh peer-bound keys and re-register every peer before re-enabling A2A. A database downgrade does not restore the retired secret, peer registry, or connection keys.

Why this shape​

Centralising identity has one practical payoff: there is exactly one place to audit. Every question of the form "could X have acted as Y?" reduces to "could X have produced a valid envelope?", which is the platform's problem and not a surface Kaizen widens.