Events
The structured event stream, every event name, and the ledger projected from it.
One JSON line per event to stdout. The dashboard, the audit command and the owner's ledger are all views over this stream.
Shape
{ "ts": "2026-07-07T18:21:27Z", "event": "need_info.terms_dictated",
"corr": "fam_8f3a…", "actor": "uma-as",
"details": { "tier": "tier1", "template_id": "alice/advisor-tier1/v2",
"resource_id": "alice-vault/get_positions" } }| Field | Meaning |
|---|---|
ts |
UTC, RFC 3339 |
event |
Dotted name from the register below |
corr |
The negotiation family id, stable across every ticket rotation |
actor |
Which component emitted it |
details |
Event-specific, and deliberately free-form |
corr is the field that makes this usable. With a replicated authority, a single
negotiation's events are spread across every instance, so reading one pod's logs
shows a fragment. Correlate by family, not by process.
Event register
Registration and discovery
| Event | Emitted when |
|---|---|
resource.registered |
A resource lands in the authority's registry |
resources.registered_at_startup |
The startup pull completes |
terms.published |
A terms document version is published |
terms.declined |
The requesting side refuses the proffered terms |
The negotiation
| Event | Emitted when |
|---|---|
permission.registered |
POST /perm issues a ticket |
challenge.issued |
The enforcement point refuses with a challenge |
ticket.presented |
The agent presents a ticket at the token endpoint |
need_info.terms_dictated |
The authority proffers terms |
contract.committed |
A signed agreement verifies |
contract.rejected |
An agreement fails verification |
assurance.assessed |
What her authority could verify about the agent asking |
operator_directory.checked |
An operator's key directory was fetched and searched for this agent's key |
operator_directory.rejected |
A directory was named that is not same-origin with the operator claimed |
operator_directory.unresolved |
A directory would not resolve; the claim stays where it was |
policy.evaluated |
A tier policy decision is made. result includes attention-budget when a lane is full and operator-blocked when she has shut that operator out |
The owner
| Event | Emitted when |
|---|---|
ticket.awaiting_owner |
The negotiation is held pending her decision |
owner.notified |
The pending item reaches her surface |
owner.decision |
She approves or denies |
connection.approved |
A standing relationship is recorded |
connection.revoked |
She revokes one |
policy.updated |
She edits a tier |
policy.created |
She adds terms of her own |
policy.deleted |
She removes a tier; its resources become ungoverned |
operator.blocked |
She shuts out an operator, with the connections and grants it cost |
operator.unblocked |
She lets one ask again |
The grant and its use
| Event | Emitted when |
|---|---|
rpt.issued |
A grant is minted |
receipt.issued |
The counter-signed receipt is returned |
rpt.introspected |
The enforcement point checks a grant |
rpt.consumed |
A single-use grant is spent |
access.allowed |
A call reaches the resource |
access.denied |
A call is refused |
The ledger, as a projection
The owner's ledger is not a separate record. It is this stream, grouped by family:
| Ledger column | Source event |
|---|---|
| promised | contract.committed |
| personally approved / denied | owner.decision |
| touched | access.allowed |
| connected | connection.approved |
| revoked | connection.revoked, and operator.blocked for every connection it ended |
| relaxed | a rule she wrote lowered an ask-me tier to automatic, naming the rule that fired |
Those first three columns answer the question she will actually ask: did what
happened match what I agreed to. Reading them side by side is the point —
promised without a matching touched is a grant that went unused, and
touched without a matching owner.decision is either a tier she opened
deliberately or something to investigate.

The rightmost column is the negotiation family. Three rows of a single story sit apart in time and are joined by that id, which is what makes the ledger answerable rather than merely chronological.
Reading it in the lab
make k8s-auditPrints the three columns correlated by family. The compose equivalent is
make audit.
Reading pod logs directly will mislead you when the authority is replicated. Ship the stream and query it, or use the audit command, which reads the store rather than a process.
Emitting your own
Two properties matter more than the schema:
Assign the correlation id once, when the permission is registered, and carry it through every rotation. An id regenerated per presentation makes the stream unjoinable, which is the failure mode that looks fine until the first time somebody needs an answer.
Emit refusals as loudly as successes. access.denied and
contract.rejected are the events that tell you the system is working. A stream
with only the happy path cannot distinguish a healthy deployment from one where
enforcement is switched off.