SESSION STATE · TENANT SCOPE

ADK Java core: keep session state tenant-scoped

Write one tenant fact into ADK-style session state and see which sessions, users and tenants can read it under each key prefix.

Computed experiment · sixty inspection seconds

Your experiment

Alice at tenant acme stores the acme subscription plan in state. The plan is a tenant fact: every acme session should see it and no globex session should. Start with the app: prefix and a shared app name, then change one control at a time and watch the globex read.

Play replays the computed run over sixty inspection seconds; Next event steps, Restart rewinds, and Inspection time scrubs. Any input change recomputes the run and rewinds it without autoplay.

Event trace

    Computed data

    Final metrics

    Teaching model of the scoping rules ADK session services apply to state keys: no prefix belongs to one session, user: to one app name and user id, app: to one app name, and temp: to the current invocation. Storage keys are shown as text; no session service, database or tenant is contacted. The tenant guard is an application-side check, not an ADK feature.

    One write, four readers

    A runner identifies stored state by app name, user id and session id, and ADK state keys add a scope through their prefix: a key without a prefix lives in one session, user: is shared by every session of one user id under one app name, app: is shared by everyone under that app name, and temp: lasts only for the current invocation. In this experiment alice at tenant acme writes the acme subscription plan in session S1. Four reads follow: alice on the next turn of S1, alice in a new session S2, bob at acme in S3, and a globex user in S4. The plan is tenant data, so the three acme reads should succeed and the globex read must not.

    The opening leak: app: under a shared app name

    With State key prefix for the write set to app: and App name used by the runner set to One app name for every tenant, the write is stored under app|support-agent. All three acme readers get acme-enterprise, which looks like success, but the globex reader in S4 resolves the same key and also gets acme-enterprise: one cross-tenant leak. Choose A separate app name per tenant and the write moves to app|support-agent-acme; the globex lookup now uses a different key, the leak disappears and all three acme reads are still served.

    user: narrows the scope but user ids can collide

    Return to the shared app name and choose the user: prefix. The plan is now visible in S1 and S2 but not to bob, so one acme read is missed. The globex reader is also called alice, so it computes the same user|support-agent|alice key and the leak returns. The two user-id controls only matter with the user: prefix: set User id passed to the session service to Tenant-qualified id and the keys become acme:alice and globex:alice, which removes the leak; clearing Globex also has a user named alice removes it too, but only by luck of naming.

    Session and temp scopes lose the fact instead of leaking it

    Choose No prefix: only the next turn of S1 sees the plan, so two acme reads are missed and nothing leaks. Choose temp: and even the next turn of S1 is a new invocation, so all three acme reads miss. Neither choice is a fix for tenant data; they trade a leak for a missing fact. Finally restore the opening settings and check Tenant guard checks the stored tenant tag on read: the globex read is still resolved to the shared key, but the guard compares the stored tenant tag and blocks it, so the run reports no leak with one blocked read. A guard is a useful second line, not a replacement for correct keys.

    How to inspect it and what is simplified

    Play, Next event, Restart, Speed and Inspection time step through the write, the four reads and the verdict across sixty seconds; the table lists each reader, the exact storage key it resolved and whether it should see the value. The model implements only key scoping: there is no persistence, eviction, event log or concurrency, the tenant tag and guard are application choices rather than ADK features, and real deployments should also separate artifacts, memory, caches and logs per tenant.