opbox

The inbox.* family (3 verbs). Every verb enters through the one front door: it is capability-checked, permission-checked, scope-checked and audited before it runs (see Security & permissions). Each entry below lists its capability, risk class (which fixes the minimum autonomy level), the minimum caller permission, its availability tier, whether it is idempotent, and any outbound egress.

Getinbox.get

PropertyValue
Capabilityinbox.read
Risk classRead - autonomy L0
Min. permissionMember
AvailabilityCore - always on (MCP tier 1)
IdempotentNo
EgressNone - in-box (pure Postgres, no outbound call)
Render schemaYes - drives an inline chat artefact and a GUI panel

Returns ONE notification - but ONLY if it belongs to the caller (recipient_id = ctx.actor.id): another actor’s notification reads back as “not found” (the IDOR fence returns the same not-found verdict as a truly-missing id, never leaking existence).

Input { notificationId } (fields ending in ? are optional)

Call POST /v/inbox.get with a JSON body; returns JSON (or { error, code }). Also exposed as the MCP tool inbox.get.

Listinbox.list

PropertyValue
Capabilityinbox.read
Risk classRead - autonomy L0
Min. permissionMember
AvailabilityCore - always on (MCP tier 1)
IdempotentNo
EgressNone - in-box (pure Postgres, no outbound call)
Render schemaYes - drives an inline chat artefact and a GUI panel

Returns ONLY the caller’s own notifications (recipient_id = ctx.actor.id - the IDOR fence): an actor NEVER sees another actor’s inbox. Optional status/kind filters. Ordered newest-first; the per-section unread-dot is a thin COUNT over this same store.

Input { status?, kind? } (fields ending in ? are optional)

Call POST /v/inbox.list with a JSON body; returns JSON (or { error, code }). Also exposed as the MCP tool inbox.list.

Markinbox.mark

PropertyValue
Capabilityinbox.write
Risk classWrite - autonomy L1
Min. permissionMember
AvailabilityCore - always on (MCP tier 1)
IdempotentYes
EgressNone - in-box (pure Postgres, no outbound call)

where status ∈ {READ, ARCHIVED}. WRITE/L1/MEMBER. Flips the caller’s own notification UNREAD → READ (sets read_at, ck_notification_read_iff_read holds) | ARCHIVED (clears read_at - a dismissed signal is not “read”). Recipient-scoped: a mark on ANOTHER actor’s notification matches 0 rows and reads back as not-found (the IDOR fence - you cannot mark what is not yours). Idempotent: re-marking to the same terminal status is a no-op success.

Input { notificationId, status } (fields ending in ? are optional)

Call POST /v/inbox.mark with a JSON body; returns JSON (or { error, code }). Also exposed as the MCP tool inbox.mark.