opbox

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

Attachedge.attach

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

The ONE verb-minted write path for ANY primitive<->primitive association: a TYPED, directed entity_edge row, polymorphic endpoints by entity_node_type (migration 0026). GENERALISES the former matter.attach (CC-OPBOX 34 B2): the destination may be ANY attachable node type (not just MATTER), and the edge may carry an additive weight (migration 0091, DEC-19). matter.attach is RETAINED as a thin alias of this verb (dst forced MATTER) for caller compatibility.

RATIO (CC-OPBOX 34): entity_edge is a REGENERABLE DERIVED INDEX (INV-2). This verb is the canonical relationship verb that mints a VERB-minted edge (source_column_id NULL); the LINK-column-derived edges are NOT minted here (they are an in-tx continuation of the LINK-write/column.add verb via regen_link_edges, covered by THAT verb’s single append_event). There is NO standalone edge CRUD beyond this attach/detach pair (R1) and NO bearerless side effect (R2): the write is inside the kernel tx, RLS-bound + atomic + audited (C2/C5).

HONOURS SENSITIVITY AT THE VERB (INV-11, BOTH endpoints): the caller MUST be able to READ both the src and the dst, or the verb mints the not-found-equivalent deny (anti-enumeration). A governed confidential node (MATTER/DOCUMENT/FILE_RECORD) goes through assert_can_read; a non-confidential node (PARTY / TABLE_ROW) is existence-checked in THIS workspace (RLS already scopes it).

IDEMPOTENT on the C9 dedup tuple (uq_entity_edge_dedup) with source_column_id IS NULL: re-attaching the same (src, dst, kind) edge is a no-op-shaped success (ON CONFLICT DO NOTHING, read back the existing id) - never a duplicate edge. AUDIT (CC-OPBOX 34 B4 / R3): the SINGLE event of record is this verb’s dispatch-level append_event (registry.rs audits one event per verb keyed on the OUTPUT), so the OUTPUT names the minted edge endpoints + edge_kind + source_column_id - NO second append_event (R3: the derived index carries none; no double-audit). data_plane.rs added no in-file append_event before, but every verb WAS dispatch-audited; the gap closed is the PAYLOAD richness, not an unaudited write.

Input { srcType, srcId, dstType, dstId, edgeKind?, metadata?, weight? } (fields ending in ? are optional)

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

Detachedge.detach

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

The twin of edge.attach: DELETEs the VERB-minted edge on the C9 dedup tuple WHERE source_column_id IS NULL (so it NEVER retires a LINK-column-derived edge - those are owned by regen_link_edges + the source_column_id ON DELETE CASCADE, CC-OPBOX 34 B3). IDEMPOTENT: detaching an absent edge is a no-op-shaped success (deleted=false). AUDIT (CC-OPBOX 34 B4 / R3): the dispatch-level append_event audits this verb’s OUTPUT, which names the RETIRED edge endpoints + edge_kind + source_column_id - the single event of record, no second append. INV-11: the caller must be able to READ both endpoints (the same fence edge.attach applies), so a detach cannot probe a restricted node’s existence. RLS-scoped + the explicit workspace predicate.

Input { srcType, srcId, dstType, dstId, edgeKind? } (fields ending in ? are optional)

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