Acting on a holder's instruction¶
You can tell an agent working as a team worker, "change Iris's signature to this text", and it can do it. The platform allows that for two edits, and for listing and revoking an App's credentials, and only when the person who holds that worker manages the App and the worker names what they told it. The doors that staff a team (casting, retiring and deleting a worker) and change who is a member of an App accept the same claim on slightly different terms, set out further down. This page explains what that naming is, why the rule is built this way, and what it does and does not prove. For the exact refusals, see Worker signature and trailer.
Why there is a rule at all¶
Most changes to a team's staffing are a person's act: a person who manages the App, as themselves. A worker session is an agent acting for a person, and the platform cannot tell by itself whether the person asked for a particular edit or the agent decided on its own. A blanket refusal kept that safe, and it also made a plain request ("update my signature") impossible to hand to the agent you were already talking to.
The rule for these two doors (changing a worker's signature or trailer, and changing the agent a worker casts) keeps the person accountable and lets the agent do the typing. The worker's holder must manage the App, and the call must carry an instruction claim: a typed pointer to the place where the holder told the worker to do it.
What an instruction is¶
An instruction is a pointer, never the words themselves. It has a kind and a reference:
| Kind | The reference is | The platform checks |
|---|---|---|
TEAM_CHAT_MESSAGE |
A team chat message, as its hrn:node: URN |
The caller can read it, and it is in this App. |
TASK |
A team task, as an hrn:agenttask: URN |
The caller can read it, and it is in this App. |
LIVE_CHAT |
An opaque address your client gives to a live conversation | Its shape only: 1 to 512 UTF-16 code units, with no whitespace or control characters. It is never message text. |
"The caller can read it" includes a message or task the worker wrote itself. Pointing at your own message is accepted, and the platform does not try to tell that apart from pointing at the holder's.
A person may send an instruction too. It is checked the same way, so a bad, unreadable or other-App reference refuses the person as well. It is never recorded for them.
What is recorded¶
When a worker is admitted, the platform writes one team event that names the
worker, the person who holds it, the kind and the reference. It records the
pointer and never the content of the message or task. The signature edit is
recorded as worker.signature-changed and a casting change as worker.casting.
What is not verified¶
The platform records the claim. It does not check that:
- the instruction was ever given, or was clear;
- the person wrote it, or that it is recent;
- it is being used only once.
The holder answers for the claim. That is the point of the record: if an agent says it was told to and it was not, the event shows whose worker it was and what it pointed to.
Two limits follow, and they are accepted rather than hidden:
- The platform does not detect a headless session. At the doors above it refuses a worker session that carries no claim, and a session launched by a host even when it carries one. (The staffing and member doors differ for a launched session; see below.) A session someone started by hand, which a ticker later wakes with a claim, is admitted, and the record is what holds its holder to account.
- Nothing gives an instructed edit more reach than its holder has. If the holder manages the App, the worker may make these two edits. A holder who is a plain member gets the same refusal through the worker as they would get themselves.
Where it applies¶
This page covers sixteen doors that accept an instruction. Two edit a worker:
updateWorker (the signature and trailer) and changeWorkerAgent. Seven list
or revoke an App's credentials. Seven staff a team or change its members:
castWorker, castWorkerPreview, retireWorker, deleteWorker,
addAppMember, removeAppMember and leaveApp. The MCP tools for the two
worker edits take no instruction, so a worker session that calls them stays
refused. The specifications behind them are cor:dmo:050:12 (the rule),
cor:agt:040:04 and cor:dmo:050:11 (the two worker doors) and
cor:agt:080:24 (the two grants below); see Product specs and
citations.
Listing and revoking an App's credentials¶
You can tell an agent, "list this App's keys" or "revoke that key", and it can
do it, on the same terms. The seven doors are the three lists (appKeys,
appKeysPage and appMemoryWriteGrants) and the four revokes (revokeAppKey,
revokeAppKeyScope, revokeAppGrant and revokeAppMemoryWriteGrant). Each
takes the same instruction claim.
Who is admitted. A person who manages the App, or that person's held worker on a validated instruction. A worker whose holder does not manage the App gets the holder's own refusal. A worker with no instruction is refused, as before.
A refused worker learns nothing about what exists. It gets the same answer whether or not the key, key scope or grant exists. A person keeps the more useful answer for most of them: "not found" for a missing key (including the key a scope revoke names) or a missing App grant. A missing write grant is the exception, and refuses everyone, a person included.
At these seven credential doors, a session launched by a host is refused whatever it claims, and nothing is recorded. Unlike the staffing doors, no grant admits it at these seven.
What is recorded.
- An instructed revoke that changed a row records one
app.credential-actevent. A revoke that changed nothing records none (a key already revoked, or a scope the key did not hold), and a person acting directly records none. - An instructed list records one event per call, so one per page of
appKeysPage. - The event never names a key, a scope or a grant id.
What stays person-only. Minting, rotating and giving keys, scopes, App
grants and write grants. The nested App.keys field also refuses a worker
session, with or without an instruction, because a nested field carries no
instruction to check.
For the table of who may list and revoke what, see App keys: who lists, mints, revokes and rotates them.
Staffing and membership doors¶
You can tell an agent, "cast a backend engineer called Iris", "retire Iris" or "add Dara to this App as a member", and a worker session may do it. These seven doors take the same instruction claim, and a worker session also has a second way in.
Merged, not necessarily deployed
This is built in hadron-server at 517eb1db (hadron-server#3308). A server
older than that still admits any worker session at these doors on its
holder's right, as it did before. The rest of this section describes the
server at 517eb1db and later.
The two ways in. A worker session is admitted at these doors in one of two ways, and refused if it has neither:
- An instruction, as above: the call carries the claim, and the worker's holder must be allowed to make the change themselves.
- Its own grant. A worker that holds the grant for the door may act without
an instruction.
workers.staffcovers casting (and its preview), retiring and deleting workers.app.memberscovers adding and removing App members, including a member leaving.
The holder's own rights still apply on both ways. Neither the instruction
nor the grant gives a worker more than its holder has. A holder who is a reader
in the App cannot staff, so their worker cannot either, whether it carries an
instruction or holds the grant. For the member doors, a holder who may not manage
members may still remove only themselves, and the worker gets the same.
A session launched by a host. At these doors a launched session's claim is ignored, neither checked nor recorded. It acts only on its own grant: refused without one, admitted with one. This differs from the credential doors above, where a launched session is refused whatever it claims (the credential doors above).
Refusal comes first at the member doors. A worker session that has neither an instruction nor the grant is refused before the platform looks up the user you named, so the refusal says nothing about whether that user exists. Retiring and deleting look up the worker first, because the worker's App is the scope of the call. Casting is scoped by the App you name.
leaveApp is a second way to leave. A member can leave an App with
leaveApp as well as through removeAppMember, and both are gated alike. A
worker session leaving for its holder goes through the same rule (an instruction
or app.members) and records the removal. A person leaving directly is
unchanged.
What is recorded. An admitted instruction is recorded with the worker, the holder, the kind and the reference, as at the other doors. Three cases that change nothing still record the claim: an instructed cast preview, a cast of a worker that already exists, and a member add or remove that changes nothing.
The two grants¶
A grant is a narrow, named permission that one worker holds. These two exist for workers that act with no human watching, such as a host-launched session.
| Grant | Covers |
|---|---|
workers.staff |
Casting, previewing a cast, retiring and deleting workers. |
app.members |
Adding and removing App members, and leaving an App. |
- Only a person who manages the App gives one (ruling 0099). A worker never gives a grant to itself or to another worker, in any shape. Taking one back also admits the holder's worker on an instruction, as in Listing and revoking an App's credentials.
- A grant counts only for the worker it was given to. Another worker of the same holder, or the holder themselves, does not inherit it.
- None is given by default. Until a manager gives one, a worker with no instruction is refused at these doors.
From the command line the five commands that use this are team worker cast
(and --dry-run), team worker retire, team worker rm, app member add and
app member remove; see the CLI
reference. The MCP staffing
tools take no instruction, so a worker session calling them needs
workers.staff.
Related¶
- Worker signature and trailer: who can change them, and every refusal
- Teams, workers, and sessions: changing the agent a worker casts
- Authorization: how access is decided on Hadron
- Who may cast and amend a worker: the staffing gate, including worker sessions