Investigate a Session
Reconstruct one operation from the records its owners retained, and close the incident with what is established and what stays unknown.
On this page
An investigation ends in a bounded statement about one operation: what happened on the observed path, which signed evidence supports it, what remains unknown, and which owner repairs it.
A caller's error, a signed decision, and a resource observation describe different parts of the same attempt. Preserve all three, then join them through the original operation's identities before you call it refused, completed, or unresolved.
StartThe kernel establishes what it decided and captured
Retain capability, operation key, admission operation ID, and capture. The record that establishes it is the original decision, the dispatch record, and the retained authority.
1. Name the original operation
Record the request, authenticated caller, session, tool, destination, and approximate time. Retain the original operation key and request commitment where the execution path provides them, and locate the host that admitted the work and the resource owner that could have executed it.
Follow the operation through admission, dispatch, output, and recovery. A denied call, an evaluator failure, and a lost provider response look alike to the caller and need different repairs, so find the boundary that produced the symptom first.
2. Export the original receipts
Use the installed chio and jq, with administrative read access to the deployment's receipt database. Set the original store and the tool-server ID under investigation, and export into a new private directory, which keeps evidence already gathered intact.
set -eu
umask 077
: "${CHIO_RECEIPT_DB:?Set the original deployment receipt database path}"
: "${CHIO_TOOL_SERVER:?Set the tool-server ID under investigation}"
: "${CHIO_INCIDENT:?Set a new private incident directory}"
test ! -e "$CHIO_INCIDENT"
mkdir -m 0700 "$CHIO_INCIDENT"
chio --receipt-db "$CHIO_RECEIPT_DB" receipt list \
--admin-all --tool-server "$CHIO_TOOL_SERVER" --limit 100 \
> "$CHIO_INCIDENT/receipts.jsonl" 2> "$CHIO_INCIDENT/selection.stderr"You should see one receipt per line in receipts.jsonl. When selection.stderr prints next_cursor=<seq> total_count=<n>, repeat the query with --cursor set to that value under the same database, scope, filter, and limit, and save each page and its stderr separately. Continue until no next cursor remains, as receipt pagination describes: a partial export cannot show that an absent receipt never existed.
3. Verify the export under an independent key
Take the signer from trusted configuration, never from a key copied out of the suspect artifact. The host owner supplies the original kernel public-key file from independent configuration.
set -eu
: "${CHIO_KERNEL_PIN:?Set the independently retained original kernel public-key file}"
chio receipt verify --json --input "$CHIO_INCIDENT/receipts.jsonl" \
--trusted-kernel-pubkey "$CHIO_KERNEL_PIN" \
> "$CHIO_INCIDENT/verification.json" 2> "$CHIO_INCIDENT/verification.stderr"You should see verification.json. Keep the input and any error: a failure is incident evidence. chio receipt verify checks original signatures, the signer pin, and action hashes offline; policy re-evaluation, log inclusion, and the external provider are separate questions.
A valid signature and relevance to this operation are two checks, as verify security evidence explains. Check tenant, agent, session anchor, authentication epoch, capability, request commitment, and tool identity. For caller delivery, add the executor pin and original delivery binding; for a confined tool, compare the launch's manifest, executable, and descriptor commitments.
Once signatures and bindings pass, write a separate reading aid. The signed JSONL stays unchanged, so each projected entry traces to its envelope.
jq -c '{id, tool_server, tool_name, decision, receipt_kind}' \
"$CHIO_INCIDENT/receipts.jsonl" > "$CHIO_INCIDENT/summary.jsonl"Group a verified export without changing its originals
A grouping shows which tool and decision combinations dominate an export. Run it only on evidence collected under authorized access, with your verified, bounded export in place of incident/receipts.jsonl. jq -s loads the whole export into memory, so group large incidents page by page.
jq -s '
group_by([.tool_server, .tool_name, .decision])
| map({
tool_server: .[0].tool_server,
tool_name: .[0].tool_name,
decision: .[0].decision,
count: length,
receipt_ids: map(.id)
})
' incident/receipts.jsonl > incident/by-tool-and-decision.jsonThe grouping is a navigation aid. It checks no signatures, recovers no omitted pages, and establishes no completion, and its counts describe only the collected pages and selected server. Follow a receipt ID back to its original envelope, verify it under the independent pin, and correlate its operation with dispatch and outcome state before assigning a cause. Sensitive request and result bodies stay in their access-controlled location.
4. Order the observations by what each owner can establish
The kernel records admission and capture, the executor reports its own work, and the provider or resource exposes the resulting state. Keep a working timeline beside the original artifacts, one entry per observation:
- Operation. The original operation identity the observation belongs to.
- Times. When the event occurred, if authenticated; when another component learned of it; when you collected the record.
- Source. The producing component and the original artifact reference.
- Fact. What that artifact establishes.
- Inference. Your hypothesis, kept in its own field so it stays distinct from signed observations as the incident changes hands.
Sort by cause as well as by timestamp. A receipt that references a prior operation or effect fixes an order that wall clocks miss; a late receipt can describe an effect that completed first, and an early timeout can precede a provider response that never reached the caller. Join observations through the original request and retained identities before you treat them as one sequence.
A provider object beside an unknown kernel outcome marks a missing reconciliation step; a denied admission beside a changed resource points to another effect path or a miscorrelated record. Keep both disagreeing observations, and investigate the boundary between them.
5. Classify the outcome by the boundary it reached
The last boundary the operation crossed decides what the observation means and where to look next.
| Observed state | What it means | Investigate next |
|---|---|---|
| Denial before dispatch | The selected path refused the operation | The exact guard, scope, or authority mismatch |
| Evaluator failure | The host reported failure; the effect may be unknown | Original capture and executor state |
| Capture without outcome | The external effect may have started | Authenticated downstream status and the original ledger |
| Guarded release checkpoint | The exact response can be replayed | Whether delivery was retried, without re-execution |
| Partial response or rollback | Some containment work is unresolved | Per-effect adapter state and the valid lease owner |
An evaluator failure stays distinct from a policy denial even when no output reached the client. The signed session report binds the failure observation and retains execution_outcome=unknown.
For an active-defense finding, inspect the versioned rule, group commitment, ordered event IDs, evidence digests, source receipts, and time interval. Compare event time with ingest time, and read detector-health records for lateness, eviction, or store failure. Follow the finding to its authorized plan and each requested, applied, failed, and rolled-back effect, and verify the affected-set commitment; a session-wide status can hide one remaining contribution. A failed publication walks these boundaries in order.
6. Trace one message across a cancellation
Use the stopped host from the containment rehearsal. The trace establishes whether worker sent cancel-effect-1 before cancellation and whether later refused work changed the mailbox. The native server is chio-ipc, its tool is send_effects, and the receipt's process metadata carries the original operation key.
Run the query from the extracted rehearsal directory. Supply the kernel public-key file the host owner retained, never a pin taken from the suspect receipt's kernel_key; a verification error stops the projection and keeps the export and its stderr.
set -eu
umask 077
CHIO_STATE="$PWD/.chio/cancellation/host"
CHIO_INCIDENT="$PWD/incident-cancellation"
test ! -e "$CHIO_INCIDENT"
mkdir -m 0700 "$CHIO_INCIDENT"
chio receipt list --receipt-db "$CHIO_STATE/receipts.db" \
--admin-all --tool-server chio-ipc --limit 100 \
> "$CHIO_INCIDENT/original-receipts.jsonl" \
2> "$CHIO_INCIDENT/selection.stderr"
# The owner supplies the original trusted key, independently of the export.
: "${CHIO_KERNEL_PIN:?Set the path to the independently retained original kernel public-key file}"
chio receipt verify --json --input "$CHIO_INCIDENT/original-receipts.jsonl" \
--trusted-kernel-pubkey "$CHIO_KERNEL_PIN" \
> "$CHIO_INCIDENT/verification.json" 2> "$CHIO_INCIDENT/verification.stderr"
jq -c 'select(.metadata.chio_process.operation_key == "cancel-effect-1") |
{tool_server, tool_name, decision: .decision.verdict,
operation_key: .metadata.chio_process.operation_key,
dispatch_state: .metadata.admission_operation.projected_dispatch_state,
state: .metadata.admission_operation.projected_state}' \
"$CHIO_INCIDENT/original-receipts.jsonl"{"tool_server":"chio-ipc","tool_name":"send_effects","decision":"allow","operation_key":"cancel-effect-1","dispatch_state":"terminal","state":"completed"}You should see one allow receipt, dispatch_state terminal and state completed, for the operation before cancellation. The projection omits receipt IDs and timestamps, which change per run; the original NDJSON stays beside it.
The refused call left no receipt, because authentication and cancellation refusals happen before kernel evaluation. Its absence from the query therefore shows nothing about whether a request reached the host. Confirm the rest outside the receipt store: the stopped mailbox store should hold the cancel-effect-1 row alone, and the later cancelled wire response belongs with the original evidence.
7. Route the repair and close with a bounded statement
Each missing fact belongs to the owner that can establish it; a general retry cannot create it.
- An identity mismatch goes to the authenticated host and its authority configuration.
- A captured effect with an unknown outcome goes to the original execution path, keeping the original attempt and holds while you reconcile that operation.
- A missing release checkpoint goes to the output and release owner.
- A partial response goes to the adapter and worker that own the outstanding contribution. For credential access followed by egress, join the rule's ordered event IDs and evidence digests to its finding, plan, and individual effect and removal records with the correlation and action inspector.
Keep captured authority attached to the original operation until its effect is reconciled. A fresh credential attempt, a refunded captured budget, or a resend hides the incident without resolving it; repair custody, authority, or store availability first, under valid serving fences. Quarantine recovery and key transparency take over when the failing boundary is theirs.
Before repair, restrict access to provider responses, decoy records, and sensitive request bodies; routine summaries cite commitments and artifact locations. Close with the bounded statement from the opening, naming the new work it authorizes and every alternate path outside your observation, so the next operator reads an incomplete history as incomplete.
Evidence by boundary
The identities and records each boundary of an operation contributes, for lookup during an incident.
Search for capture, release checkpoint, mailbox, or finding.
Correlation identities
| Boundary | Identity to retain | Authoritative observation |
|---|---|---|
Caller | Agent, tenant, session, epoch and original request | The authenticated host's accepted context and connection history |
Admission and dispatch | Capability, operation key, admission operation ID and capture | The original decision, dispatch record and retained authority |
Resource effect | Bound executor or provider attempt | Resource-owned state or authenticated status for that same attempt |
Response and release | Retained result and release checkpoint | The exact guarded response authorized for replay |
Detection and containment | Event, finding, plan and individual effect IDs | Authenticated event history and each adapter's applied or outstanding contribution |
Observations in the cancellation trace
| Observation | Meaning | Continue |
|---|---|---|
Process operation key and request commitment | Identify the frozen requested work and authenticated process | Compare the original request, connection, and selected capability, never an incident label chosen afterward |
Admission operation ID, dispatch commit and projected state | Explain retained kernel lifecycle for this operation | Inspect the original store and ledger, and its provider attempt when the outcome is unresolved |
Mailbox row cancel-effect-1 | The owned native message was committed | Check the stopped mailbox store independently of the receipt projection |
Later cancelled wire response | The later admission was refused before a new kernel receipt | Preserve its original response and verify the mailbox still holds only the first message |
Response plan or partial removal | Containment has separate finding, plan, and effect identities | Join the exact finding and plan, then inspect each outstanding contribution |
Timeline entries
| Timeline entry | Established by retained evidence | Still unresolved |
|---|---|---|
Admission accepted | Original request satisfied the recorded admission decision | Whether dispatch later occurred |
Capture committed | Original authority crossed the durable capture boundary | Whether the provider accepted the effect |
Connection closed | Caller or host lost its transport observation | Whether the effect completed |
Provider status obtained | Authenticated status for the bound original effect, if supported | Any behavior outside that status contract |
Guarded release checkpoint found | Exact response is authorized for replay | Whether the first client received it |
Keep event time and ingest time distinct for detector findings: a late collection time leaves the original event's rule window unchanged.
A failed publication, boundary by boundary
The reviewer reports that posting its summary for issue #417 to the pull request failed. Identify the original posting request and destination, then inspect the retained flow state and any bound declassification grant. If admission refused the transfer before dispatch, investigate the incompatible clearance or missing authority; the provider has no result to show in that case.
If capture committed, look for executor and provider evidence under that operation, because a client timeout cannot classify the result as denied. If the provider returned and output release failed, inspect the retained output, classification, and release custody before considering another call. If an exact guarded checkpoint exists, recover the original response and leave the post alone.
Finally, check whether active defense applied a restriction in the same interval. That response has its own finding, authorization, effects, and rollback history: lifting it settles nothing about the post, and a completed post says nothing about whether every containment contribution was removed.