opbox

The actor.* family (7 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.

Setactor.avatar.set

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

(30-user-profiles). Set the ACTING actor’s own avatar - a small data:image/ URL (<= 256KB) or an https:// URL. Self only (the acting actor); surfaced back by session.get as avatarUrl. The verb is the one governed write path (INV-1).

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

Deactivateactor.deactivate

PropertyValue
Capabilityactor.write
Risk classSensitive - autonomy L2
Min. permissionAdmin
AvailabilityOn demand (MCP tier 2)
IdempotentYes
EgressNone - in-box (pure Postgres, no outbound call)

Offboard a HUMAN: flip status ACTIVE→REVOKED. { actorId }. Reversible via actor.reactivate. Refuses the sole CoS + the caller themselves. A REVOKED actor’s tokens no longer resolve (auth.rs §6 - status != 'ACTIVE' is a fail-closed deny at resolve).

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

Getactor.get

PropertyValue
Capabilityactor.read
Risk classRead - autonomy L0
Min. permissionAdmin
AvailabilityOn demand (MCP tier 2)
IdempotentNo
EgressNone - in-box (pure Postgres, no outbound call)
Render schemaYes - drives an inline chat artefact and a GUI panel

Read one actor row in the caller’s workspace. { actorId }. RLS-scoped

  • an explicit workspace_id = $ predicate so a cross-workspace id is unreachable (never leaks).

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

Inviteactor.invite

PropertyValue
Capabilityactor.write
Risk classSensitive - autonomy L2
Min. permissionAdmin
AvailabilityOn demand (MCP tier 2)
IdempotentNo
EgressNone - in-box (pure Postgres, no outbound call)

Mint a NEW HUMAN roster user at a starting firm tier. { displayName, tier, email? } (tier ∈ MEMBER|ADMIN|OWNER). The post-IdP human-onboarding path; the new actor is ACTIVE but carries no sessions yet. Per [2026] VJS-CC-OPBOX 29 D2 (following CC-OPBOX 9 D8): when an email is given it is persisted at mint AND a one-time INVITE token is minted - the human redeems it through the existing unauthenticated reset/confirm door (/auth/reset/confirm) to set THEIR OWN first password (no admin-sets-another verb in the normal path). The plaintext inviteToken is returned ONCE (only its hash is stored). With no email it is the legacy seat-only invite (the D4 no-email/seed fallback sets the first password). Refuses a non-firm tier (EXTERNAL is a portal kind, not a settable firm tier).

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

Listactor.list

PropertyValue
Capabilityactor.read
Risk classRead - autonomy L0
Min. permissionAdmin
AvailabilityOn demand (MCP tier 2)
IdempotentNo
EgressNone - in-box (pure Postgres, no outbound call)
Render schemaYes - drives an inline chat artefact and a GUI panel

List actors in the caller’s workspace. { kind?, status?, limit? }. RLS-scoped (ws B never sees ws A’s actors). Paginated (NFR-PERF-3). Optional kind/status filters.

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

Reactivateactor.reactivate

PropertyValue
Capabilityactor.write
Risk classSensitive - autonomy L2
Min. permissionAdmin
AvailabilityOn demand (MCP tier 2)
IdempotentYes
EgressNone - in-box (pure Postgres, no outbound call)

Reinstate a previously-offboarded HUMAN: REVOKED→ACTIVE. { actorId }. The reversible twin of actor.deactivate (the same HUMAN-only guard; the self / sole- CoS guards are inert on a re-enable but kept for symmetry - a re-activate is never harmful).

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

Set Roleactor.setRole

PropertyValue
Capabilityactor.write
Risk classSensitive - autonomy L2
Min. permissionOwner
AvailabilityOn demand (MCP tier 2)
IdempotentYes
EgressNone - in-box (pure Postgres, no outbound call)

Move a HUMAN’s firm tier (MEMBER<->ADMIN<->OWNER). { actorId, tier }. The ONE role-change write path (INV-1) - the former ADMIN-gated actor.tier.set was deleted (F4) because it had no last-OWNER guard. BLOCKS the last-OWNER-demote (the workspace must keep at least one OWNER) AND is OWNER-gated (an ADMIN can no longer self-promote to OWNER). HUMAN-only load + the firm_tier write (an AGENT’s standing is the org lattice, not firm_tier).

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