Relationship (rel) Vocabulary Reference
The rel vocabulary is the closed set of eight values a DCP refs entry may use to describe how one entity relates to another — relates_to, part_of, references, caused_by, derived_from, supersedes, concerns, blocks. It deliberately excludes routing and permission semantics, which belong to the transport layer, not to DCP.
By InterIP Networks · Last updated 2026-07-01.
refs and rel in one sentence
Section titled “refs and rel in one sentence”An Event may carry a refs array — semantic links to other entities. Each entry is a { rel, ref } pair: rel names the relationship and MUST be drawn from the closed vocabulary below; ref is an opaque pointer string, at most 512 characters, identifying the target (SPEC §5.3).
Schema: https://schemas.devcopro.org/v1/common/ref.schema.json
{ "rel": "concerns", "ref": "decision_thin_envelope"}The closed v1 vocabulary
Section titled “The closed v1 vocabulary”rel value | Meaning |
|---|---|
relates_to | A generic, non-specific association between this entity and the target. |
part_of | This entity is a component or member of the target (composition). |
references | This entity cites or points to the target for context, without a stronger claim. |
caused_by | This entity’s existence or state is a consequence of the target. |
derived_from | This entity was produced or generated from the target. |
supersedes | This entity replaces the target, which is now outdated. |
concerns | This entity is about the target — e.g. a finding that concerns a decision. |
blocks | This entity prevents the target from proceeding. |
These eight values are the entire vocabulary. There is no ninth value in v1, and no free-text escape hatch within rel itself — a producer with a relationship that doesn’t fit MUST pick the closest of the eight (typically relates_to) rather than invent a new token.
What’s intentionally excluded
Section titled “What’s intentionally excluded”SPEC §5.3 is explicit: routing and permission relations are intentionally excluded and MUST NOT appear in rel. That includes terms like routes_to, approved_by, delivered_via, and assigned_to. These describe who handles or authorizes something — transport, identity, and approval concerns — not how one project entity relates to another. DCP’s prime directive is that it carries no trust; a rel vocabulary that let producers assert approved_by or assigned_to would smuggle authorization semantics into a layer that has none. Those concerns belong to the transport or application layer, never to DCP’s refs.
The ref field is equally strict about what it is not: an opaque descriptive pointer, not a network address. A consumer MUST NOT automatically resolve a ref as a URL — doing so would open a server-side request forgery (SSRF) path, since ref is untrusted, producer-supplied data. See SECURITY.md in the DCP repository for the full treatment.
Why rel is closed at all
Section titled “Why rel is closed at all”DCP has exactly two closed vocabularies in v1: entity_type (the eight nouns — see Entity Reference) and rel. Everything else that looks like a vocabulary — verbs, status values — is an open, tolerate-and-pass set (see Status Is a Vocabulary, Not a State Machine). The difference is deliberate: entity_type and rel are structural single-responsibility guards. entity_type fixes what kinds of things DCP talks about; rel fixes what shapes of relationship DCP is allowed to express between them. Widening either set is not a documentation update — it is a change to what DCP’s structure can represent at all, so SPEC treats it as a major-version concern, not a routine addition. Verbs and statuses, by contrast, are project vocabulary that varies by domain and can grow without breaking consumers, which is why they stay open.
Worked example
Section titled “Worked example”The finding.raised worked example in the DCP repository shows a real concerns reference: a security finding about the envelope design points at the decision it concerns.
{ "dcp_version": "1.0", "message_id": "msg_01HZ0FINDINGRAIS1", "message_type": "finding.raised", "issued_at": "2026-06-29T10:20:00Z", "correlation_id": "msg_01HZ0REVIEWREQ001", "body": { "event_id": "evt_01HZ0FINDINGRAIS1", "entity_type": "finding", "verb": "raised", "entity_id": "finding_actor_rename", "attributed_to": "security-reviewer", "entity": { "id": "finding_actor_rename", "subject_ref": "review_envelope_design", "severity": "medium", "category": "security", "title": "Rename 'actor' to 'attributed_to' to avoid identity confusion", "status": "resolved" }, "refs": [ { "rel": "concerns", "ref": "decision_thin_envelope" } ] }}A supersedes reference works the same way for a decision that replaces an earlier one:
"refs": [ { "rel": "supersedes", "ref": "decision_thin_envelope" }]Note that Decision also has a dedicated supersedes field of its own in its entity schema, for the common case of one decision explicitly replacing exactly one prior decision. Using rel: "supersedes" inside refs is the general-purpose form, available on any entity’s refs array, and it also allows an event (not just a Decision entity snapshot) to assert the same relationship.
The one rule JSON Schema cannot express
Section titled “The one rule JSON Schema cannot express”As with every part of an Event, remember that refs is only ever carried inside the body of a DcpMessage — and a validator MUST check that the envelope’s message_type equals <body.entity_type>.<body.verb>. On conflict, the body wins and the message is malformed. Pure JSON Schema cannot enforce this cross-field equality; it is a mandatory extra step every conformant implementation adds on top of schema validation (SPEC §4.1).
Related reference pages
Section titled “Related reference pages”For the full shape of the object that carries refs, see the Event Schema Reference. For the eight entity nouns that ref values typically point at, see the Entity Reference. For why ref must never be treated as an address to fetch, see Carries No Trust. For how DCP’s other controlled vocabularies stay open while rel stays closed, see Status Is a Vocabulary, Not a State Machine.