Vultron Protocol Specification
This page holds the whole Vultron Protocol Specification in order, for printing or for searching the whole text at once. The parts and annexes publish the same text one part per page, and the site search indexes those pages rather than this one.
Abstract
The Vultron Protocol coordinates vulnerability disclosure among multiple organizations. Participants exchange asynchronous messages and track the case across four dimensions of shared state, realized as five state machines. Any number of organizations may take part in a single case.
Coordination is scoped to the case rather than to a system: the case-management role is held per case, and different cases may be managed by different actors. No single service holds every case. Within a case, one participant holds write authority over the case's shared state, so that concurrent claims resolve to one answer.
1. Introduction [I]
1.1 Background and Motivation
A vulnerability divides the world into two sets: those who know about it, and those who do not. From the moment of discovery, every party in the knowing set repeatedly answers two questions. What should it do next, and who else needs to know what, and when? Coordinated Vulnerability Disclosure (CVD) continues until the answers are "nothing" and "nobody".
Multi-Party CVD (MPCVD) is CVD involving three or more independent organizations with different interests and roles. The shapes vary. A reporter may go to one vendor, which discovers that other vendors ship the same component. A reporter may go to a coordinator, which finds and notifies the affected vendors. A reporter may go to a vendor and a coordinator at once. What the cases have in common is that the set of participants grows after coordination has begun, each vendor works to its own timeline, and the parties must agree when disclosure happens.
Each of those organizations usually already runs its own vulnerability tracking system. The coordination problem is therefore not only one of people talking to each other; it is one of separate systems needing to stay consistent about a shared case. That is the problem Vultron addresses.
Email carries the content of a CVD case but records none of its state. No participant can see which vendors have read the report, which have agreed to an embargo, or in what order events occurred. Adding a party mid-case means re-sending the history by hand, and nothing detects the gap if that fails. Vultron gives each of these an explicit, machine-checkable representation: report status per participant (§6 Report Management (RM) State Machine), embargo agreement per participant (§9 Participant Embargo Consent (PEC) State Machine), and an ordered case ledger that every participant replicates (§5.4 Addressing and Channels).
Embargo timing is worth noting early. In practice the first two parties to a case establish the initial embargo, and it carries forward as further participants join and agree to it. Establishing terms early is generally easier than negotiating them once many parties are already involved.
The protocol tracks coordination state across four dimensions: the report lifecycle, Report Management (RM); the embargo, Embargo Management (EM); what is known about the vulnerability, Case State (CS); and each participant's agreement to the embargo, Participant Embargo Consent (PEC). Because CS is a compound of two independent axes, an implementation runs five state machines (§3.3 Tracking Dimensions). Messages are ActivityStreams 2.0 Activities delivered over Hypertext Transfer Protocol (HTTP). Delivery is asynchronous: no exchange requires both parties to be available at the same moment.
Who this specification is for
This specification is written for implementers building software that takes part in Vultron cases, and for reviewers assessing whether an implementation conforms.
It assumes familiarity with coordinated vulnerability disclosure as a practice — reporters, vendors, coordinators, embargoes, Common Vulnerabilities and Exposures (CVE) IDs. It does not assume prior knowledge of Vultron. Where it relies on an external specification, it says so and cites it (§1.3 Relationship to Existing Standards).
See also
1.2 Design Goals
Actor-local state. Each participant keeps its own replica of the case, assembled from the messages it has received. A participant is the authority on its own report and fix status. What the case as a whole asserts — its embargo state and what is publicly known — has a single writer, the participant holding the Case Manager role, so that concurrent claims resolve to one answer (§5.4.1 Single-Writer Authority).
Coordination is scoped to a case, not to a system. The Case Manager role is held per case, and different cases may be managed by different actors. The protocol does not require every case to pass through one shared service. It does require, within a case, that shared state have one writer.
Informative: how centralized is this?
Two readings should both be avoided.
Vultron does not require a single central authority holding all cases. Any actor able to satisfy the Case Manager role can manage a case, and the role is transferable (§11.3 Case Ownership Transfer).
Equally, Vultron as it stands is not a fully decentralized design. Within a case there is one writer, and its availability bounds how fast the case can progress. The current reference implementation goes further and runs a dedicated service actor per case.
The protocol leaves more distributed realizations open — a shared ledger among peers, for instance — and this version does not specify one (§5.4.2 Routing Topology).
Asynchronous, message-driven coordination. Participants announce their own transitions rather than being polled, and no exchange requires two parties to be online at once. A participant that is temporarily unreachable does not block the others from making progress.
Extensible role model. Roles are not exclusive: a participant may hold Reporter, Vendor and Coordinator at the same time (§3.5 Participants and Roles). Roles that confer protocol authority are kept separate from roles that describe what an actor does in the world, so the two can vary independently.
Interoperable wire format. ActivityStreams 2.0 is the normative wire vocabulary. Reusing an established open standard means the message format is already specified and already understood, and it leaves ActivityPub-based delivery available as a path to potentially compatible tooling. Vultron has not yet been integrated with the wider ActivityPub ecosystem.
1.3 Relationship to Existing Standards
ActivityStreams 2.0 and ActivityPub. Vultron uses the ActivityStreams 2.0 vocabulary as its wire format, and ActivityPub HTTP delivery is the anticipated transport.
This version requires the vocabulary but not full ActivityPub server behavior: inbox and outbox HTTP delivery, WebFinger discovery and HTTP Signatures are not conformance requirements here. A future version is expected to raise the floor to full ActivityPub for all participants, at which point the vocabulary-only profile may become a compatibility profile. An implementation built against this version should expect to be re-evaluated against that one. This paragraph is the specification's single statement of that roadmap; other sections cite it.
ISO/IEC 29147 and 30111. International Organization for Standardization (ISO) 29147 specifies vulnerability disclosure practices; ISO 30111 specifies vulnerability handling processes. Vultron implements the multi-party coordination layer those standards describe at a high level. The CERT Guide to Coordinated Vulnerability Disclosure provides the practitioner-level complement.
1.4 Document Conventions
This specification uses the key words defined in RFC 2119: MUST, MUST NOT, REQUIRED, SHALL, SHALL NOT, SHOULD, SHOULD NOT, RECOMMENDED, MAY, and OPTIONAL.
Sections marked [N] are normative. Sections marked [I] are informative.
Sections marked [N/I] contain a mix; normative requirements are explicitly
flagged.
State names. This specification writes state names in full, in prose form: Received, Active, Agreed, Fix Ready.
Where the name of a specific enumerated value matters, for example when citing an implementation's enumeration, the value is written in code font with its machine as a prefix, as in RM.RECEIVED or EM.ACTIVE.
The two forms name the same state.
Consent row states and embargo register statuses are written in code font without a prefix (AGREED, PROPOSED); where a word names both an EM state and a register status, the text says which.
Some positions the specification names are reads of state rather than states: a participant is a signatory when its consent to the embargo in force is Agreed (§9.3 Signatory and Lapsed).
Case-state notation. The Case State axes use one letter per condition. A
lowercase letter means the condition is not met; an uppercase letter means it is.
Vfd therefore means the vendor is aware, the fix is not ready, and the fix is
not deployed. An arrow names the transition that flips one letter, so f→F is
the change from "fix not ready" to "fix ready". An asterisk is a wildcard: pX*
matches any state in which p is lowercase and X is uppercase.
2. Terminology [N/I]
Vultron gives several ordinary Coordinated Vulnerability Disclosure (CVD) words a narrower meaning than they carry in general use. The tables in this section state the protocol meaning of each term. Where the protocol meaning differs from common usage, the protocol meaning applies throughout this specification.
This section is the authoritative source for the terms it defines, including the role enumeration in §2.2 Roles. An implementation derives its vocabulary from this specification.
Where a term already has a settled definition in the wider coordinated vulnerability disclosure literature, this specification adopts that definition rather than coining its own, and says where it came from. Vulnerability is such a term.
2.1 Actors, Participants and Cases
These four terms are ordered deliberately: each builds on the one before it.
| Term | Definition |
|---|---|
| Vulnerability | A set of conditions or behaviors that allows the violation of an explicit or implicit security policy. Vulnerabilities are the subject of every Vultron case. |
| Report | A document describing a specific vulnerability, submitted to initiate coordination. |
| Case | The coordination context around a specific vulnerability: the participants, the shared state, the messages exchanged, and any embargo agreement. The case is the unit of Vultron protocol activity. |
| Actor | An identity in the protocol, named by a Uniform Resource Identifier (URI). An actor may be an organization, a person, or a service. Actors exist independently of any case. |
| Participant | An actor with a CaseParticipant record in a specific case, whether or not it has yet joined. The record associates that actor with the roles it holds and the state it owns. An active participant is one that has joined and agreed to the case's current embargo. An actor is a participant in a case; the same actor may participate in many. |
Source of the vulnerability definition
The definition of vulnerability above is taken unchanged from the CERT Guide to Coordinated Vulnerability Disclosure. It is also consistent with ISO/IEC 29147:2018 §5.4, which defines the term in the same policy-violation terms (ISO crosswalks).
Note what the definition does not require: it says nothing about whether the vulnerability is known, reported, fixed or public. Those are properties a Vultron case tracks about a vulnerability (§8 Case State (CS) Dimensions), not part of what makes something one.
2.2 Roles
A role is a position held within a case. It determines what a participant is obliged to do and which transitions it is authorized to cause. Roles are granted through the case's authority chain (§11.1 Role Assignment), not claimed. A participant may hold several at once.
Roles fall into two categories, which §12.3 Role Taxonomy develops further.
Process roles describe what an actor does in a case:
| Role | Definition |
|---|---|
| Reporter | A participant that submitted the report. The protocol is concerned with who reported the vulnerability, not who discovered it; a discoverer who reports holds the Reporter role. |
| Vendor | A participant that produces a product containing the vulnerability and is responsible for developing a fix. |
| Coordinator | A participant that facilitates multi-party coordination without being responsible for developing a fix itself. |
| Deployer | A participant that deploys a vendor's fix to systems it operates. Distinct from Vendor: producing a fix and applying it are separate acts. |
| CVE Numbering Authority (CNA) | A participant authorized to assign CVE IDs directly, rather than delegating assignment to an external service. Orthogonal to the other process roles and commonly held alongside Coordinator or Vendor. |
| Observer | A participant with no obligation to drive vendor/fix/deploy transitions. An Observer still tracks case state, is subject to embargo consent, and may report observations about the world. |
Protocol authority roles describe what an actor controls in the protocol itself:
| Role | Definition |
|---|---|
| Case Owner | The party whose disclosure decision the case exists to serve. It decides who is admitted, which roles they hold, and whether embargo terms are accepted or torn down. Case ownership is never delegated, though it may be transferred (§11.3 Case Ownership Transfer). |
| Case Manager | The participant that writes the canonical case ledger and relays case-scoped messages, acting on the Case Owner's behalf. It is the case's single-writer authority (§5.4.1 Single-Writer Authority). The Case Owner may delegate this role. |
The Observer role and the Case Observer capability set
Observer is a process role (defined above). The minimum conformance capability set that every participant must provide is the Case Observer capability set (§12.2 Capability Sets) — named to echo the least-privileged role, with a Case prefix that keeps the two distinct. They are not the same kind of thing: a role is a position in a case; a capability set is a property of software (§12.3.3 Roles and Capability Sets Are Independent). The same prefix distinguishes the Case Decision and Case Hosting capability sets from the roles they serve.
Informative: the CASE_MANAGER role and the actor that holds it
Authority over a case follows the Case Manager role. It does not follow any actor's name, URI, or hosting location. Normative text in this specification therefore names the authority the CASE_MANAGER, meaning whichever participant currently holds the role.
The reference implementation happens to satisfy the role with an automated software actor it labels a case actor, provisioned by a case actor service. Those are implementation labels with no protocol meaning. An implementation MUST NOT infer authority from them.
Informative: Finder is not a protocol role
Earlier CVD models distinguish the finder of a vulnerability from its reporter. Vultron does not: nothing in the protocol depends on who discovered the vulnerability, so an actor that discovers and reports holds the Reporter role. The discoverer's identity may be recorded in the report content or in a case note.
2.3 Protocol Objects and Messages
These terms name the things a case is made of and the messages that move between participants.
| Term | Definition |
|---|---|
| Activity | The wire form of a Vultron message: an ActivityStreams 2.0 Activity. Every message this specification describes is carried as an Activity (§5 Syntactic Layer — Wire Format). |
| Message | A protocol operation sent from one actor to another. A message states that something has happened; it is not an instruction to the recipient. |
| Message type | One of the operations the protocol defines, each named by a protocol shorthand. RS (Report Submission) and EP (Embargo Proposal) are message types (§4 Semantic Layer — Message Meanings). |
| Protocol shorthand | The two-letter code naming a message type by its meaning rather than its wire form — RS, EP, CV, and so on. Introduced in §4 Semantic Layer — Message Meanings and mapped to wire forms in §5.7 Shorthand to Wire Form Mapping. A reader meeting a shorthand before §4 Semantic Layer — Message Meanings may treat it as an opaque label. |
| Case stub | A minimal description of a case, carrying enough for an invited actor to decide whether to join and no vulnerability detail. Sent with an invitation, before the invitee has been admitted (§11.2 Invitation and Acceptance). |
| Case ledger | The case's authoritative, append-only history. The CASE_MANAGER is its only writer, and every participant holds a replica built from what the CASE_MANAGER sends. |
CaseLedgerEntry |
One entry in the case ledger. Each entry records one admitted change, carries a timestamp assigned by the CASE_MANAGER, and is hash-chained to its predecessor so a participant can tell whether it holds the entries in order and without gaps. |
ParticipantStatus |
A participant's report about its own state, or its observation about the world. A ParticipantStatus is a claim: any participant may write one, and writing one does not change the case's state (§10.3 Status Adoption: The Two-Seam Model). |
CaseStatus |
The case's shared state — its embargo state and what is publicly known. A CaseStatus is canonical: only the CASE_MANAGER writes it, and it changes only when a claim is adopted (§10.3 Status Adoption: The Two-Seam Model). |
| Channel | The path a message travels. In Vultron the delivery path is inbox to inbox, and case-scoped messages route through the CASE_MANAGER (§5.4.2 Routing Topology). |
2.4 State, Embargo and Disclosure
These terms concern what a case knows and what its participants have agreed to. The last two are the pair most often conflated.
| Term | Definition |
|---|---|
| State | The current value of one state machine. Always qualified in this specification — RM state, embargo state, case state — because the unqualified word is ambiguous. |
| Transition | A change from one state to another. |
| Track | To maintain a local value for a state machine and update it as incoming messages report transitions. Every participant tracks all five machines (§12.2 Capability Sets). |
| Drive | To cause a transition that the participant's roles authorize, and announce it so the other participants learn of it. Which transitions a participant may drive depends on its roles (§12.4 Role-Specific Normative Requirements). |
| Embargo | A time-bounded agreement among the participants of a case not to disclose the vulnerability publicly before an agreed point. Tracked for the case by the embargo state machine (§7 Embargo Management (EM) State Machine) and per participant by embargo consent (§9 Participant Embargo Consent (PEC) State Machine). |
| Teardown | Ending an active embargo. The CASE_MANAGER performs the teardown and records it; a participant may report that it intends to exit, or propose an earlier end, but does not end the case's embargo itself (§10.2 Embargo Revision and Termination Cascades). |
| Publication | A deliberate act by a participant that makes vulnerability information publicly available. Publication is something a participant does, and the embargo constrains when a participant may do it. |
| Public awareness | The condition of the vulnerability being known outside the case, however that came about. Public awareness may arise with no participant publishing anything: an attacker may discover the vulnerability independently, a commit may reveal it, or a non-participant may write about it. |
Publication and public awareness are not the same thing
Vultron governs publication — what a participant may deliberately disclose, and when. It cannot govern public awareness, which is a fact about the world that no participant controls.
The distinction is load-bearing because most of the protocol's disclosure logic keys off whether information is public, not off how it became public. An embargo that is overtaken by an independent leak is over as surely as one ended by agreement, and the case state records Public Aware either way (§8.2 PXA — Public Aware, Exploit Public, Attacks Observed).
See also
- Glossary — project-wide terminology, including terms outside this specification's scope
- Formal Protocol Messages
3. Protocol Overview [I]
This section describes the shape of the protocol before the normative sections specify it. It covers how the protocol is modeled, what state it tracks, how that state is divided between individual participants and the case as a whole, and what roles participants hold. Nothing here is normative; each subsection points to the section that carries the requirements.
3.1 Coordination Model
Vultron models a case as a set of communicating state machines — one set per participant — that coordinate only by exchanging messages. No participant reads another's state directly. Each learns about the others from the messages it receives.
A participant's own view is therefore always a replica: its local copy of what it believes the case state to be, assembled from the messages that have reached it. Two participants can hold different views at the same moment, because a message is still in transit or because a participant has not yet reported something.
State divides into two kinds, and the distinction runs through the whole
specification. Participant-specific state belongs to one participant, which
is the authority on its own value. Shared case state has one value for the
entire case, and only the participant holding the CASE_MANAGER role writes it
(§5.4.1 Single-Writer Authority); it replicates each admitted
change to every participant as Announce(CaseLedgerEntry).
| Machine | Ownership | Written by |
|---|---|---|
| Report Management (RM) | Participant-specific | The participant itself |
| Vendor aware, Fix ready, fix Deployed (VFD) | Participant-specific | The participant itself |
| Embargo Management (EM) | Shared case state | The CASE_MANAGER only |
| Public aware, eXploit public, Attacks observed (PXA) | Shared case state | The CASE_MANAGER only |
| Participant Embargo Consent (PEC) | Per-participant | The CASE_MANAGER only |
A participant reports its own state and reports observations about the world. It does not write shared case state, and reporting an observation is not the same as that observation becoming canonical (§10.3 Status Adoption: The Two-Seam Model).
3.2 What a Deployment Looks Like
The protocol is abstract, but a deployment is concrete, and the shape is worth stating plainly before the state machines begin.
Each participating organization runs one Vultron service, which exposes an inbox at
a URI. For each case, one participant additionally holds the CASE_MANAGER role
for that case, and every case-scoped message passes through it: a participant
addresses the message to the CASE_MANAGER, which records it in the case ledger and
then sends the resulting entry to every participant, including the sender.
---
title: Message flow within one case
---
flowchart LR
R["Reporter<br/>(own inbox)"]
V1["Vendor A<br/>(own inbox)"]
V2["Vendor B<br/>(own inbox)"]
CM["Participant holding<br/>CASE_MANAGER for this case<br/>(own inbox + case ledger)"]
R -- "case-scoped message" --> CM
V1 -- "case-scoped message" --> CM
V2 -- "case-scoped message" --> CM
CM -- "Announce(CaseLedgerEntry)" --> R
CM -- "Announce(CaseLedgerEntry)" --> V1
CM -- "Announce(CaseLedgerEntry)" --> V2
Two things this is not:
- It is not a central clearinghouse. The role is held per case. A different case may route through a different organization entirely, and there is no service that sees every case.
- It is not a registry. Nothing looks up authority in a directory; authority follows the role recorded on the case (§5.4.1 Single-Writer Authority).
Participants also talk to each other outside the protocol — by mail, by phone, in a shared channel. That is normal and out of scope. What routes through the CASE_MANAGER is the case's protocol traffic, because that is what changes case state (§5.4.2 Routing Topology).
Messages describe what has happened
A Vultron message states that something has already occurred. It is not an
instruction to the recipient. An RV message means "I received this report
and determined it was valid" — it does not ask the recipient to treat the
report as valid. This holds throughout
§4 Semantic Layer — Message Meanings through §10 Model Interactions and Cascade Rules
and governs how every message description in those sections should be read.
One construct is deliberately request-shaped: a proposal such as an embargo invitation asks for a decision. Even there, the reply reports a decision that has been made; it does not command a transition.
See also
- Formal Protocol Definition — the formal treatment, including the process count and message-set notation
3.3 Tracking Dimensions
The protocol tracks coordination state across four dimensions. Each is a state machine, or a pair of them, with its own states and transitions. The lists below name the states so the rest of this overview can refer to them; the defining sections carry the full definitions.
- Report Management (RM) — the lifecycle of a report inside one participant, from receipt to closure. Participant-specific. Seven states: Start, Received, Invalid, Valid, Deferred, Accepted, Closed (§6 Report Management (RM) State Machine).
- Embargo Management (EM) — the negotiated disclosure timing agreed for the case. Shared case state, computed from the case's embargo register of every embargo proposed on it. Five states: None, Proposed, Active, Revised, Exited (§7 Embargo Management (EM) State Machine).
- Case State (CS) — what is known about the vulnerability. CS is the pair
(VFD, PXA)(§8 Case State (CS) Dimensions). - Participant Embargo Consent (PEC) — each participant's answer to each embargo in the register, one row per embargo. Per-participant, written by the CASE_MANAGER. Five states: Uninvited, Invited, Agreed, Declined, Timed out. Whether a participant is bound, a signatory, is read from its row for the embargo in force (§9 Participant Embargo Consent (PEC) State Machine).
Four dimensions, five state machines
Case State (CS) is a compound of two independent axes, so four tracked dimensions are realized as five state machines:
- VFD — Vendor Aware, Fix Ready, Fix Deployed. Participant-specific: it records what one participant has done (§8.1 VFD — Vendor Aware, Fix Ready, Fix Deployed).
- PXA — Public Aware, Exploit Public, Attacks Observed. Participant-agnostic: it records the state of the world, and it is shared case state (§8.2 PXA — Public Aware, Exploit Public, Attacks Observed).
This specification says "four dimensions" when describing what is tracked and "five state machines" when describing what an implementation must provide. Both counts are correct; they count different things.
RM, EM and CS were present in the original protocol design. PEC emerged during implementation and is fully normative; its provenance is recorded at §9 Participant Embargo Consent (PEC) State Machine.
3.4 How the Dimensions Interact
The four dimensions are coupled. A transition in one creates obligations in others. This subsection summarizes the couplings; §10 Model Interactions and Cascade Rules specifies them.
RM governs what a participant receives. A participant's RM state determines what it is obliged to do and what the CASE_MANAGER may send it. Full case content is withheld until the participant has been admitted — recorded in the case's participant roster with RM state Received — and its embargo consent has been resolved (§9.7 Gating Full Case Delivery).
EM gates disclosure. While an embargo is in force, participants defer publication. An embargo ends by teardown: the CASE_MANAGER terminates it and records the termination. A participant may report that it intends to exit, or propose a shorter embargo or an earlier end date, but a participant does not end the case's embargo by itself — EM is shared case state (§7.2 Transitions and Guards, §10.2 Embargo Revision and Termination Cascades).
CS records observation, not decision. A VFD transition is a fact about what one participant has done. A PXA transition is a fact about the world. Any participant may report a PXA observation; whether that report becomes canonical case state is decided separately (§10.3 Status Adoption: The Two-Seam Model).
EM and PEC answer different questions. EM says whether the case has an embargo. PEC says whether a given participant is bound by it. The two can disagree: a case at EM Active may hold a participant whose row for the embargo in force is not Agreed, if that participant has been invited but has not yet joined, or declined the terms. Cascade rules keep them consistent (§10.2 Embargo Revision and Termination Cascades).
3.5 Participants and Roles
An actor is an identity in the protocol — an organization, a person, or a service, named by a URI. An actor that joins a case becomes a participant in that case, and the case's record of that participant associates it with a set of roles. Roles determine what a participant is obliged to do and which transitions it is authorized to cause.
Roles are not exclusive. A participant may hold Reporter, Vendor and Coordinator at once. A vendor that discovers a vulnerability in its own product is both Reporter and Vendor.
Participant count is not role count. Every participant runs all five state machines regardless of which roles it holds. Roles govern which transitions it may cause, not which machines it maintains.
Two categories of role apply. §12.3 Role Taxonomy gives the full taxonomy.
- Process roles — what an actor does in a case: Reporter, Vendor, Coordinator, Deployer, CVE Numbering Authority (CNA), Observer.
- Protocol authority roles — what an actor controls in the protocol itself: Case Owner and Case Manager. These confer specific rights within a case, independent of what other actors do.
The distinction between the two authority roles matters throughout:
- The Case Owner is the party whose disclosure decision the case exists to serve. It decides who is admitted, which roles they hold, and whether embargo terms are accepted or torn down. Case ownership is never delegated, though it may be transferred (§11.3 Case Ownership Transfer).
- The Case Manager is the participant holding the
CASE_MANAGERrole. It writes the canonical case ledger, relays case-scoped messages, and acts on the Case Owner's behalf. The role cannot be granted by a role offer (§11.1 Role Assignment); how it is handed to another actor is not yet specified.
Roles are assigned through the case's authority chain, not claimed. An actor does not acquire a case role by asserting that it holds one (§11.1 Role Assignment).
4. Semantic Layer — Message Meanings [N]
This section defines what each protocol message means. It says nothing about how a message is encoded; §5 Syntactic Layer — Wire Format specifies that, and §5.7 Shorthand to Wire Form Mapping gives the mapping between the two.
Messages are named by two-letter shorthands, grouped by the state machine they
concern: R* for report management, E* for embargo management, C* for case
state. A shorthand names a meaning, not a wire format. Several shorthands share one
wire form, so the mapping is not one-to-one in either direction.
Every message reports something that has already happened. A message does not instruct its recipient to make a transition; it tells the recipient that the sender has made one, or that the sender has observed something (§3.1 Coordination Model).
Six of the message types the formal protocol defines have no wire representation in this version. Each table below marks them, and §4.6 Error and Acknowledgment Messages explains why they are absent rather than merely unimplemented.
4.1 Report Management Messages
These eight messages concern one participant's handling of a report. Each reports a change to the sender's report management state (§6 Report Management (RM) State Machine).
| Shorthand | Name | Meaning | On the wire? |
|---|---|---|---|
RS |
Report Submission | The sender is sending a report to a new participant | yes |
RI |
Report Invalid | The sender assessed the report as invalid | yes |
RV |
Report Valid | The sender assessed the report as valid | yes |
RD |
Report/Case Deferred | The sender is deferring further action; a case-participation decision | yes |
RA |
Report/Case Accepted | The sender accepted the report/case for active work; a case-participation decision | yes |
RC |
Report Closed | The sender closed the report | yes |
RK |
Report Acknowledgment | The sender received the report submission | yes |
RE |
Report Error | The sender received an unexpected report message | no (§4.6 Error and Acknowledgment Messages) |
Only RS drives a state change in its receiver: receiving a report moves the
receiver to Received. The other seven tell the receiver something about the sender
and leave the receiver's own state alone.
RK acknowledges the submission, not the reading
RK reports that the submission arrived. It does not report that the recipient
has read the report, assessed it, or agreed to work on it — those are RV,
RI, RA and RD.
The protocol defines no message meaning "I have read the report". An
implementation MUST NOT infer from RK that the recipient has examined the
content, and MUST NOT treat RK as a substitute for a report management state
message.
4.2 Embargo Management Messages
These nine messages negotiate the case's embargo (§7 Embargo Management (EM) State Machine). They fall into three groups: proposing an embargo, revising one already in force, and ending one.
| Shorthand | Name | Meaning | On the wire? |
|---|---|---|---|
EP |
Embargo Proposal | The sender proposes embargo terms | yes |
ER |
Embargo Proposal Rejection | The sender rejects a proposal | yes |
EA |
Embargo Proposal Acceptance | The sender accepts a proposal | yes |
EV |
Embargo Revision Proposal | The sender proposes changing terms already in force | yes |
EJ |
Embargo Revision Rejection | The sender rejects a proposed revision | yes |
EC |
Embargo Revision Acceptance | The sender accepts a proposed revision | yes |
ET |
Embargo Termination | The sender ends the embargo, effective immediately | yes |
EK |
Embargo Acknowledgment | The sender received an embargo message | no (§4.6 Error and Acknowledgment Messages) |
EE |
Embargo Error | The sender received an unexpected embargo message | no (§4.6 Error and Acknowledgment Messages) |
The initial-proposal group (EP, ER, EA) and the revision group (EV, EJ,
EC) are distinct meanings. The distinction matters because the outcome of a
rejection differs: rejecting an initial proposal leaves the case with no embargo,
while rejecting a revision leaves the previously agreed terms in force
(§7.2 Transitions and Guards).
The revision shorthands are indistinguishable on the wire
EV, EJ and EC share their wire forms with EP, ER and EA
respectively. The distinction is not encoded in the message.
An implementation MUST determine which it has received from its own embargo state. If the case embargo is Active or Revised, an arriving proposal is a revision. If it is None or Proposed, the same message is an initial proposal.
An implementation that treats the wire form as self-describing will mishandle every revision. Because a mishandled revision rejection returns the case to None, it will silently drop embargoes that every participant still believes are in force.
Choosing between EV and ET. ET takes effect immediately. A sender that
wants the embargo to end at some future point SHOULD send EV proposing that end
date rather than ET.
Tacit acceptance of a receiver's default embargo policy is specified at §7.2 Transitions and Guards: submitting a report without proposing terms accepts the receiver's default. Where the receiver has published no default, the same section specifies a short protocol default, so an embargo-eligible case never begins without an embargo.
4.3 Case State Messages
These messages report facts about the vulnerability (§8 Case State (CS) Dimensions). The first three concern what a specific participant has done; the next three concern the state of the world.
| Shorthand | Name | Axis | Meaning | On the wire? |
|---|---|---|---|---|
CV |
Vendor Awareness | VFD | A vendor has been made aware of the vulnerability | yes |
CF |
Fix Readiness | VFD | A vendor has a fix ready | yes |
CD |
Fix Deployment | VFD | A fix has been deployed | yes |
CP |
Public Awareness | PXA | The vulnerability is publicly known | yes |
CX |
Exploit Public | PXA | An exploit has been published | yes |
CA |
Attacks Observed | PXA | Attacks exploiting the vulnerability have been seen | yes |
CK |
Case State Acknowledgment | — | The sender received a case state message | no (§4.6 Error and Acknowledgment Messages) |
CE |
Case State Error | — | The sender received an unexpected case state message | no (§4.6 Error and Acknowledgment Messages) |
All six reporting shorthands share a single wire form. Which fact is being reported
travels in the message's payload rather than in its type, so an implementation that
dispatches on message type alone cannot tell CF from CA
(§5.7 Shorthand to Wire Form Mapping).
The three VFD messages report the sender's own progress, so receiving one updates the receiver's model of the sender rather than the receiver's own state (§8.4 Receiving CS Messages: Own State vs. Model of Others). The three PXA messages report a claim about the world, which becomes canonical case state only if the CASE_MANAGER adopts it (§10.3 Status Adoption: The Two-Seam Model).
4.4 Case Coordination Operations
Alongside the state machine messages, the protocol defines the operations that create a case and manage who is in it. These have no two-letter shorthands, because they concern the case as an object rather than any machine's state.
| Operation | Meaning |
|---|---|
| Case creation | An actor establishes a case for a vulnerability, and with it the case's authority chain (§4.5 Trust and Bootstrap Semantics) |
| Invitation | The CASE_MANAGER invites an actor to join, on the Case Owner's behalf; the actor accepts or declines (§11.2 Invitation and Acceptance) |
| Actor suggestion | A participant recommends that some actor be brought into the case; the Case Owner decides whether to invite it (§11.2 Invitation and Acceptance) |
| Role offer | The Case Owner offers a role to an actor, which accepts or declines (§11.1 Role Assignment) |
| Ownership transfer | The Case Owner offers ownership of the case to another actor (§11.3 Case Ownership Transfer) |
| Ledger replication | The CASE_MANAGER sends each committed ledger entry to every participant. This is the only way a participant learns of an admitted change to shared case state |
| Case delivery | The CASE_MANAGER sends a participant the full case, once that participant is admitted and its embargo consent is resolved (§9.7 Gating Full Case Delivery) |
| Case metadata update | The CASE_MANAGER records a change to the case's own attributes |
The wire form of each is given at §5.3 Activity Types and Canonical Message Forms.
4.5 Trust and Bootstrap Semantics
A case begins before there is a case. The reporter's first message goes to a vendor directly, because no case exists and so no CASE_MANAGER exists to route through. Every later message routes through the CASE_MANAGER (§5.4.2 Routing Topology). The transition between those two regimes is the bootstrap, and it is where a case's trust is established.
The case-creation message is the trust root. When a case is created for a report,
the receiving party sends the reporter a case-creation message signed by the receiving
party itself, the actor the reporter chose. That message introduces the actor holding
the CASE_MANAGER role, which may be the receiving party or another actor it delegated
case maintenance to. The reporter trusts the introduction because it came from the actor
the reporter addressed, and never because a case snapshot names its own sender. Every
participant's trust in the ledger derives from it: a participant accepts entries
from the CASE_MANAGER because it accepts the message that established which actor
holds the role.
A late joiner inherits that trust transitively. An actor invited after the case is under way never sees the original creation message. It receives the case snapshot and the prior ledger entries from the CASE_MANAGER (§10.1 Admitting a Participant), and it trusts them because it trusts the invitation that named the CASE_MANAGER. The hash-chaining of the ledger lets the joiner verify it received an unbroken history — a check against tampering and omission, not a second source of trust.
Messages that arrive before their case. A case-scoped message may reach a participant before the case it refers to has. A participant MUST NOT apply such a message to a case it has not yet received. It holds the message until the case arrives, and discards it if the case never does. Applying it early would create case state from a message whose context was never established.
The threat model, identity verification, and the limits of this trust chain are covered at §14.1 Trust Model.
4.6 Error and Acknowledgment Messages
The formal protocol defines an acknowledgment and an error message for each of the
three state machines: RK, EK, CK and RE, EE, CE. Their status here is
not uniform, and an implementation MUST NOT assume symmetry across the three.
Of the six, only RK exists on the wire. EK, CK, RE, EE and CE have no
wire representation. An implementation MUST NOT expect to receive any of the five,
and MUST NOT wait on one.
The two absences have different reasons.
EK and CK are redundant. Embargo and case state changes are replicated
through the case ledger, and a participant establishes that it received them by
holding an unbroken hash chain. A per-message acknowledgment would restate what
the chain already shows. RK survives because report submission happens before the
case exists, so there is no ledger to carry it.
RE, EE and CE are deliberately unmodeled, not merely unimplemented. The
protocol does not model inbound error messages typed by the state machine that
produced them, because that grouping tells a receiver nothing it can act on:
knowing which machine was confused does not indicate what to do about it. Errors
are grouped by failure mode instead, which is actionable:
| Failure mode | When it applies | What the sender can do |
|---|---|---|
| Not understood | The message could not be parsed, or its meaning is unrecognized | Re-send, or send a simpler equivalent |
| Understood but declined | The message was well-formed and meaningful, but the receiver will not act on it given the case's state | Reconsider; the state, not the message, is the obstacle |
| Needs explanation | The condition cannot be expressed as a structured message and requires prose | Read the explanation; there is no automatic recovery |
§5.3 Activity Types and Canonical Message Forms gives the wire form of each failure mode.
What is still missing. A message that cannot be processed is currently set aside for administrative attention, and its sender is not told. The three failure modes give a receiver a way to report a problem where it is implemented, but this version does not require it to.
Open question: negative-acknowledgment semantics are unspecified
Error message types (RE, EE, CE) are deliberately unmodeled
(ADR-0049). Unprocessable inbound messages are dead-lettered with no sender
notification. The three-way fault partition
(§4.6 Error and Acknowledgment Messages, ADR-0083) provides
structured error signaling where it is implemented, but whether the
protocol needs a normative negative-acknowledgment facet — and if so,
what the expected sender behavior (retry, suppress, escalate) should be —
remains open.
4.7 Knowledge Model and Actor Isolation
Each participant knows only what it has been told. This subsection states what follows from that, including why a recipient must be able to obtain every object a message names (§5.5 Serialization).
What a participant knows versus what is true. A participant's replica reflects the messages it has received. Two participants in the same case may hold different views at the same moment, because a message is in transit or because a participant has not yet reported a change. The protocol does not require every participant to agree at every instant. It requires that each converge on the case's canonical state as ledger entries reach it.
No participant reads another's state. An implementation MUST NOT read or write another actor's state directly. Everything it knows about another participant MUST be derived from a message that participant sent, or from the canonical ledger. This is what makes a participant's knowledge independent of any other participant being reachable.
A recipient can obtain what a message names. A participant MUST NOT assume a recipient holds an object it was never sent (AKM-02-001). An object the recipient cannot be expected to hold is carried inline, or by a reference the recipient can dereference (AKM-02-002). Either form satisfies the protocol.
5. Syntactic Layer — Wire Format [N]
This section specifies how a Vultron message is encoded and delivered. Where §4 Semantic Layer — Message Meanings says what a message means, this section says what it looks like.
Vultron does not define its own message syntax. It uses ActivityStreams 2.0, a published World Wide Web Consortium (W3C) vocabulary for describing things that actors do, and extends it with the object types a vulnerability case needs. Reusing an existing vocabulary means the outer envelope is already specified, already tooled, and already understood by readers who have met it elsewhere.
The consequence for a reader is that Vultron messages look generic. There is no
ProposeEmbargo verb; there is an Invite whose object happens to be an embargo
event. Meaning comes from the combination of verb and object, not from the verb
alone, which is why §5.7 Shorthand to Wire Form Mapping is needed to
connect the two layers.
5.1 Base Vocabulary
Every Vultron message is an ActivityStreams 2.0 Activity: an object describing an action, with the actor who performed it and the thing it was performed on.
Four fields are required on every Activity:
| Field | Contains |
|---|---|
type |
The Activity type — the verb, such as Offer or Announce |
actor |
The Uniform Resource Identifier (URI) of the actor that performed the action |
object |
The thing acted on, inline or by reference (§5.5 Serialization) |
id |
A URI uniquely identifying this Activity, used for deduplication (§14.3 Replay and Idempotency) |
Vultron's own types are declared in the vocabulary namespace
https://certcc.github.io/Vultron/ns.
An implementation MUST use the Vultron ActivityStreams vocabulary for message structure.
This version does not require full ActivityPub server behavior. Inbox and outbox Hypertext Transfer Protocol (HTTP) delivery, WebFinger discovery and HTTP Signatures are not conformance requirements here.
Informative: ActivityPub conformance is not required by this version
A future version is expected to raise the floor to ActivityPub; §1.3 Relationship to Existing Standards states the roadmap.
5.2 Object Types
An Activity's meaning depends on what it acts on, so the object types matter as much as the verbs. Vultron defines eight, and they are not a flat set: a case contains participants, a report, status records, and an ordered ledger. That containment is the mental model the rest of this specification assumes.
---
title: Vultron object containment
---
classDiagram
VulnerabilityCase "1" *-- "1..*" CaseParticipant
VulnerabilityCase "1" *-- "0..*" VulnerabilityReport
VulnerabilityCase "1" *-- "1" CaseStatus
VulnerabilityCase "1" *-- "0..*" CaseLedgerEntry
VulnerabilityCase "1" *-- "0..*" EmbargoEvent
CaseParticipant "1" *-- "0..*" ParticipantStatus
CaseParticipant "1" o-- "0..*" CaseParticipantRole
CaseProposal ..> VulnerabilityCase : requests creation of
| Object | What it is |
|---|---|
VulnerabilityCase |
The case itself: the container for everything below |
VulnerabilityReport |
The report artifact that started the case |
CaseParticipant |
An actor's membership in a case, carrying the roles it holds and the state it owns |
CaseParticipantRole |
A role being offered to an actor in the context of a case. A distinct type so that offering a role is structurally different from offering ownership of the case |
EmbargoEvent |
An embargo's terms — proposed, accepted, revised or terminated. A subtype of the ActivityStreams Event type, which is why embargo messages appear on the wire as invitations to an event rather than under an embargo-specific verb |
CaseLedgerEntry |
One entry in the canonical case ledger (§2.3 Protocol Objects and Messages) |
CaseProposal |
A request that some actor create and manage a case. Used when the requesting actor does not intend to manage the case itself; the actor that accepts becomes the case's creator |
CaseStatus / ParticipantStatus |
Status records — see below |
Status records: claim versus canonical. These two are the most easily confused types in the protocol, and the difference is one of authority rather than content.
- A
ParticipantStatusis a claim. It carries what one participant reports about its own state, or observes about the world. Any participant may write one, and writing one does not change what the case asserts. - A
CaseStatusis canonical. It carries what the case asserts: its embargo state and what is publicly known. Only the CASE_MANAGER writes it (§5.4.1 Single-Writer Authority).
A claim becomes canonical only when the CASE_MANAGER adopts it, and adoption is an
authorization decision with a named default rather than an automatic consequence of
receipt (§10.3 Status Adoption: The Two-Seam Model). An
implementation MUST NOT substitute one type for the other: a participant that
emits CaseStatus is asserting authority it does not hold, and a CASE_MANAGER that
treats an inbound ParticipantStatus as canonical has skipped the decision.
Informative: which types have core models
CaseProposal and the fault object of
§5.3 Activity Types and Canonical Message Forms exist in the
reference implementation only as wire types, with no separate internal model.
Every other type above has both.
5.3 Activity Types and Canonical Message Forms
Vultron uses ActivityStreams verbs rather than defining its own. Thirteen are in use:
Create, Offer, Accept, Reject, TentativeReject, Announce, Update,
Add, Remove, Invite, Read, Join, Ignore.
Because the verbs are generic, meaning often comes from nesting: an Activity
whose object is itself an Activity. Accepting an embargo, for instance, is an
Accept whose object is the Invite being accepted, which in turn carries the
embargo event. The nesting is what distinguishes accepting an embargo from
accepting a report.
{
"@context": "https://certcc.github.io/Vultron/ns/context.jsonld",
"id": "https://vendor.example/activities/8f2c",
"type": "Accept",
"actor": "https://vendor.example/actors/vendor",
"object": {
"id": "https://coord.example/activities/4a19",
"type": "Invite",
"actor": "https://coord.example/actors/case-manager",
"context": { "type": "VulnerabilityCase", "id": "https://coord.example/cases/2026-0042" },
"object": {
"type": "EmbargoEvent",
"endTime": "2026-11-01T00:00:00Z"
}
}
}
The two ways an actor joins a case are different messages. Conflating them loses a round-trip in which the Case Owner decides:
Invite, with the case stub as its target, is the CASE_MANAGER inviting an actor to join on the Case Owner's behalf. It is answered withAcceptorReject.Offer, with aCaseParticipantas its object, is a participant recommending an actor. It is answered by the Case Owner, which may then cause anInviteto be sent. An implementation MUST NOT treat it as an invitation to the recommended actor.
Both paths end in an Accept of an Invite
(§11.2 Invitation and Acceptance).
That Accept joins the case; it is not RV. A participant that joined this way never saw the
report offer, so it judges the case by answering a second Invite that has the case itself
as its target, the full-case Invite, and that reply is RV, RI or RC
(VAM-04-011, CM-11-020).
Fault reporting. The three failure modes of §4.6 Error and Acknowledgment Messages have these wire forms:
| Failure mode | Wire form |
|---|---|
| Not understood | Create of a ProcessingFault object |
| Understood but declined | Reject of the message being declined |
| Needs explanation | Create of a Note, or Add of a Note to the case |
5.4 Addressing and Channels
An actor is identified by a URI, and that URI is also where it receives messages. Each actor exposes an inbox to receive Activities and an outbox from which it sends them. An actor's URI is its identity for every purpose in the protocol: there is no separate registry, and no name or hosting location carries authority (§2.2 Roles).
Delivery is therefore inbox to inbox. Which inbox a message may go to, however, is constrained — and that constraint is the subject of the next two subsections.
5.4.1 Single-Writer Authority
The participant holding the CASE_MANAGER role is the only entity authorized to
write the case's shared state: the PXA axis, the embargo state, the embargo record,
and the case ledger. No participant and no handler may write shared case state
directly; every such change MUST be made by the CASE_MANAGER.
The participant-specific axes — report management and VFD — are owned by each
participant's own CaseParticipant record and are explicitly not subject to
this restriction. A participant is the authority on its own report management and
VFD state.
This rule exists to make concurrent claims resolvable. Two participants may report contradictory observations about the world at the same moment; with one writer, the case has one answer, and the decision about which claim becomes that answer is explicit (§10.3 Status Adoption: The Two-Seam Model) rather than a race. The routing rule below follows from it.
5.4.2 Routing Topology
Once a case exists, every case-scoped message follows this path:
Participant → CASE_MANAGER → CaseLedgerEntry → Announce(CaseLedgerEntry) → all Participants
- A participant MUST address case-scoped Activities to the CASE_MANAGER.
- The CASE_MANAGER MUST record a case-scoped message in the case ledger before sending it on to the other participants.
Announceof aCaseLedgerEntryis the only mechanism by which a participant learns of an admitted change to shared case state.
There are exactly two exceptions, both confined to case bootstrap, both occurring before a CASE_MANAGER exists to route through (§4.5 Trust and Bootstrap Semantics):
- Pre-case report submission — the reporter sends the report directly to the
vendor. No case exists, and therefore no case actor service has provisioned an
actor to hold the
CASE_MANAGERrole. - Case creation handshake — the receiving party sends the case-creation message back to the reporter as itself, introducing the actor that holds the role. When that actor is not the receiving party, its first message to the reporter is a later sync, which the reporter holds if it arrives before the introduction.
This is a routing rule, not a prohibition on talking
The rule above scopes Vultron protocol messages about a case. It does not say that participants may not communicate with each other, and the protocol has no way to enforce such a thing. Participants routinely talk directly — by mail, by phone, in a shared channel — and that is outside the protocol's scope.
What the rule means is narrower: a case-scoped Vultron Activity delivered directly from one participant to another is not part of the protocol, and a receiving implementation has no obligation to apply it. Case state changes through the ledger or not at all.
Informative: why route through one writer
Routing through a single writer avoids the coordination cost of agreeing an order among peers, while still leaving each participant its own replica and its own view. The cost is that the CASE_MANAGER is a single point of coordination authority for the case, and its availability bounds how fast that case can progress.
This is scoped per case. The role is held for one case at a time, different cases may be managed by different actors, and the role is transferable (§11.3 Case Ownership Transfer). The current reference implementation goes further than the protocol requires and provisions a dedicated service actor per case.
The protocol deliberately leaves more decentralized realizations open — a ledger shared among peers, for instance, which would remove the single point of authority at the cost of needing agreement on ordering. This version does not specify one, and no implementation of one exists.
5.5 Serialization
Vultron messages are serialized as JavaScript Object Notation for Linked Data (JSON-LD). JSON-LD is JavaScript Object Notation (JSON) with a context that maps short names to full URIs, so a message is readable as ordinary JSON while remaining unambiguous about which vocabulary each term comes from.
An outbound message MUST set @context to the Vultron JSON-LD context document
URI:
{
"@context": "https://certcc.github.io/Vultron/ns/context.jsonld",
"id": "https://coord.example/activities/1b7e",
"type": "Announce",
"actor": "https://coord.example/actors/case-manager",
"object": {
"type": "CaseLedgerEntry",
"logIndex": 12,
"contentHash": "sha256:9f2a...",
"predecessorHash": "sha256:41c8..."
}
}
That context document imports the ActivityStreams 2.0 namespace and declares every Vultron type name, so an implementation cites only the Vultron URI and does not need to declare the ActivityStreams namespace separately.
Objects travel inline or by reference. As ActivityStreams 2.0 allows, an Activity MAY carry an object inline or name it by URI, and a receiver MUST cope with either form (SE-01-002). An actor that names by reference an object its recipient cannot be expected to hold MUST use a reference the recipient can dereference (AKM-02-002, §4.7 Knowledge Model and Actor Isolation). A production implementation MUST be able to fetch an object named by a URI it holds no record of (AKM-05-004).
A case ledger entry records the asserted activity as it was sent. Its payload snapshot is the received body of an activity the Case Manager received, or the body it delivered for one it sent itself (CLP-07-011). Each nested object stays as the asserting activity carried it: inline where its sender inlined it, by reference where its sender named an object every replica already holds (CLP-07-006). The Case Manager never fills a reference in, because the entry hash covers the snapshot and nothing outside it, so a filled-in snapshot would record the Case Manager's reconstruction rather than what was sent.
The reference implementation inlines every object
The reference implementation does not yet dereference, so it carries every object inline and refuses a bare URI where it needs the object (AKM-03-003). That is a shortcut of the prototype, not a Vultron rule.
Provisional namespace URI
The namespace is currently hosted on GitHub Pages. A permanent URI may be registered in a future version (§13 IANA and Namespace Considerations).
5.6 Transport Layer [N/I]
The transport layer moves a serialized message from one actor to another. The message schema of §5.1 Base Vocabulary through §5.5 Serialization is transport-agnostic: the same JSON payload is deliverable over any conformant transport.
This version recognizes one required transport profile and describes a second that is anticipated.
REST (HTTP) profile [N]
This is the transport an implementation MUST provide.
- Each actor exposes an inbox endpoint that accepts inbound Activities.
- An outbound Activity is delivered by HTTP POST to the recipient's inbox.
- Authentication and authorization requirements are at §14 Security Considerations.
Open question: which HTTP responses a receiving actor returns
This version does not say which HTTP status code a receiving actor returns when it refuses a message. For example, it does not say whether an invalid activity gets 422, an activity addressed only to other actors gets 400, or a message that cannot be decrypted gets 400.
One option is for the protocol to specify these responses, so that a sending actor can rely on them and act on them. The other is to leave them to each server implementation. A Vultron actor built on a stock ActivityPub server returns whatever that server returns, so a protocol rule could conflict with the server it runs on.
Until this is settled, these codes are recorded as the reference implementation's choices, not as protocol requirements. The codes that HTTP itself defines, such as 405 for an unsupported method and 413 for a body that is too large, are not part of this question.
ActivityPub federation profile [I]
Full ActivityPub conformance is not required by this version. The roadmap is at §1.3 Relationship to Existing Standards; Annex E Relationship to ActivityPub describes where Vultron follows ActivityPub conventions and where it diverges.
Participant discovery [I]
Before two actors can exchange messages they must locate each other. WebFinger is the anticipated mechanism, consistent with its use alongside ActivityPub in federated systems. This version does not specify participant discovery.
Transport is not routing
The routing rule of §5.4.2 Routing Topology is a protocol rule and applies regardless of which transport carries the messages. The transport is responsible for getting a message to an inbox. The protocol is responsible for which inbox it may go to.
5.7 Shorthand to Wire Form Mapping
This is the normative mapping from the protocol shorthands of §4 Semantic Layer — Message Meanings to their wire forms. It appears here rather than in §4 because it depends on both the shorthands and the object types of §5.2 Object Types.
| Shorthand | Wire form |
|---|---|
RS |
Offer of a VulnerabilityReport |
RI |
TentativeReject of the report offer, or of the full-case Invite (VAM-04-013) |
RV |
Accept of the report offer, or of the full-case Invite (VAM-04-012) |
RD |
Ignore of the VulnerabilityCase |
RA |
Join of the VulnerabilityCase |
RC |
Reject of the report offer, of the stub Invite (CM-11-007), or of the full-case Invite (VAM-04-014) |
RK |
Read of the report offer |
EP, EV |
Invite to an EmbargoEvent, in the context of the case |
EA, EC |
Accept of that Invite |
ER, EJ |
Reject of that Invite |
ET |
Remove of the EmbargoEvent |
CV–CA |
Add of a status record to the case — see below |
RE, EE, CE, EK, CK |
none (§4.6 Error and Acknowledgment Messages) |
The status messages depend on who is sending. The six case state shorthands do not have one wire form; they have two, and which applies is determined by the sender's authority:
- A participant reporting its own VFD progress, or an observation about the
world, sends
Addof aParticipantStatus. This is a claim. - The CASE_MANAGER, having adopted a claim, sends
Addof aCaseStatustargeting the case. This is the canonical write.
A participant MUST NOT send Add of a CaseStatus
(§5.4.1 Single-Writer Authority). An implementation that maps all
six shorthands to CaseStatus regardless of sender will have participants
asserting authority they do not hold.
Two collision classes exist. In both, the wire form alone is insufficient and an implementation MUST use additional information to disambiguate:
| Collision | Disambiguate by |
|---|---|
| The revision shorthands share wire forms with their initial-proposal counterparts | The local embargo state (§4.2 Embargo Management Messages) |
| All six case state shorthands share one verb | The status record's payload (§4.3 Case State Messages) |
6. Report Management (RM) State Machine [N]
Report Management is the first of the five state machines, and the one every case starts with: before there is an embargo to negotiate or a fix to track, someone has received a report and has to decide what to do with it. This section specifies the states that decision moves through and the transitions between them.
Behavioral Layer — §6 through §11
§6 Report Management (RM) State Machine through §11 Participant Lifecycle Within a Case together specify the five state machines of the Vultron protocol: RM (§6 Report Management (RM) State Machine), EM (§7 Embargo Management (EM) State Machine), VFD and PXA (§8 Case State (CS) Dimensions, together the CS dimension), and PEC (§9 Participant Embargo Consent (PEC) State Machine). Model interactions and cascade rules are in §10 Model Interactions and Cascade Rules; the participant lifecycle is in §11 Participant Lifecycle Within a Case.
On scope: PEC. The Participant Embargo Consent machine was not part of the original four-machine design. It is fully normative here; its provenance is recorded at its definition site (§9 Participant Embargo Consent (PEC) State Machine).
Two conventions apply throughout §6 Report Management (RM) State Machine through §11 Participant Lifecycle Within a Case:
- A participant maintains its own state and a model of other participants' states. Where a transition rule applies to one and not the other, this is stated explicitly (§8.4 Receiving CS Messages: Own State vs. Model of Others).
- Transitions listed without a named trigger are driven by the corresponding protocol message from §4 Semantic Layer — Message Meanings.
6.1 States
Report Management tracks one report inside one participant, from the moment it arrives to the moment that participant is finished with it. Every participant runs its own RM machine and is the authority on its own value: two participants working the same case are routinely at different RM states, and that is expected rather than a disagreement to reconcile.
| State | Meaning |
|---|---|
| Start | No report has been received |
| Received | A report has arrived; triage is not yet complete |
| Invalid | This participant assessed the report as invalid |
| Valid | This participant assessed the report as valid |
| Deferred | This participant is deferring further action |
| Accepted | This participant accepted the report for active work |
| Closed | This participant closed the report |
The states divide into three groups. Start and Received precede any assessment. Invalid and Valid record the outcome of triage. Deferred, Accepted and Closed record what the participant decided to do about a report it considers valid — except that Received and Invalid can also be closed directly.
See also
6.2 Transitions and Guards
A participant drives its own RM transitions. Each transition below is a participant-level state change: it records something that participant did, and it does not change any other participant's RM state.
---
title: Report Management State Diagram
---
stateDiagram-v2
direction LR
S: Start
R: Received
I: Invalid
V: Valid
D: Deferred
A: Accepted
C: Closed
[*] --> S
S --> R : receive
R --> I: invalidate
R --> V : validate
I --> V : validate
V --> A : accept
V --> D : defer
A --> D : defer
D --> A : accept
R --> C : close
A --> C : close
D --> C : close
I --> C : close
C --> [*]
| From | Trigger | Message | To |
|---|---|---|---|
| Start | receive | receipt of RS |
Received |
| Received | validate | RV |
Valid |
| Received | invalidate | RI |
Invalid |
| Received | close | RC |
Closed |
| Invalid | validate | RV |
Valid |
| Valid | accept | RA |
Accepted |
| Valid | defer | RD |
Deferred |
| Deferred | accept | RA |
Accepted |
| Accepted | defer | RD |
Deferred |
| Invalid | close | RC |
Closed |
| Deferred | close | RC |
Closed |
| Accepted | close | RC |
Closed |
These twelve transitions are the complete set. Three consequences are worth stating, because each is a plausible assumption that does not hold:
- A report cannot be closed from every state. Only Received, Invalid, Deferred and Accepted are closable.
A participant at Start or Valid MUST reach one of those states before it can close.
A
Rejectsent from Received is the Received → Closed transition, and a participant at Valid that leaves a case is recorded as Valid → Deferred → Closed. - Valid does not return to Invalid. Invalidation is available only from Received. Once a participant has assessed a report as valid it does not re-invalidate it; if it decides to stop work, it defers or closes.
- Accepted and Deferred are mutually reachable. A participant may defer work it had accepted and accept work it had deferred, without passing through Valid again.
See also
6.3 Per-Participant RM Tracking
Each participant tracks its own RM state independently; a participant is the authority on its own RM state (§5.4.1 Single-Writer Authority).
Received is the entry state for a participant joining a case, reached by several paths:
- Invited participant — the CASE_MANAGER creates an inert participant record at RM Received when it sends the stub
Invite(CM-11-006).Accept(Invite)activates the inert record, recording willingness to join and, where an embargo is active, consent to it.Reject(Invite)closes the record (RM Received → Closed) and retains it as history (CM-11-007). Neither transition constitutes validation of the report: the invitee has seen only a case stub at that point. After the participant joins, the CASE_MANAGER sends the case and the ledger, then a second Invite, the full-case Invite, carrying its ledger position as a floor (CM-11-010). The participant judges the case by answering it, with its own ledger position:Accept(RV, Received → Valid),TentativeReject(RI, Received → Invalid) orReject(RC, Received → Closed) (CM-11-011). A reply behind the floor, or naming a ledger entry the CASE_MANAGER does not hold, is refused (CM-11-012). - Direct report recipient — on receiving a report.
- Case proposal recipient — on receiving a
CaseProposal.
The triage cycle (RECEIVED → VALID | INVALID → ACCEPTED | DEFERRED) is a
distinct subsequent step that the participant runs after the full case
replica has been delivered to it.
Two Invites, two different acts
Joining a case and judging a case are different protocol acts, and each has its own Invite.
The stub Invite asks whether the actor will join: Accept(Invite) joins it and consents to the embargo, and leaves RM at Received.
The full-case Invite asks whether the case is valid: its Accept is RV (Received → Valid), and RM Accepted is reached only by a later Join(VulnerabilityCase).
A participant cannot accept what it has not seen, so the CASE_MANAGER MUST NOT treat a participant as having committed to the case until it receives an RM status message from that participant confirming the transition.
See §9.7 Gating Full Case Delivery for why this matters to case delivery.
7. Embargo Management (EM) State Machine [N]
An embargo is an agreement among the participants of a case not to disclose the vulnerability publicly before an agreed point. Embargo Management tracks the progress of that agreement for the case as a whole: whether one has been proposed, whether it is in force, whether it is being renegotiated, and whether it has ended.
There is exactly one embargo state per case. It is shared case state, so only the CASE_MANAGER writes it (§5.4.1 Single-Writer Authority). The CASE_MANAGER does not set it directly either: it changes the case's embargo register, and the EM state is computed from the register (§7.1.2 Deriving EM from the Register). Whether each individual participant has agreed to the current terms is a separate question, tracked per participant and per embargo by embargo consent (§9 Participant Embargo Consent (PEC) State Machine).
7.1 States
The five states below describe the case's collective position, not any one participant's, and not any one embargo's.
| State | Meaning | Embargo register entries |
|---|---|---|
| None | No embargo is in effect for this case, and none is proposed | none ACTIVE, PROPOSED or TERMINATED |
| Proposed | At least one embargo is proposed, and none is in force | one or more PROPOSED, none ACTIVE |
| Active | An embargo is in force, and no revision is open | one ACTIVE, none PROPOSED |
| Revised | An embargo is in force, and at least one revision of it is open | one ACTIVE, one or more PROPOSED |
| Exited | The embargo has been torn down | one TERMINATED |
None here means no embargo is in effect for the case.
A participant's consent row for an embargo has a separate start state, UNINVITED, meaning the participant has not been asked about that embargo (§9.1 States).
The two can legitimately disagree: a case at Active may hold a participant whose row for the embargo in force is INVITED, because it has been invited to the case and has not yet accepted, or DECLINED.
7.1.1 The Embargo Register
A case keeps one embargo register entry for every embargo ever proposed on it (ADR-0122). An entry names the embargo, holds its status, and, for an activated revision, names the embargo it replaced. Entries are appended and never removed, so the register is the case's whole embargo history, rejected and canceled proposals included.
| Status | Meaning |
|---|---|
PROPOSED |
An open proposal, awaiting the case owner's decision |
ACTIVE |
In force |
REJECTED |
The case owner declined the proposal |
SUPERSEDED |
Was in force; a revision replaced it |
CANCELLED |
Closed with no owner decision, because the embargo question became moot |
TERMINATED |
Was in force when the case's embargo ended |
REJECTED, SUPERSEDED, CANCELLED and TERMINATED are final.
Six triggers move an entry:
| Trigger | Change |
|---|---|
PROPOSE |
new entry → PROPOSED |
ACTIVATE |
PROPOSED → ACTIVE |
REJECT |
PROPOSED → REJECTED |
SUPERSEDE |
ACTIVE → SUPERSEDED, in the step that activates another entry |
TERMINATE |
ACTIVE → TERMINATED, recording what ended the embargo and, when known, why |
CANCEL |
PROPOSED → CANCELLED, in the step that terminates the embargo in force, or on a threat signal while no entry is ACTIVE |
A register step is the whole change one act causes, so a termination and the cancellation of every open proposal happen in one step and are checked together (EP-08-004). Every step keeps these invariants, and a step that would break one is refused:
- At most one entry is
ACTIVEand at most one isTERMINATED, and never one of each. - A
SUPERSEDEDentry exists only alongside exactly oneACTIVEorTERMINATEDentry. - No entry is
PROPOSEDwhile an entry isTERMINATED. - Once a step has left an entry
TERMINATED, no later step adds or changes an entry.
A termination records two things.
What ended the embargo is END_TIME_REACHED or EARLY.
Why, when known, is a structured cause whose kind is THREAT_SIGNAL (with the signals P, X or A), FIX_READY (with the vendors whose readiness made the quorum), FIX_DEPLOYED or OTHER (EMB-13-004).
Every cause is EARLY, and an END_TIME_REACHED termination has none.
The termination message carries the what as content and the why as terminationCause (EMB-13-005).
Register status is not EM state
The register statuses and the EM states share the words proposed and active, but they name different things.
Register status ACTIVE is one embargo in force; EM ACTIVE is the case with an embargo in force and no revision open.
A case whose register holds one ACTIVE entry and one PROPOSED entry is at EM Revised, not EM Active.
7.1.2 Deriving EM from the Register
The case's EM state is a read of its register's statuses, never set directly (EMB-18-001):
ACTIVE entries |
PROPOSED entries |
TERMINATED entries |
EM |
|---|---|---|---|
| 0 | 0 | 0 | None |
| 0 | ≥1 | 0 | Proposed |
| 1 | 0 | 0 | Active |
| 1 | ≥1 | 0 | Revised |
| 0 | 0 | 1 | Exited |
REJECTED, SUPERSEDED and CANCELLED entries do not move EM.
A register that breaks an invariant matches no row, and the derivation refuses it.
The case's status carries a copy of the derived EM, stamped from the register, and a received status can report EM but never move it: an asserted EM that differs from the derived one is refused.
Because EM aggregates over every entry, the case leaves Proposed or Revised only when no proposal remains open.
Rejecting one of several open proposals leaves the others open, so EM stays where it was.
Activating one of several open proposals leaves the rest open as revisions of the embargo now in force, so EM moves to Revised rather than Active.
An ACTIVE entry leaves that status only when another entry supersedes it or when it is terminated, so a case that has had an embargo in force never returns to None or Proposed.
7.2 Transitions and Guards
Four triggers drive the machine: propose, accept, reject and terminate. The register computes EM, and this table constrains how it may move: every register step names its EM trigger, and a step whose EM move is not in the table is refused, with nothing written (EMB-18-005, EMB-18-006, ADR-0130). A proposal is propose, an activation is accept, a rejection, or the cancellation of open proposals with no embargo in force, is reject, and a termination is terminate. Every transition is a change to shared case state, so the CASE_MANAGER applies it, and every replica applies the same check when it copies the step from the case ledger; a participant causes a transition by sending the corresponding message, not by writing the state itself.
---
title: Embargo Management State Diagram
---
stateDiagram-v2
direction LR
[*] --> None
state Pre-Embargo {
None --> Proposed : propose
Proposed --> Proposed : propose
Proposed --> Proposed : reject
Proposed --> None : reject
}
state Active_Embargo {
Active --> Revise : propose
Revise --> Active : accept
Revise --> Active : reject
Revise --> Revise : propose
Revise --> Revise : accept
Revise --> Revise : reject
Revise --> eXited : terminate
Active --> eXited : terminate
}
state Post-Embargo {
eXited
}
Proposed --> Active : accept
Proposed --> Revise : accept
eXited --> [*]
The diagram shows every move of the EM transition table. A self-loop on reject or accept is the move made while another proposal is still open, and accept from Proposed reaches Revise when the other open proposals become revisions of the embargo now in force.
| From | Trigger | To | Notes |
|---|---|---|---|
| None | propose | Proposed | |
| Proposed | propose | Proposed | A further proposal is added beside the open ones |
| Proposed | reject | None | The last open proposal is rejected; no terms were ever in force |
| Proposed | reject | Proposed | Another proposal is still open |
| Proposed | accept | Active | No other proposal is open |
| Proposed | accept | Revised | The other open proposals become revisions of the embargo now in force |
| Active | propose | Revised | Proposing against an active embargo opens a revision |
| Revised | propose | Revised | |
| Revised | reject | Active | The last open revision is rejected; the prior terms stand |
| Revised | reject | Revised | Another revision is still open |
| Revised | accept | Active | The revised terms replace the prior ones; no other revision is open |
| Revised | accept | Revised | The revised terms replace the prior ones; another revision is still open |
| Active | terminate | Exited | |
| Revised | terminate | Exited | Every open revision is canceled in the same step |
Rejecting a revision does not end the embargo
Reject behaves differently depending on where it arrives, and the difference is easy to get backwards.
From Proposed, rejecting the last open proposal returns the case to None: no terms were ever in force, so refusing them leaves the case with no embargo.
From Revised, rejecting the last open revision returns the case to Active, not to None. The case owner is refusing the proposed change, not the embargo. The previously agreed terms remain in force. An implementation that returns to None here silently drops an embargo that everyone still expects to hold.
Reaching Exited.
Exited means the embargo has been torn down at the case level.
A participant does not put the case into Exited by deciding to stop observing the embargo.
What a participant may do is withdraw its own consent, report that it intends to exit, propose a shorter embargo, or propose an earlier end date; the CASE_MANAGER then terminates the embargo and records the termination (§10.2 Embargo Revision and Termination Cascades).
Terminating an embargo requires Case Owner authorization by default, on the same terms as any other change to canonical case state (§10.3 Status Adoption: The Two-Seam Model).
An embargo whose end time has passed is terminated by the CASE_MANAGER itself, with the reason END_TIME_REACHED (EMB-13-003).
Open question: Case Owner discretion once a termination condition is canonical
Terminating a case-level embargo requires Case Owner authorization by default, and the CASE_MANAGER effects the teardown as the single writer of canonical case state. What is settled is who may terminate: any Participant may initiate termination (report a public/exploit/attacks condition, or notify intent to exit) and may terminate its own compliance, but only the Case Owner authorizes ending the embargo for the case.
What is not settled is whether the Case Owner retains any discretion once
a termination condition has been adopted as canonical case state. When public
awareness or exploit publication (\(q^{cs} \in \{\cdots P \cdots, \cdots X
\cdot\}\)) is canonical, VP-14-001/VP-14-002 read as a mandatory consequence —
the embargo no longer protects anything — and the working position of this
specification is that teardown is then mandatory, with the Case Owner's
authority exercised at the point the condition is adopted (guarding against
a false report) rather than as a second, separate decision to keep the
embargo. This matches the automatic-teardown path (a canonical CS.P set by
the Case Owner triggers teardown).
Open: whether the Case Owner may decline to tear down even after the
condition is canonical; whether the "attacks" condition (SHOULD, VP-11-006)
differs from the "public/exploit" condition (SHALL) in this respect; and how
a deliberate decision to keep an otherwise-terminable embargo would be
recorded. This is expected to be a matter of discussion; the resolution MUST
be reflected in this reference and in specs/vultron-protocol-spec.yaml
(VP-11, VP-14) once reached.
Embargo duration selection. Where several proposals are outstanding, participants SHOULD accept the shortest and propose the remainder as a revision (EP-08-001). Other policies are also admissible: an implementation MAY defer the choice to the Case Owner, or apply its own organizational policy. This specification does not mandate shortest-wins.
Tacit acceptance. A receiver MAY publish a default embargo policy. Where it has, a sender that submits a report without proposing terms accepts that default.
A protocol default applies when nobody proposes anything. Where neither party proposes terms and the receiver has published no default, an embargo-eligible case MUST still begin with an active embargo, at a short duration fixed by the protocol rather than by either party. The duration MUST be configurable, and MUST be no less than 72 hours and no more than 5 days. Where the case is not embargo-eligible (the vulnerability is public, an exploit is public, or attacks are observed), no embargo is established and the case remains in None.
The protocol default is not a proposal: it MUST NOT take part in the shortest-wins comparison above, because a default short enough to be useful would otherwise win against every longer proposal and cap every embargo at its own length. Nor is it a minimum: a party that proposes a shorter duration MUST get the duration it proposed.
Outside these two default paths, embargo agreement and rejection SHOULD be explicit.
7.3 Relationship to Embargo Consent
The two machines answer different questions. Embargo Management says whether the case has an embargo. Embargo consent (§9 Participant Embargo Consent (PEC) State Machine) says whether a given participant has agreed to a given embargo. Neither determines the other, which is why both are needed.
Both read the same register. A participant's consent rows are one per register entry, and whether it is a signatory or has lapsed is read from its rows and the register, not stored (§9.3 Signatory and Lapsed). Two register steps change who is bound; only one of them writes rows:
- Activating a revision that ends later than the terms it replaces binds only participants that agreed to the revision.
A signatory to the replaced terms that has not agreed to the revision has lapsed: its agreement covered the previous, shorter terms.
A revision that ends no later than the terms it replaces carries every signatory over, by a written
CARRY_OVERon each of their rows for the revision. Proposing a revision changes no one's consent to the embargo in force. - Terminating the embargo leaves no entry
ACTIVE, so nobody is a signatory and nobody has lapsed. The termination writes no consent row, and every row for a final entry is frozen as it stood (CM-18-003, CM-18-016).
§10.2 Embargo Revision and Termination Cascades specifies both cascades.
8. Case State (CS) Dimensions [N]
Case State records what is known about the vulnerability. It answers two different questions, and it is a compound of two machines because those questions have different owners.
The first is what each vendor has done about the vulnerability: is it aware, does it have a fix, has the fix been deployed? That is participant-specific — each participant answers for itself. The second is what the world knows: is the vulnerability public, is there a public exploit, are attacks happening? That is a fact about the world, shared by the whole case.
The two are tracked separately, as VFD and PXA. Confusing them is the most consequential error in this part of the model: it leads an implementation to record one participant's action as a change to the state of the world, or to treat a world event as though some participant caused it.
8.1 VFD — Vendor Aware, Fix Ready, Fix Deployed
VFD records what a specific participant has done, so each participant has its own value. It moves in one direction only, and through its states in a fixed order: a vendor must be aware before a fix can be ready, and a fix must be ready before it can be deployed. That ordering leaves four reachable states rather than eight.
| State | Vendor Aware | Fix Ready | Fix Deployed |
|---|---|---|---|
vfd |
no | no | no |
Vfd |
yes | no | no |
VFd |
yes | yes | no |
VFD |
yes | yes | yes |
Because the axes are ordered, the state is written as a single three-letter token
in which case indicates progress: Vfd means the vendor is aware, the fix is not
ready, and nothing is deployed.
Which participant may cause which VFD transition depends on the roles it holds, and is specified at §12.4.1 Participant-Specific CS Transitions (VFD).
See also
8.2 PXA — Public Aware, Exploit Public, Attacks Observed
PXA records the state of the world, not the state of any participant. There is one PXA value for the case, it is shared case state, and only the CASE_MANAGER writes it (§5.4.1 Single-Writer Authority).
Unlike VFD, the three axes are independent: any combination can occur, in any order.
| Axis | Not yet | Set | Meaning when set |
|---|---|---|---|
| Public Aware | p |
P |
The vulnerability is known outside the case participants |
| Exploit Public | x |
X |
An exploit for the vulnerability has been published |
| Attacks Observed | a |
A |
Attacks exploiting the vulnerability have been seen |
Independence gives PXA eight states — every combination of the three, written
by setting each letter to upper or lower case: pxa, Pxa, pXa, pxA, PXa,
PxA, pXA, PXA.
---
title: pxa State Diagram
---
stateDiagram-v2
direction LR
[*] --> pxa
pxa --> Pxa : P
pxa --> pXa : X
pxa --> pxA : A
pXa --> PXa : P
pXa --> pXA : A
pxA --> PxA : P
pxA --> pXA : X
Pxa --> PxA : A
Pxa --> PXa : X
pXA --> PXA : P
PXa --> PXA : A
PxA --> PXA : X
PXA --> [*]
Any participant MAY report a PXA observation, because a PXA fact is not any participant's to own — anyone may notice that an exploit has appeared. Reporting is not the same as the case adopting the observation (§12.4.2 Participant-Agnostic CS Transitions (PXA), §10.3 Status Adoption: The Two-Seam Model).
Two PXA states are passed through, not rested in
Publishing an exploit makes the vulnerability public. The two states in which
an exploit is public while the public is unaware — pXa and pXA — therefore
describe a situation that does not persist: they resolve to PXa and PXA.
An implementation SHOULD resolve either in the same processing step rather
than leaving a case at rest there.
The reverse does not hold. Attacks being observed does not by itself make
the vulnerability public: an attack can be detected without its mechanism
being understood or disclosed. pxA is a state a case can legitimately rest
in.
This specification does not say where the pX→PX resolution is enforced. See
the open question in §8.3 Case State as a Compound Tuple.
8.3 Case State as a Compound Tuple
Case State is the pair CS = (VFD, PXA), giving 4 × 8 = 32 compound states.
Not every ordering among them is reachable or meaningful. This specification does not yet state the ordering constraints normatively, so a conformance claim cannot cover them.
Open question: CS ordering constraints are unspecified
CS ordering constraints — including the pX→PX invariant (exploit-public
implies public-aware) and the rule that CP must precede ET where public
disclosure triggers embargo teardown — exist in three non-normative places:
a code docstring, a measurement-oriented reference document
(Possible Histories),
and a design note. No spec group covers them normatively.
A conformance claim cannot meaningfully cover CS ordering until this gap is closed. Treat the material cited here as informative until a normative spec is issued.
One ordering relationship is specified, because it is a cascade rather than an ordering constraint: adopting a status that sets Public Aware, Exploit Public or Attacks Observed requires the CASE_MANAGER to evaluate embargo teardown (§10.3 Status Adoption: The Two-Seam Model). The embargo therefore ends after public awareness is recorded, not before.
8.4 Receiving CS Messages: Own State vs. Model of Others
A participant holds its own case state and a model of what it believes the other participants' states to be. Different events update each one.
- Receiving a VFD message (
CV,CF,CD) updates the receiver's model of the sender's VFD state. The receiver's own VFD state does not change. No acknowledgment is sent: a participant confirms it has the case history by the fact that its ledger replica is unbroken, not by acknowledging each entry (§4.6 Error and Acknowledgment Messages). - Driving one's own VFD transition happens locally, subject to the role requirements of §12.4.1 Participant-Specific CS Transitions (VFD), and is then announced.
- PXA is shared case state, so an adopted PXA observation updates the case's canonical status rather than any per-participant model (§10.3 Status Adoption: The Two-Seam Model).
See also
9. Participant Embargo Consent (PEC) State Machine [N]
An embargo binds the participants who agreed to it. Embargo Management (§7 Embargo Management (EM) State Machine) records whether the case has an embargo; it cannot record who has agreed, because that answer differs per participant. Participant Embargo Consent records it.
The distinction has practical force. A participant invited to a case that already has an active embargo has not yet agreed to anything, and the CASE_MANAGER must not send it embargoed content until it has (§9.7 Gating Full Case Delivery). A participant that declines is still in the case and still receives the negotiation traffic (§9.5 Embargo Traffic Reaches Non-Signatories). Neither position is expressible in the case-level embargo state.
Consent is per participant, but the CASE_MANAGER writes it: it is recorded on the participant's record in the case, alongside the participant's other case state.
Informative: provenance
The original protocol design had four state machines and no consent machine. This one emerged during implementation, when the case-level embargo state proved unable to distinguish "no embargo exists" from "this participant has not yet agreed". It is normative here, because correct embargo behavior cannot be specified without it.
9.1 States
Consent is given to specific terms, so a participant has one consent row for every entry in the case's embargo register (§7 Embargo Management (EM) State Machine), and each row holds one of the five states below (CM-18-001). A row answers one question: what has this participant said about this one embargo? A participant can therefore be bound by the embargo in force and have already agreed to a proposed revision of it, on two different rows.
| State | Meaning |
|---|---|
UNINVITED |
Not asked about this embargo; the start state of every row |
INVITED |
Asked, with no answer yet; carries that invitation's RSVP deadline |
AGREED |
Agreed to this embargo: explicitly, as its proposer, by seeding, or by carry-over |
DECLINED |
Explicitly refused this embargo, or withdrew from it |
TIMED_OUT |
Invited, and the RSVP deadline passed with no answer; not a refusal |
Every row is written.
Proposing an embargo writes an UNINVITED row for it on every current participant, and creating a participant record writes an UNINVITED row for every entry already in the register (CM-18-001).
No state is read from a missing row: a missing row is a defect, and reading one raises.
A participant that joins late stays UNINVITED on embargoes that are no longer in force; it is invited only to the embargo in force and to open revisions (CM-11-006, EP-09-011).
UNINVITED and DECLINED both leave the participant unbound by the embargo the row answers, but they are distinct states, because whether the participant gave an answer matters.
The lapsed read stops at a DECLINED row and passes over an UNINVITED one (§9.3 Signatory and Lapsed), and an open proposal is relayed to a joiner only while its row for that proposal is UNINVITED (CM-18-001, EP-09-011).
INVITED carries the invitation's respond-by deadline, the RSVP deadline, on the row itself (CM-28-013).
The deadline belongs to one invitation, so a participant can hold several INVITED rows with different deadlines, and one passing times out only its own row.
---
title: Participant Embargo Consent State Machine (one row per participant and embargo)
---
stateDiagram-v2
direction LR
[*] --> UNINVITED
UNINVITED --> INVITED: INVITE
UNINVITED --> AGREED: AGREE
UNINVITED --> DECLINED: DECLINE
INVITED --> AGREED: AGREE
INVITED --> DECLINED: DECLINE
INVITED --> TIMED_OUT: TIME_OUT
AGREED --> DECLINED: DECLINE
DECLINED --> INVITED: INVITE
DECLINED --> AGREED: AGREE
TIMED_OUT --> INVITED: INVITE
TIMED_OUT --> AGREED: AGREE
TIMED_OUT --> DECLINED: DECLINE
The diagram shows the five states of one consent row and the triggers INVITE, AGREE, DECLINE and TIME_OUT that move it.
CARRY_OVER also moves every state except AGREED to AGREED, when the case owner activates a revision that ends no later than the embargo it replaces; those edges are omitted for legibility.
No state is terminal: a row stops accepting triggers when its embargo's register entry reaches a final status.
See also
9.2 Transitions and Guards
Five triggers drive each row (CM-18-003). INVITE records an invitation, AGREE and DECLINE record an answer, TIME_OUT fires when an invitation's RSVP deadline passes unanswered, and CARRY_OVER agrees for a participant when the case owner activates a revision that asks nothing new of it.
| Trigger | From | To |
|---|---|---|
INVITE |
UNINVITED, DECLINED, TIMED_OUT |
INVITED |
AGREE |
UNINVITED, INVITED, DECLINED, TIMED_OUT |
AGREED |
DECLINE |
UNINVITED, INVITED, AGREED, TIMED_OUT |
DECLINED |
TIME_OUT |
INVITED |
TIMED_OUT |
CARRY_OVER |
every state except AGREED |
AGREED |
No other trigger is valid from any state, and an implementation rejects one as a protocol error (CM-18-009).
A row for an embargo whose register entry is final (REJECTED, SUPERSEDED, CANCELLED or TERMINATED) accepts no trigger at all, so the consent given to an embargo that is no longer in play is frozen as it stood (CM-18-003).
AGREED refuses INVITE.
Consent already given is not undone by asking again (CM-18-004).
A participant whose terms have since changed is invited on its row for the new embargo, which leaves its AGREED row for the old one alone.
AGREE and DECLINE are valid directly from UNINVITED.
Consent does not always arrive through an invitation: a finder that opens a case for its own finding and sets its default embargo was invited by nobody, and a reporter consents by reporting.
The case history MUST NOT record an invitation that did not happen (ADR-0048).
A repeated answer is not a transition.
When a participant sends the same answer to the same invitation again, the CASE_MANAGER changes nothing, records nothing in the case ledger and sends nothing, whatever the repeat's activity id (CM-13-005).
A different answer is a new answer, and the table above applies to it.
A participant that declined and then agrees has changed its answer, so its row moves to AGREED directly, without being invited again.
None of DECLINED, TIMED_OUT and AGREED is terminal.
A DECLINED or TIMED_OUT row can be invited again, which is what makes renegotiation possible, and an AGREED row can be declined.
What ends a row's life is its register entry reaching a final status, not any state of the row.
9.3 Signatory and Lapsed
Two positions a participant can hold are not states. Each is a read of the participant's rows and the case's register, and neither is stored or drawn on the state machine (CM-18-001).
| Read | Definition |
|---|---|
| Signatory | The participant's row for the register's ACTIVE entry is AGREED. |
| Lapsed | An entry is ACTIVE, the participant's row for it is neither AGREED nor DECLINED, and, following replaces back from the ACTIVE entry, the first entry whose row is AGREED or DECLINED has an AGREED row. |
With no ACTIVE entry nobody is a signatory and nobody has lapsed.
After an embargo is terminated no entry is ACTIVE, so the termination writes no row: every participant's position follows from the register alone (CM-18-016, MSM-07-006).
Abandoning an open proposal once the case is public, an exploit is public or attacks are observed writes no row either (EMB-16-001).
The CASE_MANAGER cancels the proposal with its Reject of the proposal it received, or its Undo of one it made itself, and then withdraws each invitation it relayed for the proposal with an Undo.
Each row for the canceled proposal is frozen as it stood.
The CASE_MANAGER's Reject is its abandonment, not its consent (MSM-07-004).
A CASE_MANAGER that has a stake in an embargo therefore records its own answer as its own update of the row, never as a Reject.
A participant lapses when the case owner activates a revision that ends later than the embargo the participant agreed to, and the participant has not agreed to the revision.
It did agree, and the embargo in force has since become something it did not agree to.
Only the embargoes that were once in force sit on the replaces chain, so agreeing to a proposal that never took effect binds nothing to lapse from.
A participant that withdrew from a later embargo has a DECLINED row on the chain, and has not lapsed when a further revision replaces that one.
Lapsed is neither the proposal state nor the deadline state
A merely proposed revision lapses nobody, and neither does a revision that ends no later than the accepted terms.
A participant that lets an invitation deadline pass reaches TIMED_OUT on that invitation's row, not lapsed.
A lapse is not timer-based and has no transition of its own: a lapsed participant is re-invited, on an INVITED row for the embargo in force, and the deadline attaches to that invitation (CM-18-002).
A revision does not move anyone's consent to the embargo in force until it takes effect (EP-05-002).
While a revision is only proposed, the prior embargo is in force and every signatory to it remains one.
The proposer's row for the revision is AGREED, because proposing terms is consenting to them, and every other participant gets an UNINVITED row for it.
A signatory that agrees to the revision records that on the revision's row, and one that declines it refuses those terms and nothing more, on that row only (MSM-07-003, MSM-07-004).
A signatory withdraws by leaving the embargo in force with Leave(EmbargoEvent), not by rejecting an invitation to it (MSM-07-010).
The withdrawal declines its row for the embargo in force and every open proposal's row it had agreed to, because every open proposal is a revision of the embargo it is leaving.
A Reject of the active embargo from an AGREED row is refused with a reported reason (MSM-07-004).
The case owner does not leave an embargo; it ends one by terminating it.
Only the case owner's decision changes the embargo on the case; the other participants' answers inform it (EP-09-005). When the owner activates a revision, the direction of the change decides what happens to the consent of the signatories to the old terms (EP-05-001, MSM-07-005):
- A revision that ends no later than the terms it replaces asks nothing new of anyone who agreed to the old terms, since agreeing to N days is agreeing to every shorter period.
The CASE_MANAGER applies
CARRY_OVERto the revision's row of every participant whose row for the replaced embargo isAGREED, aDECLINEDrow for the revision included. - A revision that ends later asks for more than they promised.
No row is written: a signatory to the old terms whose row for the revision is not
AGREEDhas lapsed, by the definition above.
Either way, a participant already holding an AGREED row for the revision is its signatory by lookup, whether it is the proposer or an early acceptor.
Only signatories to the old terms are carried over.
A participant that had lapsed from an earlier embargo stays lapsed until it is re-invited or agrees.
An INVITED participant whose invitation the activation has made stale is re-invited to the current terms, as in §9.4 Deadlines and the Pocket Veto (EMB-17-003).
If the owner rejects the revision instead, the old terms stand and nobody's consent changes (MSM-07-009).
9.4 Deadlines and the Pocket Veto
An embargo invitation does not stay open indefinitely.
A participant that neither agrees nor declines before the RSVP deadline has its row for that invitation moved to TIMED_OUT, the pocket veto (CM-18-002).
Silence is not treated as assent, because proceeding on an unanswered invitation would mean asserting agreement the participant never gave.
Nor is it recorded as a refusal: DECLINED is reached only by an explicit refusal (CM-28-004).
The deadline may be explicit or defaulted:
- An embargo invitation MAY carry its own
endTime, giving an explicit respond-by instant. Where present, it takes precedence over the policy window (CM-28-001, CM-28-002). - Where absent, a configurable policy window applies. The default window is 7 days.
Either way the result is bounded below and above by the two rules further down this section: the explicit endTime takes precedence over the policy window, but neither escapes the minimum window or the embargo-end ceiling.
The pocket veto is the implicit form of this one mechanism, not a second mechanism alongside it.
One invitation carries two different end times
The invitation's endTime is the respond-by deadline for the invitee.
The embargo event nested inside the invitation has its own endTime, which is when the embargo would expire.
They sit one nesting level apart and mean different things.
An implementation that reads the embargo's expiry as the RSVP deadline will hold invitations open for the entire embargo period. One that reads the RSVP deadline as the embargo expiry will tear down embargoes days after they were agreed.
Recording the deadline.
When the CASE_MANAGER relays an invitation, it sets the Invite's endTime and records the deadline on the invitee's INVITED row for the invited embargo (CM-28-012, CM-28-013).
Every replica records the same deadline by copying the CASE_MANAGER's ledger entry for that row.
A participant does not derive or store a deadline on receipt of an Invite.
Enforcing the deadline.
The CASE_MANAGER enforces it, and only the CASE_MANAGER (CM-28-003, CM-28-014).
It does so lazily: when it next handles the case, it compares each deadline against its own clock, so no scheduler or timer service is required (CM-28-007, CM-28-008).
Before it commits the case owner's decision on a proposal, whenever it processes any other received embargo activity for the case, and whenever it runs an embargo trigger of its own, it expires every INVITED row whose deadline has passed and whose embargo is an open proposal or the embargo in force (CM-28-015).
A row of a replaced, rejected, canceled or ended embargo is frozen and is not swept.
The sweep runs after the activity's sender has been admitted and before the activity's own refusals, so an expiry is recorded even when the activity is then refused.
A minimum window applies. The CASE_MANAGER MUST enforce a minimum respond-by window. The minimum is the lesser of a configured window, 72 hours by default, and the time remaining in the embargo the invitation concerns (EP-07-002). Where an invitation carries a shorter deadline, the CASE_MANAGER MUST extend the deadline to that minimum. It MUST NOT reject the invitation for this reason: a too-short deadline is the proposer's error, and refusing the invitation would penalize the invitee for it.
A deadline may not outlive its embargo.
The CASE_MANAGER MUST NOT let a respond-by deadline fall after the end of the embargo it concerns (CM-28-011).
Where a computed deadline would, whether it came from the invitation's explicit endTime or from the policy window, the CASE_MANAGER MUST clamp it down to the embargo's end.
An invitee must be able to answer while there is still something to answer about.
Invite a participant to a 24-hour embargo with no explicit endTime and a 7-day policy window, and its inaction would be recorded as a time-out on day 7, six days after the embargo ended.
This is why the minimum window above is relative rather than absolute: a 12-hour embargo grants a 12-hour answer window, and that is not an unreasonably short deadline when 12 hours is the whole embargo.
A late acceptance is not refused. The CASE_MANAGER MUST NOT refuse a late accept on deadline grounds (EMB-17-001). Three cases apply:
- If the terms it accepts are the case's current embargo, the CASE_MANAGER records the accept exactly as an on-time accept: its entry is the accept itself, and a
TIMED_OUTorDECLINEDrow moves toAGREEDdirectly (EMB-17-002). - If the terms are stale, the CASE_MANAGER relays a fresh invitation carrying the current terms, on behalf of their proposer (EMB-17-003).
- If no embargo remains, the accept changes nothing: the CASE_MANAGER records no entry, sends no reply and leaves the rows unchanged; the participant keeps its place in the case either way (EMB-17-004, EMB-17-008).
A time-out and a refusal reach different states.
Silence records TIMED_OUT; an explicit refusal records DECLINED.
The case ledger distinguishes them too, holding either the refusal that was sent or the CASE_MANAGER's record of the expiry (CM-28-005, CM-28-009), but every reader of the consent rows can tell them apart without consulting it.
Both can be invited again, and both move to AGREED directly on a late accept of the current terms.
9.5 Embargo Traffic Reaches Non-Signatories
Embargo consent gates case content. It does not gate the negotiation about the embargo itself.
The CASE_MANAGER MUST deliver embargo meta-protocol activities addressed to a participant directly (an embargo Invite and its responses) to that participant even when it declined, timed out or lapsed; they are not ledger fan-out, which the content gate holds back from non-signatories (CM-10-005).
A participant cannot agree to revised terms it was never told about, and one that declined the original terms may well accept the revision.
Only case content (report details, fix status, sensitive notes) is gated on being a signatory (§9.7 Gating Full Case Delivery).
9.6 Embargo Adherence
Whether a participant is currently bound by the embargo is a single yes-or-no question, and the protocol answers it from the consent rows: a participant is bound exactly when it is a signatory, that is, when its row for the register's ACTIVE entry is AGREED (CM-18-001).
This is a derived fact, not an independent one. An implementation MUST NOT record adherence as a value that can be set separately from the rows it reports, because the two would then be able to disagree, and a case that reports a participant as bound while its rows say otherwise has no correct reading.
A change of consent MUST be made either by applying a consent trigger through the transitions of §9.2 Transitions and Guards, or by copying the row a ledger entry carries after checking its starting state, through the one apply function the CASE_MANAGER and every replica share, so that every change is a valid one and is recorded as such (CM-18-005).
The CASE_MANAGER commits every consent row it changes as its own Update(EmbargoConsent) ledger entry, or Create(EmbargoConsent) for a row it brings into existence, after the entry of the act that caused it (CLP-07-013, EP-09-007).
Where the act's own activity states the row, such as a participant's Accept(Invite(EmbargoEvent)) or, for the case owner's own row, its Accept(EmbargoEvent, target=Case) activating an embargo, that entry is the row's entry.
Informative: how the reference implementation does this
The reference implementation exposes adherence as a computed lookup over the stored consent rows and the register, so there is no separate field to fall out of step. Any mechanism with that property satisfies the requirement above.
9.7 Gating Full Case Delivery
Before the CASE_MANAGER delivers full case content (Announce(VulnerabilityCase) carrying report details, vulnerability description, and sensitive notes), both conditions MUST hold for the recipient:
- The participant is admitted to the case: its RM state is at least Received.
- The participant is a signatory to the active embargo (its row for the active embargo is
AGREED), OR no embargo is in force (EM is notACTIVEorREVISE).
Recall: report management states
| State | Meaning |
|---|---|
| Start | No report has been received |
| Received | A report has arrived; triage is not yet complete |
| Invalid | This participant assessed the report as invalid |
| Valid | This participant assessed the report as valid |
| Deferred | This participant is deferring further action |
| Accepted | This participant accepted the report for active work |
| Closed | This participant closed the report |
Full definitions are in §6.1 States.
The gate is admission plus consent — not completed triage
It is tempting to read condition 1 as RM.ACCEPTED.
That reading is wrong and self-defeating: an invitee is at RM.RECEIVED from the stub Invite and stays there after Accept(Invite), and reaches ACCEPTED only after receiving the full case and running its triage cycle (§6.3 Per-Participant RM Tracking).
Requiring ACCEPTED before delivery would mean a participant could never obtain the case it needs in order to reach the state that gates it.
Embargo consent — not RM progress — is the substantive gate on case content.
The ordering is: admit the participant at RM.RECEIVED, resolve embargo consent, then deliver the full case.
This matters to §10 Model Interactions and Cascade Rules's cascade ordering: Accept(Invite) agrees to any active embargo on the joiner's row for it, which is what allows delivery to proceed immediately rather than waiting on a separate consent round-trip.
A joiner whose row for the active embargo is DECLINED stays declined, because joining is not an answer to the embargo.
10. Model Interactions and Cascade Rules [N]
The five state machines are not independent. A transition in one produces obligations in others, and this section specifies those couplings.
Each coupling is a cascade: a state change raises a domain event, and the CASE_MANAGER handles that event by applying further changes. Every cascade step is authorized separately, and where the order of steps matters this section says so. The cascades below are the complete set that this specification requires.
Recall: which machine owns which state
| Machine | Ownership | Written by |
|---|---|---|
| Report Management (RM) | Participant-specific | The participant itself |
| Vendor aware, Fix ready, fix Deployed (VFD) | Participant-specific | The participant itself |
| Embargo Management (EM) | Shared case state | The CASE_MANAGER only |
| Public aware, eXploit public, Attacks observed (PXA) | Shared case state | The CASE_MANAGER only |
| Participant Embargo Consent (PEC) | Per-participant | The CASE_MANAGER only |
10.1 Admitting a Participant
When an actor accepts the stub Invite, the CASE_MANAGER admits it to the case and
delivers the case content. The CASE_MANAGER created the actor's inert participant
record, at RM state Received, when it sent the stub Invite
(§11.2 Invitation and Acceptance).
The steps are ordered because each one supplies a precondition for the next.
---
title: Admitting a participant
---
sequenceDiagram
autonumber
participant I as Invited actor
participant CM as CASE_MANAGER
participant P as Active participants
I->>CM: Accept(Invite(Actor, VulnerabilityCaseStub))
CM->>CM: Commit ledger entry for the acceptance
CM->>P: Announce(CaseLedgerEntry)
CM->>CM: Record effect: RM stays Received, embargo consent AGREED
CM->>I: Announce(VulnerabilityCase) — full case content
CM->>I: Prior ledger entries, oldest first
CM->>I: Invite(Actor, VulnerabilityCase) — full-case Invite
On receiving Accept(Invite), the CASE_MANAGER MUST perform these four steps in
this order (CM-17-004):
- Commit a ledger entry recording the acceptance, and send that entry to every active participant.
- Record the acceptance's effect on the participant: its RM state stays
Received, and its consent row for the active embargo, if any, becomesAGREED, unless the participant had declined that embargo. - When the participant is now active, send
Announce(VulnerabilityCase)carrying the full case snapshot, then replay the ledger entries committed before this participant joined, oldest first. - Send the full-case Invite, which asks the participant to judge the case.
Recall: report management states
| State | Meaning |
|---|---|
| Start | No report has been received |
| Received | A report has arrived; triage is not yet complete |
| Invalid | This participant assessed the report as invalid |
| Valid | This participant assessed the report as valid |
| Deferred | This participant is deferring further action |
| Accepted | This participant accepted the report for active work |
| Closed | This participant closed the report |
Full definitions are in §6.1 States.
Step 3 delivers the content that step 2 authorizes: the embargo consent recorded in step 2 is what the delivery gate tests (§9.7 Gating Full Case Delivery). Step 4 follows the replay, so the participant judges a case it already holds.
10.2 Embargo Revision and Termination Cascades
A change to the embargo in force propagates to participants' consent, because consent is given to specific terms. A change that is only proposed does not. Each cascade below starts from a step on the case's embargo register (§7.1.1 The Embargo Register), and the EM state follows from the register.
Recall: embargo management states
| State | Meaning | Embargo register entries |
|---|---|---|
| None | No embargo is in effect for this case, and none is proposed | none ACTIVE, PROPOSED or TERMINATED |
| Proposed | At least one embargo is proposed, and none is in force | one or more PROPOSED, none ACTIVE |
| Active | An embargo is in force, and no revision is open | one ACTIVE, none PROPOSED |
| Revised | An embargo is in force, and at least one revision of it is open | one ACTIVE, one or more PROPOSED |
| Exited | The embargo has been torn down | one TERMINATED |
Full definitions are in §7.1 States.
- A revision is proposed (EM enters, or stays at,
Revised). The register gains aPROPOSEDentry for the revision, and every other participant gets anUNINVITEDrow for it (CM-18-001). No consent to the embargo in force changes: the prior embargo is still in force and every signatory to it remains one (EP-05-002). The CASE_MANAGER records the proposer's row for the revision asAGREED, because proposing terms is consenting to them. - The case owner activates the revision.
The register activates the revision's entry and supersedes the entry in force, in one step; EM returns to
Active, or stays atRevisedwhile another revision is open (EMB-18-005). The owner's row for the revision becomesAGREEDunless it already is. If the revision ends no later than the embargo it replaces, the CASE_MANAGER appliesCARRY_OVERto the revision's row of every participant whose row for the replaced embargo isAGREED, aDECLINEDrow for the revision included (EP-05-001, MSM-07-005). If it ends later, no row is written: a signatory to the replaced terms whose row for the revision is notAGREEDhas lapsed, which is read from its rows and the register (§9.3 Signatory and Lapsed). A participant that has already agreed to the revision is its signatory by lookup. - The case owner rejects the revision.
The register rejects the revision's entry; EM returns to
Activeonce no other revision is open. No consent row changes, the owner's included, and the prior terms stand (MSM-07-009). - The embargo is terminated (EM enters
Exited). The register terminates the entry in force and cancels every open proposal in the same step (EP-08-004). No consent row is written (CM-18-016, MSM-07-006). With noACTIVEentry, no participant is a signatory and none has lapsed, and every row for the terminated and canceled entries is frozen as it stood, so an accept that arrives afterwards records nothing (EMB-17-004). - After a teardown.
The termination's own ledger entry, fanned out as
Announce(CaseLedgerEntry), records the end (EP-09-009). The CASE_MANAGER then sendsAnnounce(EmbargoEvent)to every other active participant (EMB-19-001), and a directRemove(EmbargoEvent)notice to each signatory the ledger fan-out does not reach (CM-31-009). No message is sent for consent, because none is written (MSM-07-006).
Recall: participant embargo consent states
| State | Meaning |
|---|---|
UNINVITED |
Not asked about this embargo; the start state of every row |
INVITED |
Asked, with no answer yet; carries that invitation's RSVP deadline |
AGREED |
Agreed to this embargo: explicitly, as its proposer, by seeding, or by carry-over |
DECLINED |
Explicitly refused this embargo, or withdrew from it |
TIMED_OUT |
Invited, and the RSVP deadline passed with no answer; not a refusal |
Full definitions are in §9.1 States.
Every one of these changes reaches the case ledger, and every replica applies it by copying (CM-23-016).
The act's own activity carries its whole register step: a termination's entry also cancels every open proposal, and an activation's entry also supersedes the embargo it replaces (CLP-07-013).
Where the act's activity states a row itself, as the owner's Accept states the owner's agreement, the act's entry carries that row (CLP-07-013).
Each other consent row the CASE_MANAGER writes as a consequence of the act, such as an UNINVITED row, the proposer's AGREED row or a CARRY_OVER, is its own CASE_MANAGER entry after the act's entry, a Create(EmbargoConsent) for a new row or an Update(EmbargoConsent) for a changed one, carrying the row in full (EP-09-007).
A replica copies those rows and never decides a carry-over, an expiry or a lapse itself.
10.3 Status Adoption: The Two-Seam Model
A participant's report does not become canonical case state on arrival. It passes two authorization points, which this specification calls seams. A seam is a place where the CASE_MANAGER makes an authorization decision that can be re-policied without affecting the other.
Two seams exist because "record what a participant claimed" and "act on that claim as truth" are separate decisions with separate authority.
Seam 1 — Adoption
A participant reports an observation by sending Add(ParticipantStatus) to the
CASE_MANAGER. A participant does not send Add(CaseStatus): writing canonical
case status is reserved to the CASE_MANAGER
(§5.4.1 Single-Writer Authority).
The CASE_MANAGER records the claim in the case ledger first, and then decides whether to adopt it as canonical:
- The CASE_MANAGER MUST adopt a Case Owner's report without seeking approval. Asking the Case Owner to approve its own report would be circular (§12.4.4 Case Owner Authority).
- For every other sender, the CASE_MANAGER MUST obtain Case Owner authorization before adopting the report as canonical case state. This is the default posture, and it is deliberate: without it, any admitted participant could force a change to canonical case state.
- An implementation MAY configure a permissive policy instead, where all admitted participants are trusted and the cost of Case Owner approval is unacceptable. Such a configuration MUST be explicit and documented. An implementation MUST NOT relax the default in order to unblock a path that the gate is correctly blocking.
- The CASE_MANAGER accepts a reported status as sent or refuses all of it, and the refusal names every refused value and why.
A valid
vfdupdate is refused with anrmvalue in the same report that cannot be accepted, and the sender corrects and resends the status. - On adoption, the CASE_MANAGER commits the adopted status as its own
Add(CaseStatus)case ledger entry, setting every public-state flag the report sets and clearing none. It writes the entry directly, never by sending anAdd(CaseStatus)to itself, and a receiver refuses anAdd(CaseStatus)from any other sender. - The component that records the claim MUST NOT execute side-effects.
The default is Case Owner authorization, not automatic adoption
A permissive default would mean that any admitted participant — including a hostile or malfunctioning one admitted as an external monitor — could force a canonical PXA change and, through it, an embargo teardown, with no Case Owner confirmation. A conservative default makes that a protocol guarantee rather than a deployment posture.
Seam 2 — Side-effects
After the canonical write, the CASE_MANAGER evaluates what the new canonical state requires:
- The CASE_MANAGER MUST check whether the adopted status set Public Aware, Exploit Public, or Attacks Observed (§8.2 PXA — Public Aware, Exploit Public, Attacks Observed).
- If any of those is now set, the CASE_MANAGER MUST evaluate embargo teardown.
- This check MUST run after the canonical write, never before. What is
written is the
CaseStatusrecord holding the case's shared EM and PXA values; the check reads that written record, so that the side-effect always follows from canonical state rather than from an unadopted claim. - Executing the teardown MUST also require Case Owner authorization by default, on the same terms as adoption above.
The two seams are independent. Seam 1 does not examine embargo teardown, and seam 2 cannot tell whether the canonical write came from an inbound message or from the CASE_MANAGER's own emission. Either seam can therefore change policy without a change to the other.
When teardown is required but not authorized
Evaluating teardown is mandatory; executing it is authorized. If the authorization is withheld, the case is left in a state where public awareness coexists with an active embargo. That combination is a contradiction, and the CASE_MANAGER records it as a diagnostic note on the case rather than resolving it silently.
Where the gate model applies
Seam 1 governs any reported status. Its authorization question matters most for the participant-agnostic PXA values, which any participant may report (§12.4.2 Participant-Agnostic CS Transitions (PXA)) — including reports about other participants.
See also
11. Participant Lifecycle Within a Case [N]
A case is not a fixed set of parties. Actors join, take on roles, hand responsibilities to one another, and leave. This section specifies that lifecycle: how an actor acquires a role, how it is brought into a case, how case ownership moves, and how a participant is removed.
11.1 Role Assignment [N]
A role is granted through the case's authority chain. An actor does not acquire a case role by declaring that it holds one, because the other participants act on role claims: they send a Vendor the report content and treat a Coordinator's routing decisions as authoritative. A self-declared role would let an actor take on authority the Case Owner never granted.
- A case begins with a Case Owner — the actor whose disclosure decision the case exists to serve. Case ownership is not delegated, though it may be transferred (§11.3 Case Ownership Transfer).
- The Case Owner MAY offer an additional role to an actor that is already a participant.
The offer is an
Offer(CaseParticipantRole)addressed to the case's Case Manager only, never to the receiving actor (CM-33-001). The Case Manager records the offer and forwards it to the receiving actor, which answers the Case Manager withAcceptorReject(CM-33-004). The Case Manager records the answer and grants the role on anAccept(CM-33-006). Only the Case Owner may make a role offer (CM-33-002). The Case Owner and Case Manager roles cannot be offered (CM-33-003): ownership moves by transfer, and how the Case Manager role is handed to another actor is not yet specified. - The Case Manager records each actor's roles on that actor's
CaseParticipantentry in the case.
An actor MUST NOT be recorded as holding a role it did not accept, and a participant MUST NOT be recorded as holding a role the Case Owner did not grant. When an actor accepts a role offer, the roles it receives MUST be taken from the offer the case sent, not from the accepting actor's reply — otherwise an actor could widen its own grant on the way back.
An implementation MAY verify that an actor has the capability prerequisites for a role (§12.3.1 Process Roles) before completing the assignment.
Open question: acquiring a role after joining a case
This specification says how the Case Owner assigns a role and how a role is offered and accepted. It does not say how a participant already in a case asks for an additional one.
A participant admitted as a Vendor that later also needs the Coordinator
role has no protocol path to request it. In practice the request is made out
of band, or as a Add(Note) on the case, and the Case Owner then issues a
role offer. Neither is a specified mechanism.
Open: whether the protocol should define a role-request activity, whether a participant may decline a role it was assigned, and how a role that is removed rather than added is represented in the case history.
11.2 Invitation and Acceptance [N]
Bringing a new actor into a case has a problem to solve: the actor cannot decide whether to join until it knows something about the case, but it must not receive case content before it has been admitted and its embargo consent resolved (§9.7 Gating Full Case Delivery).
The protocol solves this with a case stub: a minimal stand-in for the case that carries enough for the invitee to decide, and no vulnerability detail.
The stub is its own object, of type VulnerabilityCaseStub, and is not the case (CM-11-013).
It carries exactly these fields (CM-17-010):
id: the stub's own identifier, which is the case's identifier with/stubappended. A receiver never derives the case from it.type:VulnerabilityCaseStub. A receiver tells a stub from a case by itstypealone.caseId: the identifier of the case the stub stands for.activeEmbargoandcaseStatus: present whenever an embargo is in force, that is, while the embargo state isACTIVEorREVISE. They state the embargo terms the invitee would agree to by accepting: the embargo in force and the case's embargo state (CM-17-002). During a revision the stub carries the embargo in force, never the open revision, which the invitee receives after joining (EP-09-011). The stub may carry the embargo inline or name it by an identifier the invitee can ask the inviter to resolve. This implementation always carries the full embargo inline, because it does not build that lookup (CM-17-011).summary: required human-readable context saying what the invitee is being asked to join (MV-10-001). The sender chooses it: it may be the case name, or a deliberately less revealing string, because the case name itself can be sensitive to share with an actor not yet bound by the embargo.
The stub does not name the case's coordinator or Case Owner.
The Invite that carries the stub already identifies who is asking: its actor is the CASE_MANAGER, and its attributedTo can name the Case Owner.
Full content follows only after the invitee accepts.
Two paths bring an actor into a case, and they differ in who initiates:
- Direct invitation. The CASE_MANAGER sends
Invite(Actor, target=VulnerabilityCaseStub)to the actor. At the same time, it records the invitee in the case as an inert participant at RM Received (CM-11-006). The CASE_MANAGER commits the record's creation to the case ledger, and a replica applying that entry creates the same inert record. The record is not yet an active participant: it does not receive case content and is not included in embargo fan-out until the actor accepts (CM-10-004). The actor answersAccept(Invite)orReject(Invite).Accept(Invite)activates the inert record, admitting the actor at RM Received and, where an embargo is in force, recording its consent to those terms.Reject(Invite)closes the inert record (RM Received → Closed) and keeps it as history, so the CASE_MANAGER can see that the invitation was extended and declined (CM-11-007). Every stub Invite carries a reply deadline. When it passes unanswered, the Invite closes as expired and the record is left unchanged: still inert, at RM Received, with its embargo consent row stillINVITED(CM-11-014). After joining, the participant judges the case by answering the full-case Invite, not the original report Offer (CM-11-011, CM-11-020). - Suggested actor. An existing participant proposes a third party — "this vendor is also affected" — by sending
Offer(CaseParticipant)to the CASE_MANAGER. The proposal is a recommendation, not an invitation: the Case Owner decides whether to act on it, and if it does, the CASE_MANAGER then sends theInviteabove. An implementation MUST NOT treatOffer(CaseParticipant)as an invitation to the proposed actor.
Both paths converge on Accept(Invite).
The suggested-actor path adds one round-trip, because the Case Owner's decision sits between the proposal and the invitation.
Recall: report management states
| State | Meaning |
|---|---|
| Start | No report has been received |
| Received | A report has arrived; triage is not yet complete |
| Invalid | This participant assessed the report as invalid |
| Valid | This participant assessed the report as valid |
| Deferred | This participant is deferring further action |
| Accepted | This participant accepted the report for active work |
| Closed | This participant closed the report |
Full definitions are in §6.1 States.
11.3 Case Ownership Transfer [N]
The Case Owner MAY transfer ownership to another actor via
Offer(VulnerabilityCase) / Accept handshake routed through the CASE_MANAGER
(ADR-0053).
The Case Owner proposes the transfer to the CASE_MANAGER as Offer(Offer(VulnerabilityCase)), and the CASE_MANAGER makes the one Offer(VulnerabilityCase) the receiving actor answers (CM-21-005, ADR-0148).
On acceptance, the receiving actor acquires Case Owner authority and
the associated protocol responsibilities.
Open: cryptographic identity across a change of case manager
Authority over the canonical ledger follows the CASE_MANAGER role, not any
actor's name or Uniform Resource Identifier (URI). Transferring case ownership, or moving the
CASE_MANAGER role to a different actor, therefore does not by itself
re-key the ledger.
What remains open is key handover. A future case-encryption design must define how the keys protecting existing case content pass to the new role holder, and what a participant does with ledger entries it can no longer decrypt. This specification states no rule for either.
11.4 Participant Removal [N]
Removing a participant withdraws its entitlement to case content. It does not delete the participant from the case. The participant's record, its status history, and the ledger entries it authored stay in the case, and the record carries a removal fact (CM-31-001). A removed participant is inert: it receives no case content, and it has no authority to act on the case.
Only the Case Owner requests a removal.
It sends Remove(CaseParticipant, target=VulnerabilityCase) to the CASE_MANAGER, which applies it (CM-31-004).
The CASE_MANAGER MUST refuse the request when the sender does not hold the Case Owner role, when it names the CASE_MANAGER's or the Case Owner's participant, or when it names no participant of the case.
A second removal of an already-removed participant is a no-op, and the CASE_MANAGER reports it as skipped.
The CASE_MANAGER MUST NOT remove a participant on its own initiative.
A participant that wants to stop taking part leaves the case with Leave(VulnerabilityCase); that is not removal.
An accepted removal is one ledger entry: the Case Owner's received Remove(CaseParticipant), committed by the CASE_MANAGER as received (CM-31-005).
The CASE_MANAGER commits no second entry of its own for the same removal.
After the commit, the CASE_MANAGER sends the removed participant a direct Remove(CaseParticipant) that names it, with actor set to the CASE_MANAGER and attributedTo set to the Case Owner (CM-31-006).
This notice is delivery, not a record, and is not ledgered.
The removal entry's fan-out selects its recipients before the removal is applied, so the removed participant receives that entry too.
It is the last ledger entry the removed participant receives, by fan-out or by replay, until it is reinstated.
Every replica, the removed participant's own included, applies the removal from that ledger entry (CM-31-007).
A replica MUST NOT apply a removal from a directly received Remove(CaseParticipant): it stores the notice and takes the removal from the ledger.
Removal does not change the participant's embargo consent (CM-31-008). A removed signatory stays bound by the embargo it accepted. A removed participant receives no Invite of any kind: not a stub Invite, not a full-case Invite, and not an embargo Invite (CM-31-013).
Removal is reversible.
The Case Owner reinstates a removed participant by sending Add(CaseParticipant, target=VulnerabilityCase) to the CASE_MANAGER (CM-31-011).
The request follows the removal pipeline: only the Case Owner may send it, the received Add is the one ledger entry, the CASE_MANAGER sends the participant a direct Add(CaseParticipant) notice naming it, and every replica applies the reinstatement from the ledger entry.
Reinstatement clears the removal fact.
The participant does not accept again, because it never withdrew.
The CASE_MANAGER MUST refuse an Add that names a participant that is not removed or that never joined the case: Add does not seat a new member, and an actor joins only by accepting its stub Invite.
For the same reason the CASE_MANAGER does not send Add(CaseParticipant) when an invitee accepts its stub Invite; the ledger holds the messages exchanged (the Invite, the CASE_MANAGER's creation of the inert record, then the invitee's Accept, which is the entry for its consent and joined mark), and every replica applies what the entries carry (CM-31-012).
When the reinstated participant is active again, the CASE_MANAGER sends it every ledger entry committed after its removal entry, in log order, so its copy of the ledger joins the chain with no gap (CM-10-006). A participant reinstated into a case whose active embargo it has not accepted stays inert. The CASE_MANAGER sends it that embargo's Invite, and its catch-up waits until it accepts.
See also
12. Conformance [N]
12.1 Conformance Model Overview
Conformance is two-dimensional: capability sets (what protocol machinery an implementation provides) and a role profile (which roles it claims). A conformance claim names capability sets and roles directly:
CapabilitySet [+ CapabilitySet ...] / Role [+ Role ...]
Examples: Case Observer / Reporter, Case Observer / Vendor, Case Observer / Vendor + Deployer,
Case Observer + Case Decision + Case Hosting / Coordinator + Case Owner.
The Case Observer capability set is required for all participation. Role obligations are additive and orthogonal: no role subsumes another.
Capability set names and role names come from §12.2 Capability Sets and §12.3 Role Taxonomy respectively.
Roles and capability expectations. The relationship between roles and capabilities is bidirectional. An implementation must have the capability prerequisites for a role before it can be assigned that role (§12.3.1 Process Roles). Conversely, holding a role in a case creates an expectation that the implementation has those capabilities — other participants act on that assumption. See §11.1 Role Assignment for the role assignment gatekeeping rules.
Capability sets are not the same as conformance test layers
This project uses two distinct schemes, and they must not be conflated:
- Capability sets, defined here, describe what an implementation provides — a claim an implementer makes about their software.
- Conformance test layers (L1–L4), used in the behavioral conformance material, describe what a test verifies — syntax, semantics, behavior, and internal process structure. These are orthogonal: a Case Observer implementation is tested at layers L1 through L3.
A conformance claim names capability sets; a test result names a layer. The two are answering different questions and are not interchangeable.
12.2 Capability Sets
A capability set is a group of obligations an implementation takes on. Three are defined: Case Observer, which every participant must provide, and Case Decision and Case Hosting, which add the obligations of deciding for a case and of running one. The Case prefix marks each as a capability set — a property of software — distinct from the similarly-named roles it serves.
Most of what a capability set requires is expressed as maintaining a state machine, so that phrase needs pinning down first.
What 'implement a state machine' means
Implementing a state machine has two components:
- Track: maintain a local value for the machine, and update it as incoming messages report transitions.
- Drive: cause a transition that the participant's roles authorize, and announce it so the other participants learn of it.
The two are separate obligations. A participant that tracks but does not drive leaves the others to discover its state changes some other way. A participant that drives but does not track acts on case state it does not know. Which transitions a participant may drive depends on its roles (§12.4 Role-Specific Normative Requirements); every participant tracks.
Four dimensions, five state machines
Case State (CS) is a compound of two independent axes, so four tracked dimensions are realized as five state machines:
- VFD — Vendor Aware, Fix Ready, Fix Deployed. Participant-specific: it records what one participant has done (§8.1 VFD — Vendor Aware, Fix Ready, Fix Deployed).
- PXA — Public Aware, Exploit Public, Attacks Observed. Participant-agnostic: it records the state of the world, and it is shared case state (§8.2 PXA — Public Aware, Exploit Public, Attacks Observed).
This specification says "four dimensions" when describing what is tracked and "five state machines" when describing what an implementation must provide. Both counts are correct; they count different things.
Case Observer capability set
The Case Observer capability set is the participation floor. Every actor that participates in any Vultron case MUST implement it.
There is no sub-Observer participation level. An actor that accepts a case invitation has committed to Observer behavior from that point. It must track state and notify others of its own transitions.
- MUST maintain all five state machines — RM, EM, PEC, VFD, PXA — for itself: it MUST hold a current value for each, and MUST drive the transitions its roles authorize
- SHOULD also track the states of the other participants in the case, to inform its own decisions. This is a SHOULD rather than a MUST because correct operation of the protocol MUST NOT depend on any participant holding perfect information about the others
- MUST send the messages appropriate to its claimed roles when its own state transitions occur
- MUST receive and update local state when notified of other participants' transitions
- MUST participate in embargo negotiation: responding to
Invite(Event), and recording consent or refusal via PEC - MUST route all case-scoped messages through the CASE_MANAGER (§5.4.2 Routing Topology)
- MAY report PXA observations; no VFD drive obligations unless a role extension set adds them
Why there is no sub-Observer level
A monitoring-only actor might seem to need message parsing and nothing else. Two things rule that out. An actor that does not track its own PEC state cannot take part in embargo negotiation, and the CASE_MANAGER decides what to send it on the basis of that state — so an untracked participant cannot be reliably served. An actor that does not track RM has no basis for evaluating case status. Meaningful use of Vultron data requires the full Observer set.
The protocol governs what the CASE_MANAGER sends, not what a recipient does with information it already holds. Vultron gates dissemination; it cannot constrain display.
A parse-only tool is not a case Participant: it holds no CaseParticipant
record and no case is obliged to deliver anything to it.
Role-specific drive obligations
All Observer participants track all five machines. Which transitions a participant drives depends on its role extension set: a Vendor drives its own VFD transitions; a Reporter drives RM; any participant may report PXA observations. See §12.3 Role Taxonomy and §12.4 Role-Specific Normative Requirements.
Case Decision capability set
The Case Decision capability set defines Case Owner governance capabilities. It is separable from the Case Hosting capability set.
- Case Observer capability set, plus:
- Status updates MUST be adopted without requiring an external approval gate (the Case Owner's own updates are authoritative)
- MUST be able to drive shared EM transitions
- MUST be able to transfer case ownership via the
Offer(VulnerabilityCase)/Accepthandshake
A human Coordinator typically provides the Case Decision capability set while a service actor provides Case Hosting.
Case Hosting capability set
The Case Hosting capability set defines Case Manager infrastructure capabilities. It is separable from the Case Decision capability set.
- Case Observer capability set, plus:
- MUST hold the CASE_MANAGER role for each case it hosts, and therefore MUST implement the single-writer authority rules of §5.4.1 Single-Writer Authority
- MUST maintain the authoritative canonical case ledger and replicate it to
participants via
Announce(CaseLedgerEntry) - MUST implement multi-party case management: participant invitation, acceptance, role assignment, and case ownership operations
- MUST implement the two-seam status adoption model (§10.3 Status Adoption: The Two-Seam Model), including the canonical-write-before-side-effects ordering
- MUST deliver full case content only when the §9.7 Gating Full Case Delivery gate is satisfied
Ledger replication mechanics are specified separately
This specification states the obligation: an implementation holding the
Case Hosting capability set MUST maintain the canonical ledger and replicate it
via Announce(CaseLedgerEntry). The single-writer-plus-fan-out model is normative:
one CASE_MANAGER holds exclusive write authority and sends each entry to every
participant actor.
The mechanics of that replication — hash-chaining, gap detection and ordering guarantees — are specified in a companion Vultron Ledger Replication Specification, which is still in draft and is not yet published alongside this document. An implementation cannot demonstrate conformance to the replication mechanics from this document alone. The reasoning for splitting them is recorded in ADR-0077.
Distributed consensus among several case managers is a possible future extension and is out of scope here.
Named configurations
Common combinations of capability sets have names because they appear frequently in real deployments. These names are informative; conformance claims use the full capability set list.
| Configuration | Capability sets | Roles |
|---|---|---|
| Hosting Coordinator | Case Observer + Case Decision + Case Hosting | Coordinator + Case Owner |
| Self-coordinating Vendor | Case Observer + Case Decision + Case Hosting | Vendor + Deployer + Case Owner |
| Bug Bounty Platform | Case Observer + Case Hosting | Case Manager (Case Decision optional) |
A Hosting Coordinator is a type:service actor that holds both CASE_OWNER
and CASE_MANAGER roles. It decides and executes without a separate human
approval step for its own status updates.
12.3 Role Taxonomy
12.3.1 Process Roles
Process roles define what an actor does within a case and which protocol transitions it is authorized to drive. An actor may hold multiple process roles.
| Role | Protocol capability |
|---|---|
| Reporter | Initiates cases; drives RS; authoritative source of the original report |
| Vendor | Drives its own VFD transition f→F (fix ready, CF) |
| Deployer | Drives its own VFD transition d→D (fix deployed, CD) |
| Coordinator | Drives case participant management; coordinates multi-party disclosure |
| CVE Numbering Authority (CNA) | May assign CVE IDs directly; a participant without the role delegates assignment. Orthogonal to the other roles, and typically co-held with Coordinator or Vendor |
| Observer | Holds no drive obligations for VFD; may report PXA observations (§12.4.2 Participant-Agnostic CS Transitions (PXA)) |
Capability prerequisites. Every case Participant — whatever its roles — MUST implement the Case Observer capability set (§12.2 Capability Sets, §12.3.3 Roles and Capability Sets Are Independent). Role extension sets add obligations on top of that floor; they do not substitute for it. An implementation SHOULD verify that an actor has the capability prerequisites for a role before completing a role assignment (§11.1 Role Assignment).
This specification states the per-role obligations in §12.4 Role-Specific Normative Requirements. It does not yet state a complete capability prerequisite for every role.
Note on Reporter
Reporters are most often also the discoverer of the vulnerability, but the protocol is concerned with who reported it, not who found it. The identity of the original discoverer may be recorded in the report itself or in case Notes, but nothing in the technical protocol hinges on that distinction. Reporter is the protocol-salient role from first contact.
12.3.2 Protocol Coordination Roles (protocol authority)
These roles confer specific protocol-layer authority and are distinct from process roles. They describe what an actor controls within the protocol machinery, not what it does in the world.
| Role | Protocol authority |
|---|---|
| Case Owner | Authoritative decision-maker for a case; status updates are treated as authoritative without requiring approval; drives shared EM transitions |
| Case Manager | Holds the case's single-writer authority (§5.4.1 Single-Writer Authority): writes the canonical case ledger, relays case-scoped messages, and manages the roster on the Case Owner's behalf (§2.2 Roles). Authority follows the role, not the actor that holds it; the role is commonly co-held with Coordinator but need not be (§12.3.3 Roles and Capability Sets Are Independent) |
Delegation scenarios: Protocol responsibilities may transfer during a case
lifecycle. For example, a Reporter who initially creates a case may delegate
coordination to a Coordinator (reporter → coordinator hand-off), or a primary
Vendor may bring in additional Vendors as the case grows. When a Case Owner
transfers ownership (via Offer(VulnerabilityCase) / Accept handshake routed
through the CASE_MANAGER), the receiving actor acquires Case Owner authority and
the associated protocol responsibilities.
Open architectural question: key handover
Moving the CASE_MANAGER role to a different actor raises an unresolved
key-handover question for future case-encryption designs. The question is
stated once, at §11.3 Case Ownership Transfer.
12.3.3 Roles and Capability Sets Are Independent
A role is a position within a case. A capability set is a property of software. Mixing them produces contradictions, so the relationship is stated explicitly:
- Every case Participant — whatever its roles — MUST implement the Case
Observer capability set. Holding a role means having a
CaseParticipantrecord, an RM state, and therefore tracking obligations. - A parse-only actor is not a case Participant: it holds no role and no case owes it delivery.
- An implementation holding the Case Hosting capability set additionally hosts the CASE_MANAGER role and is obliged to run the ledger. Case Hosting is commonly co-held with the Coordinator role. Ledger authority follows the CASE_MANAGER role the implementation holds — not its hosting location, and not any actor name (ADR-0088).
Observer is a participant role, not a passive state
An Observer role holder that is in a case implements the full Case Observer capability set: it has an RM state, it is subject to embargo consent, and it may report PXA observations. The Observer role is distinguished by holding no VFD drive obligations — not by being exempt from state tracking.
Observer role admission follows the standard Invite / Accept(Invite) path.
Role semantics are normative per ADR-0057; see the note at §12.3.1 Process Roles.
12.4 Role-Specific Normative Requirements
12.4.1 Participant-Specific CS Transitions (VFD)
VFD records what a specific participant has done, so authority to cause a VFD transition is scoped to the participant that did it.
Recall: vendor/fix/deploy states
| State | Vendor Aware | Fix Ready | Fix Deployed |
|---|---|---|---|
vfd |
no | no | no |
Vfd |
yes | no | no |
VFd |
yes | yes | no |
VFD |
yes | yes | yes |
Full definitions are in §8.1 VFD — Vendor Aware, Fix Ready, Fix Deployed.
For self-reported VFD transitions (a participant advancing its own state via the local trigger path):
f→F(VFd, fix ready): MUST only be driven by an actor holding Vendor; MUST fail when Vendor is absentd→D(VFD, fix deployed): MUST only be driven by an actor holding Deployer; MUST fail when Deployer is absent- A Vendor-only actor MUST NOT advance past
VFdwithout also holding Deployer
Publishing a fix is not deploying it
A vendor that publishes a patch has made the patch available. It has not deployed it: deployment happens in the systems that run the product, and it is a Deployer's act. Fix Ready and Fix Deployed are therefore distinct protocol facts, and one MUST NOT be inferred from the other.
The same separation applies to public awareness. A notification that information is public sets Public Aware, which is world state. It MUST NOT set Fix Deployed, which is participant state requiring the Deployer role. An implementation that treats publication as deployment produces an unauthorized VFD advance.
Open question: who may assert that a vendor became aware
The first VFD transition — v→V, vendor becomes aware — has no
trigger-side specification and no implementation. Design intent is a
third-party assertion ("I notified this Vendor"), not a self-report.
Open questions: who may assert it, whether the subject Vendor may dispute it, and whether transport-level delivery success, a Hypertext Transfer Protocol (HTTP) 200 response, suffices to constitute the assertion.
Do not infer a rule from this document's silence.
12.4.2 Participant-Agnostic CS Transitions (PXA)
PXA records the state of the world rather than the state of any participant, so these transitions are not scoped to a role. Anyone may notice that a vulnerability has become public.
Recall: public/exploit/attacks axes
| Axis | Not yet | Set | Meaning when set |
|---|---|---|---|
| Public Aware | p |
P |
The vulnerability is known outside the case participants |
| Exploit Public | x |
X |
An exploit for the vulnerability has been published |
| Attacks Observed | a |
A |
Attacks exploiting the vulnerability have been seen |
Full definitions are in §8.2 PXA — Public Aware, Exploit Public, Attacks Observed.
PXA records the state of the world rather than of any participant, so any participant MAY report a PXA observation. These transitions are role-ungated: information may become public, exploits may appear, and attacks may be observed independently of anything a case participant does or causes.
p→P(publicly aware): any participant may reportx→X(exploit public): any participant may reporta→A(attacks observed): any participant may report
Reporting is not adoption. A reported observation is a claim. Whether it becomes canonical case state, and whether it triggers embargo teardown, is decided by the CASE_MANAGER, following the two-seam model of §10.3 Status Adoption: The Two-Seam Model and subject to Case Owner authorization by default. The role rule here — who may report — is deliberately separate from the authorization rules there — what the CASE_MANAGER does with a report.
Informative: automated observers
An actor may be an automated service that monitors external sources — threat feeds, public disclosures, vulnerability databases — and reports what it finds into a case. Such a service is not a distinct protocol role: it reports PXA observations on the same terms as any other participant, and its reports are adopted through the same authorization as theirs (§10.3 Status Adoption: The Two-Seam Model).
12.4.3 CVE ID Assignment
An actor holding the CVE Numbering Authority (CNA) role MUST be able to assign CVE IDs, which requires evaluating a vulnerability against the eligibility criteria before assignment.
An actor that does not hold the CNA role MUST delegate ID assignment. It MAY delegate to an external CNA service, or to another participant in the same case that holds the CNA role — a coordinator or vendor acting as a CNA can perform the assignment without the case leaving the protocol.
Eligibility criteria posture. This specification does not normatively cite a specific edition of the CNA Operational Rules, nor does it treat eligibility checks as fully implementation-defined. Instead, the reference implementation follows CNA Operational Rules v4.1.0 as the conformance baseline. Adopting a newer edition requires updating the spec and the implementing call-out. This avoids coupling the Request for Comments (RFC) to an independently-versioned external document's release cycle while remaining transparent about which edition the reference implementation follows.
Eligibility checking is a single logical capability: the full set of criteria applied as a unit against one rules edition. The protocol treats it as one decision with one outcome, and does not specify how an implementation reaches that decision.
12.4.4 Case Owner Authority
Adopting a reported status as canonical case state requires the Case Owner's authorization. The Case Owner is the party whose disclosure decision the case exists to serve, so it is the party entitled to decide what the case asserts. The CASE_MANAGER MUST obtain that authorization by default before adopting a participant's reported status (§10.3 Status Adoption: The Two-Seam Model).
One case is exempt. Where the Case Owner is itself the sender, the CASE_MANAGER MUST adopt the status without seeking approval: asking the Case Owner to approve its own report would be circular.
12.5 Conformance Testing Approach
Conformance is judged from outside the implementation. A test supplies a sequence of messages and an input state, and checks the messages the implementation emits and the state it reaches. It does not inspect internal structure.
Each test case is therefore a message-sequence scenario paired with the state outcome it expects.
Some capabilities that define a role are not protocol-observable at all. Developing a fix, deploying a fix and publishing an advisory happen outside the protocol. Conformance verifies the protocol messages that this work produces, not the work itself.
Conformance testing is organized in four layers, which describe what a test verifies. Layers are orthogonal to the capability sets of §12.2 Capability Sets, which describe what an implementation provides (see the warning in §12.1 Conformance Model Overview):
| Layer | Verifies |
|---|---|
| L1 — Syntax | Messages are well-formed against the wire format (§5 Syntactic Layer — Wire Format) |
| L2 — Semantics | Each message drives the correct state transition (§4 Semantic Layer — Message Meanings, §6 Report Management (RM) State Machine through §11 Participant Lifecycle Within a Case) |
| L3 — Behavior | Correct observable outputs: right messages emitted and states reached, given input state plus received message |
| L4 — Process | Correct internal decision structure (e.g. precondition before state write before side-effect) |
L4 is only enforceable against a reference implementation and is therefore outside the scope of independent conformance claims. Some process ordering surfaces at L3 where the output sequence is itself observable — the canonical-write-before-side-effects rule of §10.3 Status Adoption: The Two-Seam Model being the clearest case.
See also
- Process Models — the behavioral material the L2 and L3 layers test against
12.6 Capability Shapes
Capability shapes describe optional, pluggable capabilities an implementation may connect to its own decision points. They are not part of the Case Observer, Case Decision or Case Hosting capability sets, and a conformance claim does not state which shapes an implementation provides.
Where an implementation does provide a capability shape, it MUST satisfy that shape's contract. The shapes and their contracts are described in Annex G Capability Shapes.
13. IANA and Namespace Considerations [I]
Vultron vocabulary namespace. The Vultron ActivityStreams 2.0 (AS2) vocabulary namespace is
https://certcc.github.io/Vultron/ns. This is the initial, provisional Uniform Resource Identifier (URI),
hosted on GitHub Pages. A permanent URI registration (for example, via a
w3id.org redirect) is planned for a future version of this specification.
See ADR-0069.
JSON-LD context document. The normative JavaScript Object Notation for Linked Data (JSON-LD) context is identified by
https://certcc.github.io/Vultron/ns/context.jsonld. Implementations MUST
use this URI as the @context value for all outbound Vultron messages (§5.5 Serialization).
That URI does not currently dereference. The context document is withheld from publication until it is served at the wire-versioned path that ADR-0106 defines. At this version the namespace is therefore a stable identifier, not a retrievable document. An implementation MUST therefore cite the URI without depending on resolving it, and MUST NOT require a successful fetch to process a message.
AS2 extension type naming conventions. Vultron type names follow PascalCase
without an as_ prefix in wire output (for example, "type": "VulnerabilityCase"
rather than "type": "as:VulnerabilityCase"). The Vultron JSON-LD context
declares all type names, so implementations citing only the Vultron context URI
do not need to separately declare the AS2 namespace.
IANA registration. No new IANA registrations are required by the current version of this specification. If the Vultron namespace moves to a standards-track document, an IANA media type registration for the Vultron JSON-LD profile may be appropriate at that time.
14. Security Considerations [N/I]
14.1 Trust Model
Actor identity. The protocol uses actor URIs as process identifiers. An
actor URI is the identity anchor for all messages sent by that actor, and for
the canonical case ledger if the actor holds the CASE_MANAGER role. Implementations
MUST treat actor URI equality as identity equality: two messages with the same
actor URI are from the same actor.
Verification. The protocol does not currently mandate a specific identity verification mechanism. The reference implementation anticipates HTTP Signatures (as used in ActivityPub) as the authentication layer for inbound messages. A future version of this specification is expected to normatively require HTTP Signatures at the transport layer.
Case bootstrap as the trust establishment mechanism. The
Create(VulnerabilityCase) message signed by the originating actor is the
trust root for a case. Late joiners receive a case snapshot and prior ledger
entries from the CASE_MANAGER. A late joiner trusts the CASE_MANAGER's delivery
because it trusts the Create message that introduced the CASE_MANAGER. This
chain is the minimal trust model; cryptographic ledger integrity (hash-chaining)
provides tamper detection.
See also
- §4.5 Trust and Bootstrap Semantics (trust and bootstrap semantics)
- §5.4 Addressing and Channels (addressing and channels)
14.2 Embargo Integrity
Protocol adherence. An active embargo is an agreement, not a technical enforcement. The protocol provides the signaling infrastructure — Participant Embargo Consent (PEC) state tracks which participants have consented, §9.5 Embargo Traffic Reaches Non-Signatories ensures all participants receive meta-protocol messages — but it cannot prevent a participant from disclosing outside the protocol.
Defection. If a participant discloses publicly while an embargo is ACTIVE,
the CP (public awareness) message will trigger the embargo teardown cascade
(§10.3 Status Adoption: The Two-Seam Model). The teardown is a state machine consequence, not a penalty mechanism.
The protocol has no built-in penalty for defection; enforcement is an
out-of-band organizational matter.
Confidentiality gate. Sensitive case content is gated on embargo consent (§9.7 Gating Full Case Delivery).
While an embargo is in force, a participant that is not a signatory to it, because its consent row for that embargo is not AGREED, does not receive full case details.
This is the primary technical lever the protocol provides against inadvertent over-disclosure to non-consenting parties.
14.3 Replay and Idempotency
Message deduplication. Each ActivityStreams Activity carries a unique id.
Implementations MUST deduplicate inbound messages by id before processing
them. Duplicate delivery does not change state.
Ledger idempotency. The canonical case ledger is append-only and
hash-chained. A re-delivered Announce(CaseLedgerEntry) with a known entry
hash MUST be treated as a no-op. The ledger NAK path (Reject(CaseLedgerEntry))
and gap-fill replay provide recovery from missed entries.
14.4 Confidentiality
In-transit protection. The protocol does not currently mandate a specific in-transit encryption mechanism. Transport Layer Security (TLS) is the expected transport protection for HTTP delivery. A future version of this specification is expected to require TLS.
At-rest and end-to-end confidentiality. Content-level encryption — protecting case details from the CASE_MANAGER itself, or supporting future multi-party computation — is not specified by this version. Implementations SHOULD treat any unencrypted case content as accessible to whoever operates the CASE_MANAGER.
Scope. This section covers security requirements for the protocol as specified. Operational security measures (key management, access control, incident response for actor compromise) are out of scope for this specification.
15. References
15.1 Normative References
These documents are required to implement this specification.
| Reference | Title |
|---|---|
| RFC 2119 | Key words for use in RFCs to Indicate Requirement Levels |
| RFC 8174 | Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words |
| ActivityStreams 2.0 Core | Activity Streams 2.0, World Wide Web Consortium (W3C) Recommendation |
| ActivityStreams 2.0 Vocabulary | Activity Vocabulary, W3C Recommendation |
| JSON-LD 1.1 | JSON-LD 1.1: A JSON-based Serialization for Linked Data, W3C Recommendation |
| RFC 8259 | The JavaScript Object Notation (JSON) Data Interchange Format |
15.2 Informative References
These documents provide context. They are not required to implement this specification.
| Reference | Title |
|---|---|
| ActivityPub | ActivityPub, W3C Recommendation. The anticipated transport; not required by this version (§1.3 Relationship to Existing Standards) |
| ISO/IEC 29147:2018 | Information technology — Security techniques — Vulnerability disclosure |
| ISO/IEC 30111:2019 | Information technology — Security techniques — Vulnerability handling processes |
| CERT Guide to CVD | The CERT Guide to Coordinated Vulnerability Disclosure. Source of this specification's definition of vulnerability (§2.1 Actors, Participants and Cases) |
| CVE Program | Common Vulnerabilities and Exposures (CVE) Numbering Authority operational rules, referenced at §12.4.3 CVE ID Assignment |
| RFC 7033 | WebFinger. The anticipated participant-discovery mechanism (§5.6 Transport Layer) |
| HTTP Signatures | Signing HTTP Messages. The anticipated authentication layer (§14.1 Trust Model) |
16. Informative Annexes
The annexes below illustrate and explain the protocol. Nothing in them is normative: where an annex and a normative section appear to disagree, the normative section governs.
Annex A — Worked Example: Single-Vendor CVD [I]
This annex traces one two-party case from first contact to closure. Each step names the message sent and the state transition it reports, so the sequence can be read against the state machine sections (§6 Report Management (RM) State Machine through §9 Participant Embargo Consent (PEC) State Machine).
Four embargo outcomes are shown — accepted, rejected, countered, and accepted then revised — because embargo negotiation is where two-party cases most often diverge.
A Finder Becomes a Reporter
As mentioned in RM Interactions, Finders have a few hidden state transitions before the Coordinated Vulnerability Disclosure (CVD) process really begins. An example of this is shown in the figure below. The Finder must discover, validate, and prioritize their finding before initiating the CVD process.
---
title: Finder Pre-Report Steps
---
sequenceDiagram
actor Finder
Finder ->> Finder: Discover
Finder ->> Finder: Validate
Finder ->> Finder: Prioritize
Finders become Reporters when they report a vulnerability to someone else. The next figure shows a Finder sending a report (RS) in conjunction with an embargo proposal (EP) to a Vendor. The Vendor receives the report and updates their state accordingly. Then the Vendor replies to acknowledge receipt of the report and the embargo proposal, and confirms that they (i.e., the Vendor) are aware of the report (RK, EK, and CV, respectively). Note that the EK response is intended to convey receipt of the embargo proposal (EP) but does not constitute acceptance of the proposal. We will discuss that in the next subsection.
---
title: Finder Submits Report and Embargo Proposal
---
sequenceDiagram
actor Finder
actor Vendor
Finder ->> Finder: Discover, Validate, Prioritize
Finder ->> Vendor: RS, EP
note right of Vendor: Report Received
note right of Vendor: Embargo Proposed
note right of Vendor: Vendor Aware
Vendor -->> Finder: RK, EK, CV
Vendor Evaluates Embargo
In this section, we show a variety of responses a Vendor might have to an embargo proposal.
Vendor Accepts Embargo
First is a basic accept sequence in which the Vendor accepts the proposed embargo and tells the Reporter this through an EA message. The Reporter acknowledges this with an EK in response.
---
title: Vendor Accepts Embargo Proposal
---
sequenceDiagram
actor Reporter
actor Vendor
Reporter ->> Vendor: EP
note right of Vendor: Embargo Proposed
Vendor -->> Reporter: RK
Vendor ->> Reporter: EA
activate Reporter
activate Vendor
note right of Vendor: Embargo Active
Reporter -->> Vendor: EK
deactivate Reporter
deactivate Vendor
Vendor Rejects Embargo
Next we show a rejected proposal. As above, this is a simple sequence where the Vendor indicates their rejection of the proposal with an ER message, and the Reporter acknowledges this with an EK message.
---
title: Vendor Rejects Embargo Proposal
---
sequenceDiagram
actor Reporter
actor Vendor
Reporter ->> Vendor: EP
note right of Vendor: Embargo Proposed
Vendor -->> Reporter: EK
Vendor ->> Reporter: ER
note right of Vendor: Embargo Rejected
Reporter -->> Vendor: EK
Vendor Counterproposal
Here we demonstrate a Vendor embargo counterproposal. The Vendor responds to the Reporter's prior EP message with an EP message of their own. The Reporter initially acknowledges the counterproposal with an RK message and then evaluates it and accepts with an EA message. Finally, the Vendor acknowledges the acceptance with an EK message. Note, however, that there is no active embargo until the Reporter accepts it. This method of counterproposal might delay the establishment of an embargo.
---
title: Vendor Counterproposal
---
sequenceDiagram
actor Reporter
actor Vendor
Reporter ->> Vendor: EP
note right of Vendor: Embargo Proposed
Vendor -->> Reporter: RK
Vendor ->> Reporter: EP
Reporter -->> Vendor: RK
Reporter ->> Vendor: EA
activate Vendor
activate Reporter
note right of Vendor: Embargo Active
Vendor -->> Reporter: EK
deactivate Vendor
deactivate Reporter
Vendor Accepts then Proposes Revision
Yes, And...
"Yes-And" is a heuristic taken from improvisational theatre in which Participants are encouraged to agree with whatever their counterpart suggests and add to it rather than reject it outright. It serves as a good model for cooperation among parties who share an interest in a positive outcome.
Finally, the following diagram offers what we think is a better approach than a simple counterproposal. In this "Accept-then-Counter" sequence, we see that the Vendor initially accepts the Reporter's proposed embargo and immediately follows up with a revision proposal of their own. The difference is that by initially accepting the proposal, the Vendor ensures that they are in an active embargo state before attempting to renegotiate. The sequence shown here is intended to be consistent with the previous discussion surrounding default embargo strategies. One might think of this as the "Yes-And" rule for embargo negotiations.
---
title: Vendor Accepts then Proposes Revision
---
sequenceDiagram
actor Reporter
actor Vendor
Reporter ->> Vendor: EP
note right of Vendor: Embargo Proposed
Vendor -->> Reporter: RK
Vendor ->> Reporter: EA
activate Vendor
activate Reporter
note right of Vendor: Embargo Active
Reporter -->> Vendor: EK
Vendor ->> Reporter: EV
note right of Vendor: Embargo Revise
Reporter -->> Vendor: RK
Reporter ->> Vendor: EC
note right of Vendor: Embargo Active
Vendor -->> Reporter: EK
deactivate Vendor
deactivate Reporter
Vendor Sets Priority
Here we show two responses from a Vendor in the course of prioritizing a report.
Vendor Accepts Report
This figure shows a Vendor accepting the report for further work (presumably to develop a patch) with an RA message.
---
title: Vendor Accepts Report
---
sequenceDiagram
actor Reporter
actor Vendor
Vendor ->> Vendor: Receive, Validate, Prioritize
Vendor ->> Reporter: RA
note right of Vendor: Report/Case Accepted
Reporter -->> Vendor: RK
Vendor Defers Report
On the contrary, this figure shows the Vendor deferring the report with an RD message. In both cases, the Reporter acknowledges the Vendor's messages with an RK message.
---
title: Vendor Defers Report
---
sequenceDiagram
actor Reporter
actor Vendor
Vendor ->> Vendor: Receive, Validate, Prioritize
Vendor ->> Reporter: RD
note right of Vendor: Report/Case Deferred
Reporter -->> Vendor: RK
Annex B — Worked Example: Multi-Party CVD [I]
This annex extends Annex A to a case with a coordinator and a second vendor. It shows the coordinator acting as proxy for the reporter, a participant joining a case that already has an active embargo, and the teardown-publish-close sequence across three parties.
Coordination With a Coordinator
The next two diagrams show the process of a Reporter engaging a Coordinator, who, in turn, engages a Vendor. The process begins in the first diagram with the Reporter sending a report along with an embargo proposal to the Coordinator (\(RS,EP\)). The Coordinator acknowledges receipt with an \(RK,EK\) response. After evaluating the proposed embargo, the Coordinator accepts it with an EA message. The Coordinator proceeds to validate and prioritize the report, emitting an RV and RA along the way.
---
title: Reporter Engages Coordinator
---
sequenceDiagram
actor Reporter
actor Coordinator
Reporter ->> Coordinator: RS,EP
note right of Coordinator: Report Received
note right of Coordinator: Embargo Proposed
Coordinator -->> Reporter: RK,EK
Coordinator ->> Reporter: EA
activate Coordinator
activate Reporter
note right of Coordinator: Embargo Active
Reporter -->> Coordinator: EK
Coordinator ->> Reporter: RV
note right of Coordinator: Report Valid
Reporter -->> Coordinator: RK
Coordinator ->> Reporter: RA
note right of Coordinator: Report/Case Accepted
Reporter -->> Coordinator: RK
deactivate Coordinator
deactivate Reporter
Coordinator-as-proxy can be sub-optimal
In this scenario, we are showing the Coordinator acting as a proxy between the Reporter and the Vendor. While this reflects the way CVD has been practiced in the past, it is not necessarily the most efficient way to operate. CVD Platforms like VINCE have demonstrated that bringing CVD case participants into a shared space can make the process more efficient.
In the next diagram, the Coordinator now acts as a proxy for the Reporter, notifying the Vendor and passing along the embargo information through an \(RS,EP\) message of its own. The Vendor accepts the existing embargo (EA) and proceeds to validate (RV) and prioritize (RA) the report. Relevant responses from the Vendor are passed through to the Reporter. Having accepted the report for further work, the Vendor continues with creating a fix for the reported vulnerability. When complete, the Vendor conveys their readiness to the Coordinator, who in turn passes this information along to the Reporter through the CF message.
---
title: Coordinator Notifies Vendor
---
sequenceDiagram
actor Reporter
actor Coordinator
actor Vendor
Coordinator ->> Vendor: RS,EP
Vendor -->> Coordinator: RK,EK,CV
Coordinator ->> Reporter: CV
Vendor --> Vendor: Evaluate Embargo
Reporter -->> Coordinator: CK
activate Vendor
note right of Vendor: Embargo Active
Vendor ->> Coordinator: EA
activate Coordinator
Coordinator ->> Reporter: EA
activate Reporter
Vendor --> Vendor: Validate Report
Coordinator -->> Vendor: EK
Reporter -->> Coordinator: EK
note right of Vendor: Report Valid
Vendor ->> Coordinator: RV
Vendor --> Vendor: Prioritize Report
Coordinator -->> Vendor: RK
note right of Vendor: Report/Case Accepted
Vendor ->> Coordinator: RA
Vendor --> Vendor: Create Fix
Coordinator -->> Vendor: RK
note right of Vendor: Fix Ready
Vendor ->> Coordinator: CF
Coordinator ->> Reporter: CF
Coordinator -->> Vendor: CK
Reporter -->> Coordinator: CK
deactivate Vendor
deactivate Coordinator
deactivate Reporter
Embargo Teardown, Publish, and Close
Any Participant can initiate an embargo teardown. We happened to show the case where the Coordinator initiates it in the following diagram, sending an embargo termination message (ET) to all parties in the case (Reporter and Vendor in this scenario). Recipients of the ET message acknowledge receipt and update their EM state accordingly.
Embargo Teardown, Publication, and Closure Can Start with Any Participant
Note that for all three scenarios in this section, there is no specific order in which Participants must act. We could just as easily have shown the Reporter initiating an embargo teardown because of a leaked media report or the Vendor exiting an embargo early because they had their fix ready sooner than expected.
---
title: Coordinator Initiates Embargo Teardown
---
sequenceDiagram
actor Reporter
actor Coordinator
actor Vendor
Coordinator ->> Reporter: ET
Coordinator ->> Vendor: ET
note right of Vendor: Embargo Terminated
Reporter -->> Coordinator: EK
Vendor -->> Coordinator: EK
Embargo Termination is not Publication
Our protocol only sets a discrete end to the embargo period, it intentionally does not address a publication schedule. Once the embargo has been exited, any Participant may publish at any time. Participants might choose to coordinate publication schedules more closely, but there is nothing in the protocol to require it. With the recognition that more concise publication scheduling might be needed in some situations, we revisit this concern in Process Implementation Notes.
Publishing After Embargo Teardown
Once the embargo has been exited, any Participant may now publish. In the following figure, we show the Vendor publishing first. They notify the Coordinator that they have published using a CP message to convey that information about the vulnerability is now public. The Coordinator relays this information to the Reporter. Both the Reporter and the Coordinator publish their own reports shortly thereafter.
---
title: Publishing After Embargo Teardown
---
sequenceDiagram
actor Reporter
actor Coordinator
actor Vendor
Vendor --> Vendor: Publish
note right of Vendor: Public Aware
Vendor ->> Coordinator: CP
Coordinator ->> Reporter: CP
Reporter -->> Coordinator: CK
Reporter --> Reporter: Publish
Reporter ->> Coordinator: CP
Coordinator ->> Vendor: CP
Coordinator -->> Reporter: CK
Vendor -->> Coordinator: CK
Coordinator --> Coordinator: Publish
Coordinator ->> Reporter: CP
Coordinator ->> Vendor: CP
Reporter -->> Coordinator: CK
Vendor -->> Coordinator: CK
Report Closure is a Per-Participant Choice
Report closure is a per-Participant choice. We chose to show a simple case where all Participants agreed at approximately the same time that there was nothing further to be done. This will not always be the case, nor is it necessary.
Closing the Case
Having no further work to be done on the case, the Reporter closes their report and tells the Coordinator using an RC message in the next diagram. This prompts the Coordinator to review their outstanding tasks and decide to initiate the closure of their own report. In turn, the Coordinator relays this to the Vendor, who also closes their report.
---
title: Participants Close Their Reports
---
sequenceDiagram
actor Reporter
actor Coordinator
actor Vendor
Reporter --> Reporter: Close
Reporter ->> Coordinator: RC
Coordinator -->> Reporter: RK
Coordinator --> Coordinator: Close
Coordinator ->> Vendor: RC
Vendor -->> Coordinator: RK
Vendor --> Vendor: Close
note right of Vendor: Case Closed
Vendor ->> Coordinator: RC
Coordinator -->> Vendor: RK
Runnable scenarios
The reference implementation ships these flows as executable scenarios, which are the most current statement of what it supports:
rv_demo.py— Reporter → Vendor (two-party)rcv_demo.py— Reporter → Coordinator → Vendorrvcv_handoff_demo.py— Coordinator hand-off- scenario inventory — the full list
Annex C — Notation Reference [I]
The conventions this specification uses for state names and case-state notation are stated at §1.4 Document Conventions. This annex collects the mathematical and diagram notation used in the formal treatment.
Mathematical Notation
All of these definitions assume the standard Zermelo-Fraenkel set theory. The following notation is used:
Set Theory Symbols
| Symbol | Meaning |
|---|---|
| \(\{ \dots \}\) | depending on the context: (1) an ordered set in which the items occur in that sequence, or (2) a tuple of values |
| |\(x\)| | the count of the number of elements in a list, set, tuple, or vector \(x\) |
| \(\subset,=,\subseteq\) | the normal proper subset, equality, and subset relations between sets |
| \(\in\) | the membership (is-in) relation between an element and the set it belongs to |
| \(\prec\) | the precedes relation on members of an ordered set: \(\sigma_i \prec \sigma_j \textrm{ if and only if } \sigma_i,\sigma_j \in s \textrm{ and } i < j\) where \(s\) is an ordered set |
| |\(X\)| | the size of (the number of elements in) a set \(X\) |
| \(\langle X_i \rangle^N_{i=1}\) | a set of \(N\) sets \(X_i\), indexed by \(i\); used in the Formal Protocol in the context of Communicating Finite State Machines, taken from the article On Communicating Finite State Machines by Brand and Zafiropulo |
Logic Symbols
| Symbol | Meaning |
|---|---|
| \(\implies\) | implies |
| \(\iff\) | if-and-only-if (bi-directional implication) |
| \(\wedge\) | the logical AND operator |
| \(\lnot\) | the logical NOT operator |
Directional Messaging Symbols
| Symbol | Meaning |
|---|---|
| \(\rightharpoonup{}\) | a message emitted (sent) by a process |
| \(\leftharpoondown{}\) | a message received by a process |
DFA Symbols
| Symbol | Meaning |
|---|---|
| \(\xrightarrow{}\) | a transition between states, usually labeled with the transition type (e.g., \(\xrightarrow{a}\)) |
| \((\mathcal{Q},q_0,\mathcal{F},\Sigma,\delta)\) | specific symbols for individual DFA components that are introduced when needed in Chapters |
| \(\Big \langle { \langle S_i \rangle }^N_{i=1}, { \langle o_i \rangle }^N_{i=1}, { \langle M_{i,j} \rangle}^N_{i,j=1}, { succ } \Big \rangle\) | formal protocol symbols that are introduced at the beginning of the Formal Protocol |
Diagram Notation
A variety of diagramming techniques are used throughout the documentation.
State Diagrams
Depictions of DFA as figures use common state diagram symbols, as shown in the example below.
stateDiagram-v2
direction LR
[*] --> q0
q0 --> q1 : a
q0 --> q2 : b
q1 --> q2 : a
q1 --> q1 : b
q2 --> q0 : a
q2 --> q1 : b
q2 --> [*]
Sequence and Class Diagrams
Sequence and class diagrams follow Unified Modeling Language (UML) conventions
---
title: Sequence Diagram Example
---
sequenceDiagram
participant A as Alice
participant B as Bob
A ->> B: Authentication Request
B->>A: Authentication Response
---
title: Class Diagram Example
---
classDiagram
Class01 <|-- Class02
Class03 *-- Class04
Class05 o-- Class06
Class07 .. Class08
Behavior Tree Diagrams
A few additional notation details specific to Behavior Trees are introduced when needed.
Annex D — Possible Case Histories [I]
Case State has 32 compound states, but far fewer orderings among them are reachable: a fix cannot be deployed before it is ready, and vendors learn of a vulnerability no later than the public does. Applying those constraints to the six case-state events reduces 720 naive orderings to 70 possible case histories.
This annex derives that result. It is informative: this specification does not yet state the ordering constraints normatively (§8.3 Case State as a Compound Tuple).
Sequences of Events and Possible Histories in CVD
This page is not normative
This page is not considered a core part of the Vultron Protocol as proposed in the main documentation. Although within the page we might provide guidance in terms of SHOULD, MUST, etc., the content here is not normative.
In CS Input Symbols, we began by identifying a set of events of interest in CVD cases. Then we constructed a state model describing how the occurrence of these events can interact with each other. In this section, we look at paths through the resulting state model.
Sequences of Events
Before we can discuss the possible histories of CVD, we need to define the concept of a sequence of events.
Sequences Formally Defined
A sequence \(s\) is an ordered set of some number of events \(\sigma_i \in \Sigma\) for \(1 \leq i \leq n\) and the length of \(s\) is \(|s| \stackrel{\mathsf{def}}{=}n\).
In other words, a sequence \(s\) is an input string to the DFA defined in the CVD Case State Model.
Case Histories
CS Model Input Symbols (\(\Sigma^{cs}\)) Defined
Here we diverge somewhat from the notation used for the RM and EM models, which use lowercase letters for transitions and uppercase letters for states. Because CS state names already use both lowercase and uppercase letters, here we use a bold font for the symbols of the CS DFA to differentiate the transition from the corresponding substate it leads to: e.g., \(vfd\cdot\cdot\cdot \xrightarrow{\mathbf{V}} Vfd\cdot\cdot\cdot\).
Armed with the definition of a sequence, we can now define a history.
Vulnerability Disclosure Case History Formally Defined
A vulnerability disclosure case history \(h\) is a sequence \(s\) containing one and only one of each of the symbols in \(\Sigma\); by definition \(|h| = |\Sigma| = 6\). Note this is a slight abuse of notation; \(|\textrm{ }|\) represents both sequence length and the cardinality of a set.
where two members of the set \(\Sigma\) are equal if they are represented by the same symbol and not equal otherwise.
The set of all potential histories, \(\mathcal{H}_p\), is a set of all the sequences \(h\) that satisfy this definition.
The Possible Histories of CVD
Given that a history \(h\) contains all six events \(\Sigma\) in some order, there could be at most 720 (\(_{6} \mathrm{P}_{6} = 6! = 720\)) potential histories. However, because of the causal requirements outlined in CS Transitions, we know that Vendor Awareness (V) must precede Fix Ready (F) and that Fix Ready must precede Fix Deployed (D).
The DFA developed in CS Process Model provides the mechanism to validate histories: a history \(h\) is valid if the DFA accepts it as a valid input string. Once this constraint is applied, only 70 possible histories \(h \in \mathcal{H}p\) remain viable. We denote the set of all such valid histories as \(\mathcal{H}\) and have \(|\mathcal{H}| = 70\). The set of possible histories \(\mathcal{H}\) corresponds to the 70 allowable paths through \(\mathcal{Q}^{CS}\) as can be derived from the CS transition function \(\delta^{CS}\) in CS Transitions. and the diagram in Case State Model Definition.
Other Definitions Used in the Table Below
- The skill ranking function on the histories is defined in Discriminating Skill from Luck.
- The desirability of the history (\(\mathbb{D}^h\)) is defined in On the Desirability of Possible Histories.
- The expected frequency of each history \(f_h\) is explained in Reasoning Over Histories.
Table of Possible Histories
The set of possible histories \(\mathcal{H}\) is listed exhaustively in the table below.
| # | \(h \in \mathcal{H}\) | rank | \(\mathbb{D}^h\) count | \(f_h\) |
|---|---|---|---|---|
| 0 | (A, X, P, V, F, D) | 1 | 0 | 0.0833 |
| 1 | (A, P, V, X, F, D) | 2 | 2 | 0.0417 |
| 2 | (A, V, X, P, F, D) | 3 | 2 | 0.0278 |
| 3 | (X, P, V, A, F, D) | 4 | 3 | 0.1250 |
| 4 | (V, A, X, P, F, D) | 5 | 3 | 0.0208 |
| 5 | (P, V, A, X, F, D) | 6 | 4 | 0.0417 |
| 6 | (A, V, P, X, F, D) | 7 | 3 | 0.0139 |
| 7 | (A, P, V, F, X, D) | 7 | 3 | 0.0208 |
| 8 | (X, P, V, F, A, D) | 8 | 4 | 0.0625 |
| 9 | (V, A, P, X, F, D) | 9 | 4 | 0.0104 |
| 10 | (P, V, X, A, F, D) | 10 | 5 | 0.0417 |
| 11 | (V, P, A, X, F, D) | 11 | 5 | 0.0104 |
| 12 | (P, V, A, F, X, D) | 11 | 5 | 0.0208 |
| 13 | (V, X, P, A, F, D) | 11 | 5 | 0.0312 |
| 14 | (A, V, P, F, X, D) | 12 | 4 | 0.0069 |
| 15 | (A, P, V, F, D, X) | 13 | 4 | 0.0208 |
| 16 | (V, A, P, F, X, D) | 14 | 5 | 0.0052 |
| 17 | (X, P, V, F, D, A) | 15 | 5 | 0.0625 |
| 18 | (P, V, X, F, A, D) | 16 | 6 | 0.0208 |
| 19 | (A, V, F, X, P, D) | 17 | 4 | 0.0093 |
| 20 | (V, P, X, A, F, D) | 18 | 6 | 0.0104 |
| 21 | (P, V, F, A, X, D) | 19 | 6 | 0.0139 |
| 22 | (V, X, P, F, A, D) | 19 | 6 | 0.0156 |
| 23 | (V, P, A, F, X, D) | 20 | 6 | 0.0052 |
| 24 | (V, A, F, X, P, D) | 21 | 5 | 0.0069 |
| 25 | (P, V, A, F, D, X) | 22 | 6 | 0.0208 |
| 26 | (A, V, P, F, D, X) | 23 | 5 | 0.0069 |
| 27 | (A, V, F, P, X, D) | 24 | 5 | 0.0046 |
| 28 | (P, V, F, X, A, D) | 25 | 7 | 0.0139 |
| 29 | (V, P, X, F, A, D) | 25 | 7 | 0.0052 |
| 30 | (V, A, P, F, D, X) | 26 | 6 | 0.0052 |
| 31 | (V, A, F, P, X, D) | 27 | 6 | 0.0035 |
| 32 | (P, V, X, F, D, A) | 28 | 7 | 0.0208 |
| 33 | (V, P, F, A, X, D) | 29 | 7 | 0.0035 |
| 34 | (V, F, A, X, P, D) | 30 | 6 | 0.0052 |
| 35 | (V, X, P, F, D, A) | 31 | 7 | 0.0156 |
| 36 | (P, V, F, A, D, X) | 32 | 7 | 0.0139 |
| 37 | (V, P, A, F, D, X) | 33 | 7 | 0.0052 |
| 38 | (V, P, F, X, A, D) | 34 | 8 | 0.0035 |
| 39 | (A, V, F, P, D, X) | 35 | 6 | 0.0046 |
| 40 | (V, F, A, P, X, D) | 36 | 7 | 0.0026 |
| 41 | (V, P, X, F, D, A) | 37 | 8 | 0.0052 |
| 42 | (P, V, F, X, D, A) | 37 | 8 | 0.0139 |
| 43 | (V, A, F, P, D, X) | 38 | 7 | 0.0035 |
| 44 | (V, P, F, A, D, X) | 39 | 8 | 0.0035 |
| 45 | (V, F, P, A, X, D) | 40 | 8 | 0.0026 |
| 46 | (V, F, X, P, A, D) | 41 | 8 | 0.0078 |
| 47 | (A, V, F, D, X, P) | 42 | 6 | 0.0046 |
| 48 | (P, V, F, D, A, X) | 43 | 8 | 0.0139 |
| 49 | (V, A, F, D, X, P) | 44 | 7 | 0.0035 |
| 50 | (V, P, F, X, D, A) | 45 | 9 | 0.0035 |
| 51 | (V, F, A, P, D, X) | 46 | 8 | 0.0026 |
| 52 | (V, F, P, X, A, D) | 46 | 9 | 0.0026 |
| 53 | (A, V, F, D, P, X) | 47 | 7 | 0.0046 |
| 54 | (P, V, F, D, X, A) | 48 | 9 | 0.0139 |
| 55 | (V, P, F, D, A, X) | 49 | 9 | 0.0035 |
| 56 | (V, F, X, P, D, A) | 50 | 9 | 0.0078 |
| 57 | (V, F, P, A, D, X) | 51 | 9 | 0.0026 |
| 58 | (V, A, F, D, P, X) | 52 | 8 | 0.0035 |
| 59 | (V, F, A, D, X, P) | 53 | 8 | 0.0026 |
| 60 | (V, P, F, D, X, A) | 54 | 10 | 0.0035 |
| 61 | (V, F, P, X, D, A) | 55 | 10 | 0.0026 |
| 62 | (V, F, A, D, P, X) | 56 | 9 | 0.0026 |
| 63 | (V, F, P, D, A, X) | 57 | 10 | 0.0026 |
| 64 | (V, F, D, A, X, P) | 58 | 9 | 0.0026 |
| 65 | (V, F, P, D, X, A) | 59 | 11 | 0.0026 |
| 66 | (V, F, D, A, P, X) | 60 | 10 | 0.0026 |
| 67 | (V, F, D, X, P, A) | 61 | 11 | 0.0052 |
| 68 | (V, F, D, P, A, X) | 61 | 11 | 0.0026 |
| 69 | (V, F, D, P, X, A) | 62 | 12 | 0.0026 |
Now that we have defined the set of histories \(\mathcal{H}\), we can summarize the effects of the transition function \(\delta\) developed in CS Transitions as a set of patterns it imposes on all histories \(h \in \mathcal{H}\):
- The causality constraint of the vendor fix path must hold.
- The model makes the simplifying assumption that vendors know at least as much as the public does. In other words, all histories must meet one of two criteria: either Vendor Awareness precedes Public Awareness (P) or Vendor Awareness must immediately follow it.
- The model assumes that the public can be informed about a vulnerability by a public exploit. Therefore, either Public Awareness precedes Exploit Public (X) or must immediately follow it.
Formalizing the Constraints on Histories
Vendor Fix Path causality must hold
Vendor Awareness precedes or is caused by Public Awareness
Public Awareness precedes or is caused by Exploit Public
This model is amenable for analysis of CVD, but we need to add a way to express preferences before it is complete. That is the subject of the Desirable Histories section.
See also
- CS Transitions — the complete transition grammar the derivation above rests on
Annex E — Relationship to ActivityPub [I]
Vultron uses the ActivityStreams 2.0 vocabulary as its wire format. This version does not require full ActivityPub server behavior; the roadmap is at §1.3 Relationship to Existing Standards.
Where Vultron follows ActivityPub conventions:
- Actors are identified by Uniform Resource Identifier (URI) and expose an inbox and an outbox.
- Messages are Activities with
type,actor,objectandid. - Delivery is a Hypertext Transfer Protocol (HTTP) POST to the recipient's inbox.
Where Vultron adds constraints ActivityPub does not impose:
- Case-scoped messages route through the participant holding the Case Manager role rather than directly between participants (§5.4.2 Routing Topology).
- A case ledger entry's payload snapshot MUST be the asserted activity exactly as it was sent, references included (§5.5 Serialization), because the entry hash covers only what the snapshot carries.
- The Vultron vocabulary extends the ActivityStreams vocabulary with the object types of §5.2 Object Types.
See also
- Vultron ActivityStreams (AS) Activity Guides — how to carry out each protocol flow
Annex F — Behavior Trees as an Implementation Pattern [I]
The reference implementation expresses its protocol logic as behavior trees. This is one workable pattern, not a requirement: this specification states what an implementation must do, not how to structure the code that does it. A conformant implementation may use any internal structure.
The pattern is described at Behavior Logic, and the reference implementation's trees are at vultron/core/behaviors/.
Annex G — Capability Shapes [I]
Capability shapes define optional, pluggable capabilities that connect to call-out points in the behavior engine. Capability shapes and capability sets (§12.2 Capability Sets) are independent: an implementation may provide the Case Observer capability set and no capability shapes at all.
A capability shape defines a contract — what the call-out point accepts and what it returns. A concrete implementation that satisfies the contract is a Vultron-compatible capability of that shape.
Informative: why \"shape\"
A shape is a contract, not a component and not an autonomous system. Any mechanism that satisfies the contract fits the shape: a human following a procedure, a script, a model, or a service.
G.1 The Four Capability Shapes
Four shapes cover the ways an external capability can be consulted. They differ in what they are given, what they return, and whether they change anything outside the case. All four are consulted by the protocol, and each MUST answer within the single tick that consults it.
| Shape | Contract: accepts | Contract: returns | Notes |
|---|---|---|---|
| Evaluator | A situation and a set of options | A structured recommendation | Its result gates downstream execution. |
| Retriever | A query | Structured facts from an external source | A boolean is the simplest structured fact; a yes/no external query is a Retriever. |
| Composer | Context | A new content artifact written to the blackboard | The discriminator vs. Actuator: if a content artifact lands on the blackboard, it is a Composer. |
| Actuator | A trigger | SUCCESS when the side effect is confirmed; FAILURE otherwise | Invokes an external system for a side effect. Produces no content artifact. |
Distinguishing Composer from Actuator
Both shapes call external systems. The discriminator is whether a content artifact is written to the blackboard. If the only output is a SUCCESS/FAILURE confirming an external side effect, the shape is Actuator, not Composer.
Informative: the Sentinel pattern is not a capability shape
A Sentinel monitors a condition and acts when it fires. The protocol never consults it, so it answers no call-out point and has no contract of the kind tabled above — no accepted input, no returned value, no blackboard interaction. It is a call-in pattern, the reverse direction of every shape above.
The discriminator is who initiates, not where the information comes from. A capability is consulted and answers within the tick that asks. A Sentinel decides for itself that the moment has come. A Sentinel may well be a case Participant — typically holding the Observer role — in which case it learns case state through ordinary ledger replication and acts by sending ordinary protocol messages, with both visible to every participant. It may equally be operator-side machinery with no case identity, driving one actor through its trigger endpoints and invisible to the case. That choice is a protocol-visibility decision.
Two consequences for an implementer. A capability that answers a question the protocol asks is one of the four shapes, never a Sentinel — including one that answers only yes or no, which is a Retriever. And a Sentinel needs none of the capability-shape machinery: it needs a way to observe, a decision rule, and something to call.
G.2 Relationship to Conformance
Capability shapes are not part of the Case Observer, Case Decision or Case Hosting capability set requirements. An implementation at any capability set level may provide any number of shapes, and a conformance claim does not state which (§12.6 Capability Shapes).
Where a capability shape is implemented, it MUST satisfy the contract defined above. The technology used to fulfill the contract is not specified: a shape may be fulfilled by a human, an automated script, a Large Language Model (LLM), or any other mechanism.
G.3 Relationship to the Reference Implementation
In the Python reference implementation the three taxonomy levels map onto three distinct artifacts:
| Level | Reference-implementation artifact |
|---|---|
| Capability shape | A base class that fixes the lifecycle for that shape |
| Capability | A declaration naming the shape and the typed blackboard ports the capability reads and writes |
| Capability implementation | A factory callable satisfying the backend Protocol, injected through a domain bundle |
The intent, set by ADR-0097, is that the contract a capability declares is machine-readable and belongs to the core layer, so a substituted implementation is checked against it rather than trusted. The shape base classes and the per-capability declarations are being moved into the core layer to make that checking possible; until that work lands they live in the simulation layer and the contract is carried as prose plus a key dictionary.
This mapping is specific to the hexagonal architecture of the reference implementation. Other implementations are not required to use this structure.
See also
- ADR-0024 (original shape taxonomy; the term "call-out point")
- ADR-0025 (call-out point abstraction)
- ADR-0097 (four shapes; core-declared contracts; Sentinel as a call-in pattern)
docs/reference/vultron-taxonomy.md
Open Questions
Each question below is unresolved at the time of issue. Every one also appears inline at the point in the specification where it first matters, so a reader meeting the gap in context sees it there rather than only here.
Open question: CS ordering constraints are unspecified
CS ordering constraints — including the pX→PX invariant (exploit-public
implies public-aware) and the rule that CP must precede ET where public
disclosure triggers embargo teardown — exist in three non-normative places:
a code docstring, a measurement-oriented reference document
(Possible Histories),
and a design note. No spec group covers them normatively.
A conformance claim cannot meaningfully cover CS ordering until this gap is closed. Treat the material cited here as informative until a normative spec is issued.
Open question: who may assert that a vendor became aware
The first VFD transition — v→V, vendor becomes aware — has no
trigger-side specification and no implementation. Design intent is a
third-party assertion ("I notified this Vendor"), not a self-report.
Open questions: who may assert it, whether the subject Vendor may dispute it, and whether transport-level delivery success, a Hypertext Transfer Protocol (HTTP) 200 response, suffices to constitute the assertion.
Do not infer a rule from this document's silence.
Open question: negative-acknowledgment semantics are unspecified
Error message types (RE, EE, CE) are deliberately unmodeled
(ADR-0049). Unprocessable inbound messages are dead-lettered with no sender
notification. The three-way fault partition
(§4.6 Error and Acknowledgment Messages, ADR-0083) provides
structured error signaling where it is implemented, but whether the
protocol needs a normative negative-acknowledgment facet — and if so,
what the expected sender behavior (retry, suppress, escalate) should be —
remains open.
Open question: which HTTP responses a receiving actor returns
This version does not say which HTTP status code a receiving actor returns when it refuses a message. For example, it does not say whether an invalid activity gets 422, an activity addressed only to other actors gets 400, or a message that cannot be decrypted gets 400.
One option is for the protocol to specify these responses, so that a sending actor can rely on them and act on them. The other is to leave them to each server implementation. A Vultron actor built on a stock ActivityPub server returns whatever that server returns, so a protocol rule could conflict with the server it runs on.
Until this is settled, these codes are recorded as the reference implementation's choices, not as protocol requirements. The codes that HTTP itself defines, such as 405 for an unsupported method and 413 for a body that is too large, are not part of this question.
Open question: acquiring a role after joining a case
This specification says how the Case Owner assigns a role and how a role is offered and accepted. It does not say how a participant already in a case asks for an additional one.
A participant admitted as a Vendor that later also needs the Coordinator
role has no protocol path to request it. In practice the request is made out
of band, or as a Add(Note) on the case, and the Case Owner then issues a
role offer. Neither is a specified mechanism.
Open: whether the protocol should define a role-request activity, whether a participant may decline a role it was assigned, and how a role that is removed rather than added is represented in the case history.
Open question: Case Owner discretion once a termination condition is canonical
Terminating a case-level embargo requires Case Owner authorization by default, and the CASE_MANAGER effects the teardown as the single writer of canonical case state. What is settled is who may terminate: any Participant may initiate termination (report a public/exploit/attacks condition, or notify intent to exit) and may terminate its own compliance, but only the Case Owner authorizes ending the embargo for the case.
What is not settled is whether the Case Owner retains any discretion once
a termination condition has been adopted as canonical case state. When public
awareness or exploit publication (\(q^{cs} \in \{\cdots P \cdots, \cdots X
\cdot\}\)) is canonical, VP-14-001/VP-14-002 read as a mandatory consequence —
the embargo no longer protects anything — and the working position of this
specification is that teardown is then mandatory, with the Case Owner's
authority exercised at the point the condition is adopted (guarding against
a false report) rather than as a second, separate decision to keep the
embargo. This matches the automatic-teardown path (a canonical CS.P set by
the Case Owner triggers teardown).
Open: whether the Case Owner may decline to tear down even after the
condition is canonical; whether the "attacks" condition (SHOULD, VP-11-006)
differs from the "public/exploit" condition (SHALL) in this respect; and how
a deliberate decision to keep an otherwise-terminable embargo would be
recorded. This is expected to be a matter of discussion; the resolution MUST
be reflected in this reference and in specs/vultron-protocol-spec.yaml
(VP-11, VP-14) once reached.