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:
| Field | Source | Role |
|---|---|---|
sub | x-user-id | Stable Kamiwaza user id |
email | x-user-email | The document-ACL principal, lowercased |
display_name | x-user-name | Safe display only |
platform_roles | x-user-roles | Role checks, normalized to lowercase |
platform_workroom_id | envelope | Workroom scoping |
| auth headers | envelope | Forwarded 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_subjectandlinked_usernameonoauth.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 writesoauth.refresh.revoked, and everyfederated_readorfederated_writecall writeschat.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
adminrole. It permits the complete Kaizen operator surface plus bounded, read-only platform inventory. - Kaizen App Admin comes from
ownerin 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:
subis the member whose current authority, charge ownership, and consent bound the grant;actis 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.
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.