# 22.11 One session, one person, one company (/building-ai-workers/hosted-agents/one-session-one-person-one-company)

---
type: Document
title: "22.11 One session, one person, one company"
description: "The server chooses each session's vault from the signed-in person, one vault per connector per session, and a session matrix proves every rule."
status: stable
order: 322.11
ksor:
  owner: team:panaversity
  audience: [ public ]
  approval:
    by: process:panaversity
    at: 2026-11-06T13:18:01Z
chapter: "22"
part: III
expert_status: builder-only
concepts: [ "22.11" ]
last_verified: 2026-11-05
generated:
  at: 2026-11-06T13:18:01Z
  by: process:panaversity
trust_tier: unverified
build_id: sha256:21db26ecbf7fc84b043f434d194e035ffe0481398b00a039c42d852801046bca
dirty: true
ksor_version: 0.0.60
---

**In everyday life.** A bank teller serves one customer at a time. She asks for the customer's card, not the customer's word, before she opens an account on her screen. She closes it before the next customer steps up. And the bank checks her work by sending a test customer through now and then, with the wrong card, to see if she notices. In this concept, the teller is the portal's server, the card is the sign-in, and the test customer is the session matrix.

**What you will learn.** How the portal's server chooses which person's vault each run carries, the rules that keep one person's run away from another's, and how a test proves each rule.

**Why it matters.** A question, a page or the model could name any company. If anything but the signed-in person chooses the vault, Luis's question can run with Ana's rights.

**One request, start to finish.** Luis at Cedar Ridge asks: "Who is vendor V001, and what do we owe them?" Brightline also has a vendor V001, a different company.

1. **The portal's server reads who is signed in** from its own sign-in cookie: Luis, Cedar Ridge.
2. **It looks up the one vault** for Luis at Cedar Ridge in its table.
3. **It starts a session** naming that vault, and only that vault, and a budget.
4. **The worker calls `find_vendor` for V001.** The platform adds Luis's token. The connector reads the token's `tenant` claim, Cedar Ridge, and the database's row-level security ([Chapter 19](../business-data-and-retrieval/overview.md)) shows only Cedar Ridge's V001.
5. **Luis gets Treasure Valley Lumber,** Cedar Ridge's V001, and Cedar Ridge's own total.

Nothing in the question, the page or the model chose the company. The signed-in person did.

### Where could two companies meet now?

[Chapter 18](../securing-connectors/overview.md) kept companies apart at sign-in, and Chapter 19 kept them apart in the database. A hosted worker adds a new place where they could meet: the session. A few rules close it, and each is proved by a test that fails if the rule is broken.

![Five boxes in a row, left to right: a question arrives. The server reads the person and company from the portal's own cookie. It asks the sign-in server whether the person is still a member. It finds the one active vault in its table. It starts the session with that vault and the budget. Below, three boxes. A dashed one, with an arrow to the cookie step: a vault id sent by the page is ignored. A dashed one, with an arrow to the membership step: not a member any more, refused, and the vault archived. A plain one, below the last step: the session's vault list is recorded, exactly.](img/chapter-22-choose-vault.png)

*Figure 22.16. The server chooses the vault, one step at a time. Nothing the page or the question says can change it.*

### What is a grant?

When a person approves consent, the sign-in server records a **grant**: their standing permission for the portal to get tokens for them, until it is withdrawn. Chapter 18 called this record a consent record, and used "grant" for a way of getting a token. Removing a person from a company withdraws it.

### What is the session matrix?

You build it in Milestone 2 (Task 12). For Milestone 1, read the rule column and skim the rest. Chapter 18's sign-in matrix asked "can one company act as another?" Chapter 19's data matrix asked "can one company's rows reach another?" The session matrix asks the next question: can one person's or company's session reach another's? Each row proves one rule, except S7, which proves both of [22.12](the-portals-own-pages.md)'s page rules. In Milestone 2 you also plant a bug for each row, to show the row really fails when its rule is broken. That table is at the start of Sitting 3b.

| Row | The rule | Why it matters |
| --- | --- | --- |
| S1 | **The server picks the vault from the signed-in person,** never from the browser, the question or the model | A question could say anything |
| S2 | **A vault id from the browser is ignored** | A page could send any vault id |
| S3 | **Exactly one vault per connector per session** | With two vaults for the same connector address, the platform uses the first, so Luis's session could act as Ana |
| S4 | **No company in the definition.** The company comes only from the token | One definition serves every session |
| S5 | **A removed person cannot start a session,** because the portal first asks the sign-in server whether the person is still a member, and their vault is archived | Leaving a company must end access. Removal also withdraws the person's grant |
| S6 | **An offboarded company's vaults are archived,** and its scheduled runs pause | No future session can use them |
| S7 | *Explained in 22.12.* **No sign-in token in any event, and nothing from a session runs in the page** (22.12) | A token in a page or a log can be stolen |
| S8 | *Explained in 22.12.* **A session belongs to the person who started it** (22.12) | Anyone who learns a session id could watch it |

Rows S7 and S8 are about the portal's own pages, which the next concept, 22.12, explains. Read them now as names only.

### Which rules have no row of their own?

- **The application is the boundary.** The platform does not know which of your users owns which vault, so your server checks every time. Rows S1 to S3 together prove it.
- **The connector and the database still check.** The session rules are a new lock, not a replacement for the old ones. Chapters 18 and 19's tests still run in CI.
- **Scheduled runs are checked for removed people,** because a scheduled run does not pass through the portal's session start. That rule belongs to Milestone 2, so [22.15](schedules-and-webhooks.md) covers it.

**At work.** In [Lab 22](lab.md), Task 12, OpenCode writes `scripts/session-matrix.ts` and runs it in staging: eight rows, each starting real sessions that cost a few cents. In Task 12b, it plants each bug in turn, on a branch, runs the matrix rows that bug targets and the configuration check, records the result in `ports/ch22.md`, and deletes the branch. Every bug must fail at least one row.

**The operating rule:** choose one vault per session on the server, from the signed-in person and their current membership. Prove each rule with a test that fails when it is broken.

![Two parts. Left, the session matrix: eight rows, each marked pass in green. S1 each person gets their own company's V001. S2 a vault id from the browser is ignored. S3 one vault per connector per session. S4 no company in the definition. S5 a removed person cannot start a session. S6 an offboarded company's vaults are archived. S7 no token in any event, nothing from it runs in the page. S8 a session belongs to the person who started it. Right, headed the first-match trap: a box labeled Luis's session holds two vault tags, Ana's vault in gold, then Luis's vault. A red-orange arrow runs from Ana's vault, the first, to a box labeled connector, with the words the first match wins. Below: so the portal refuses any session with two matching vaults.](img/chapter-22-session-matrix.png)

*Figure 22.17. The session matrix, and the trap behind row S3: with two matching vaults, the first one wins.*
