Skip to content
Reference

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).

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


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

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:

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_MANAGER role. 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 ParticipantStatus is 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 CaseStatus is 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 with Accept or Reject.
  • Offer, with a CaseParticipant as its object, is a participant recommending an actor. It is answered by the Case Owner, which may then cause an Invite to 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.
  • Announce of a CaseLedgerEntry is 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):

  1. 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_MANAGER role.
  2. 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 Add of a ParticipantStatus. This is a claim.
  • The CASE_MANAGER, having adopted a claim, sends Add of a CaseStatus targeting 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:

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.

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 Reject sent 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.

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) or Reject (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:

  1. At most one entry is ACTIVE and at most one is TERMINATED, and never one of each.
  2. A SUPERSEDED entry exists only alongside exactly one ACTIVE or TERMINATED entry.
  3. No entry is PROPOSED while an entry is TERMINATED.
  4. 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.

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_OVER on 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).

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).

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.

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_OVER to the revision's row of every participant whose row for the replaced embargo is AGREED, a DECLINED row 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 AGREED has 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_OUT or DECLINED row moves to AGREED directly (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:

  1. The participant is admitted to the case: its RM state is at least Received.
  2. 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 not ACTIVE or REVISE).

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):

  1. Commit a ledger entry recording the acceptance, and send that entry to every active participant.
  2. Record the acceptance's effect on the participant: its RM state stays Received, and its consent row for the active embargo, if any, becomes AGREED, unless the participant had declined that embargo.
  3. 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.
  4. 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 a PROPOSED entry for the revision, and every other participant gets an UNINVITED row 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 as AGREED, 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 at Revised while another revision is open (EMB-18-005). The owner's row for the revision becomes AGREED unless it already is. If the revision ends no later than the embargo it replaces, the CASE_MANAGER applies CARRY_OVER to the revision's row of every participant whose row for the replaced embargo is AGREED, a DECLINED row 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 not AGREED has 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 Active once 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 no ACTIVE entry, 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 sends Announce(EmbargoEvent) to every other active participant (EMB-19-001), and a direct Remove(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 vfd update is refused with an rm value 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 an Add(CaseStatus) to itself, and a receiver refuses an Add(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 CaseStatus record 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.


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.

  1. 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).
  2. 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 with Accept or Reject (CM-33-004). The Case Manager records the answer and grants the role on an Accept (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.
  3. The Case Manager records each actor's roles on that actor's CaseParticipant entry 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 /stub appended. A receiver never derives the case from it.
  • type: VulnerabilityCaseStub. A receiver tells a stub from a case by its type alone.
  • caseId: the identifier of the case the stub stands for.
  • activeEmbargo and caseStatus: present whenever an embargo is in force, that is, while the embargo state is ACTIVE or REVISE. 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 answers Accept(Invite) or Reject(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 still INVITED (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 the Invite above. An implementation MUST NOT treat Offer(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.


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:

  1. Track: maintain a local value for the machine, and update it as incoming messages report transitions.
  2. 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:

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) / Accept handshake

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 CaseParticipant record, 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 absent
  • d→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 VFd without 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 report
  • x→X (exploit public): any participant may report
  • a→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

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:

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.

\[s \stackrel{\mathsf{def}}{=}\left( \sigma_1, \sigma_2, \dots \sigma_n \right)\]

Case Histories

CS Model Input Symbols (\(\Sigma^{cs}\)) Defined
\[\Sigma^{cs} = \{\mathbf{V},\mathbf{F},\mathbf{D},\mathbf{P},\mathbf{X},\mathbf{A}\}\]

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.

\[\begin{split} h \stackrel{\mathsf{def}}{=}s : & \forall \sigma_i, \sigma_j \in s \textrm{ it is the case that } \sigma_i \neq \sigma_j \textrm{ and } \\ & \forall \sigma_k \in \Sigma \textrm{ it is the case that } \exists \sigma_i \in s \textrm{ such that } \sigma_k = \sigma_i \end{split} \]

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

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}\):

  1. The causality constraint of the vendor fix path must hold.
  2. 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.
  3. 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

\[\mathbf{V} \prec \mathbf{F} \prec \mathbf{D}\]

Vendor Awareness precedes or is caused by Public Awareness

\[\mathbf{V} \prec \mathbf{P} \textrm{ or } \mathbf{P} \rightarrow \mathbf{V}\]

Public Awareness precedes or is caused by Exploit Public

\[\mathbf{P} \prec \mathbf{X} \textrm{ or } \mathbf{X} \rightarrow \mathbf{P}\]

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, object and id.
  • 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

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.