Skip to content
Reference

Message Processing

This page is normative

This page is considered a core part of the Vultron Protocol. This is a normative section of the documentation.

The whole specification on one page

The specification is also published as a single page that holds every section and annex in order, for printing or for searching the whole text at once.

This page holds section 14.6 of the Vultron Protocol Specification, part of its Requirements Catalog. Each row is one requirement, and the rest of the documentation links a requirement to its row here.

14.6 Message Processing

The requirements on how a receiver processes a message: handler behavior, which sender is entitled to change which status, the format of a response, and the order in which processing steps run.

Handler Protocol

Handler use-case classes process VultronEvent domain objects and implement protocol business logic. All handlers follow a common contract defined by the UseCase Protocol.

Protocol Semantics

ID Priority Requirement Related
HP-00-001 MUST A receiver MUST interpret received activities as assertions about the sender's state, not as commands to perform work

Each actor owns its own state and decisions. If a receiver obeyed a message as a command, any sender could make it act. The receiver would also do the work again on each redelivery. When the receiver reads a message as an assertion, the message only updates what the receiver knows about the sender. The receiver keeps its own decisions.
HP-00-002 MUST A receiver MUST update its local RM/EM/CS state to reflect the state transition asserted by the received activity

A receiver uses its view of the other parties to decide what to do next. For example, it checks whether an embargo is still active or whether a fix exists. The message is the only way that the receiver learns that the state of the sender changed. If the receiver did not record it, the receiver would act on a state that the sender has left.

Handler Signature

ID Priority Requirement Related
HP-01-006 MUST A receiver MUST establish that the sender of a received activity is entitled to make its assertion before any write, outbound activity or ledger commit. Entitlement is the sender's standing, not the receiver's. The governing group's sender clause names it per message (CM-11-017, CM-16-019, CM-21-011, CM-30-001, CM-30-002, CM-33-002, CM-33-004, CM-33-005, CP-06-005, HP-01-010, RMB-08-004, SYNC-03-005). A receiver role check, such as the CASE_MANAGER gate (BT-17-001), never satisfies it.

A received activity is an assertion about the state of the sender (HP-00-001). Thus the first question is whether the sender has the right to assert it. A CASE_MANAGER gate checks only the receiver. If only that gate protected a handler, any actor could claim ownership, admit itself, or get the full ledger (ADR-0115).
Derives from: HP-00-001
HP-01-018 MUST A receiver MUST refuse a received activity whose sender it cannot establish as entitled (HP-01-006), with a reason that names the missing entitlement.

A refusal with a reason tells the sender what standing it lacks. Without the refusal, the activity could still change the case. Without the reason, the sender and an operator cannot tell a missing entitlement from any other fault.
Extends: HP-01-006
HP-01-010 MUST A receiver MUST accept a new report from any actor, whether or not that actor takes part in any case. A new report is sent as Offer(VulnerabilityReport) (RS) or Create(VulnerabilityReport). Only the addressing rules apply to it (HP-01-005, HP-09-001).

A report comes before any case. Thus the sender has no participant record or role. Any person who finds a vulnerability must be able to report it.
Refines: HP-01-006
HP-01-030 MUST NOT A receiver MUST NOT refuse a new report (HP-01-010) on the sender's standing.

A report comes before any case, so its sender has no participant record or role to check. A refusal on standing would stop a finder outside every case from reporting a vulnerability.
Extends: HP-01-010

Offer Recipient Addressing

ID Priority Requirement Related
HP-09-001 MUST A receiver processing an inbound Offer activity MUST determine case-creation eligibility from the to field only

to names the actors that a message asks to act. Only an addressee can make the decision to create a case. If eligibility came from a different field, a courtesy copy could create a case at an actor that the message did not ask. That duplicates cases.
Constrains: HP-01-010
HP-09-002 MUST NOT The target field MUST NOT be used to determine case-creation eligibility for Offer activities

target names where an object must go, not who must act, and a sender can set it to any actor. If target decided case creation, a sender could create a case at an actor that the message does not address.

Unsupported Addressing Fields

ID Priority Requirement Related
HP-10-001 MUST When a receiving actor finds itself only in the cc field of an inbound Offer(VulnerabilityReport) activity, it MUST discard the activity without creating a case

cc addressing has no defined handler semantics in the current protocol version. Implementing an "informational receive" use case for cc recipients would require defining what "informational only" means, which is deferred. Deliberate simplification.
Refines: HP-09-001

Received-Side Status Authorization

Requirements for the authorization model governing how a CASE_MANAGER adopts an inbound participant's reported CaseStatus as canonical (StatusAdoptionGate) and runs the embargo teardown an adopted value calls for, which the adoption also authorizes (ADR-0166). Derived from ADR-0046 and IDEA-1836 planning session.

StatusAdoptionGate

Controls whether a participant's reported CaseStatus is adopted as canonical by the CASE_MANAGER.

ID Priority Requirement Related
RSH-01-003 MUST When the CASE_MANAGER adopts the CaseStatus that a sender's ParticipantStatus embeds, it MUST record the post-adoption CaseStatus in the canonical case ledger, unless it changes neither EM nor PXA (RSH-04-004). The post-adoption CaseStatus MUST set each of P, X and A that either the case's current PXA state or the CaseStatus the sender's ParticipantStatus embeds sets, and MUST NOT clear one the case already sets.


Adoption is the only path that moves the P, X or A state of a participant into the case status (RSH-04-005). Thus a record that copies the PXA of the case again adopts nothing. A record that clears a latch that the case set already undoes the report of another participant. P, X and A latch on only, so the union of the two is the only result that loses no report.

ThreatTerminationBranchNode

Node that triggers embargo teardown on any of the three threat-signal CaseStatus flags.

ID Priority Requirement Related
RSH-03-001 MUST The CASE_MANAGER MUST trigger embargo teardown when the canonical CaseStatus carries CS.P (public awareness), CS.X (exploit public), or CS.A (attacks observed).


*All three flags signal a breach of the conditions that justify an active embargo. Covering only CS.P (as the prior PublicDisclosureBranchNode did) leaves exploit and attack signals without a teardown trigger.
*
RSH-03-004 MUST NOT An actor that does not hold the CASE_MANAGER role MUST NOT ask the CASE_MANAGER for an embargo teardown when the P/X/A status that calls for the teardown (RSH-03-001) was declared by the CASE_MANAGER; it waits for the CASE_MANAGER's committed teardown entry (EP-09-007).


The CASE_MANAGER does the teardown when it detects P, X or A itself. It commits the teardown and records the new status, each as a ledger entry that it announces to every participant. If a replica asks it to terminate an embargo that it terminated already, the only result is a refusal and more messages. An open proposal raises no ask in either case (EMB-16-002). The CSB-18 diagnostic note raises no such false alarm, because PxaEmInvariantDiagnosticNode runs only at the CASE_MANAGER.
Refines: RSH-03-001
Depends on: EP-09-008

CaseStatus Emission Authority and Outbound Emit Invariant

Specifies which actors may emit Add(CaseStatus) directly and mandates that every CASE_MANAGER-side EM or PXA state mutation is followed by a canonical CaseStatus ledger write. CaseStatus is the only protocol channel for communicating EM and PXA state changes across actors; omitting the emit leaves all participant replicas stale until the next round-trip.

ID Priority Requirement Related
RSH-04-001 MUST Only the CASE_MANAGER MUST emit Add(CaseStatus) directly. All other participants MUST embed a suggested CaseStatus inside Add(ParticipantStatus) rather than sending Add(CaseStatus) independently.


*CaseStatus encodes shared EM and PXA state for the entire case. The CASE_MANAGER is the authoritative writer of canonical case state (ADR-0046, CLP-09). Allowing participants to emit Add(CaseStatus) directly would bypass the adoption authorization model.
*
RSH-04-002 MUST Every change the CASE_MANAGER makes to EM state (Propose, Accept, Reject, Terminate, and all cascade variants) MUST be followed immediately by a record of the updated CaseStatus in the canonical case ledger. A received activity whose committed entry records the very activity that caused the transition, and whose entry a replica applies by replaying the same transition (RSH-08-004, ADR-0113), has made that record: the committed entry carries the change to every replica, and a second CaseStatus entry for it would be a duplicate.


Remote actors see EM state changes only through CaseStatus entries in Announce(CaseLedgerEntry) messages. If a change has no ledger write after it, the replicas of the participants keep a stale EM state. If the committed entry records the activity that caused the change, and replicas replay the same transition, that entry carries the change. A second CaseStatus entry then only duplicates it.
RSH-04-003 MUST Every change the CASE_MANAGER makes to PXA state (public awareness, exploit public, attacks observed) MUST be followed immediately by a record of the updated CaseStatus in the canonical case ledger.


*PXA state is participant-agnostic and shared across the case. The rationale mirrors RSH-04-002: omitting the ledger write leaves replicas stale and breaks the Avoid Surprise principle.
*
RSH-04-004 MUST The CASE_MANAGER MUST make the CaseStatus record of RSH-04-002 and RSH-04-003 by committing a canonical CaseLedgerEntry that carries the new CaseStatus, then announcing it with Announce(CaseLedgerEntry) for participant replica sync. It is the authority for its own outbound state changes, so it MUST NOT adjudicate its own CaseStatus as a received message. A snapshot whose EM and PXA equal those of the case's last committed CaseStatus changes nothing, so the CASE_MANAGER MUST NOT commit or record it (CLP-07-013, ADR-0152). The change is measured against the ledger, not the case's current status, which already carries the register's EM (ADR-0122).


*The CASE_MANAGER's own EM or PXA transition is an authoritative state change, not a received message: a received Add(CaseStatus) is admitted only from the CASE_MANAGER at a participant replica (RSH-04-005, RSH-08-003), so judging its own change as one would apply a rule meant for others. Replicas learn of the change only from the announced entry, and an entry that repeats the last committed CaseStatus would hand every replica a duplicate that records no change.
*
RSH-04-005 MUST A received Add(CaseStatus) MUST be admitted only from the case's CASE_MANAGER, sent to a participant. A receiver MUST refuse an Add(CaseStatus) from any other sender, and an Add(CaseStatus) sent to the CASE_MANAGER, with nothing written past intake and nothing sent.


*RSH-04-001 reserves Add(CaseStatus) for the CASE_MANAGER, which sends it to participants inside a case ledger entry. add_case_status_tree has no StatusAdoptionGate, so admitting it from another sender would adopt that participant's P, X or A report without the Case Owner approval RSH-07-001 requires. A participant embeds the P, X or A it reports in its own Add(ParticipantStatus) instead (RSH-04-001), which the CASE_MANAGER adopts through the gate (RSH-01-003).
* Refines: RSH-04-001
Refines: HP-01-006

RM Anomaly Notification Policy (non-adjacent jumps and backward regressions)

When the receiver detects an RM transition anomaly — either a non-adjacent forward jump (sender skipped intermediate states) or a backward regression (sender reports a state earlier on the RM progress scale than the receiver has already observed) — it must act correctly and non-silently. Because CVD is inherently asynchronous, anomalies are detected at receipt time (t₁ > t₀ when the event occurred), and silence at that point breaks observability for both parties. Derived from ISSUE-2258 and ADR-0067.

ID Priority Requirement Related
RSH-06-001 MUST A CASE_MANAGER receiving an Add(ParticipantStatus)[target=CaseParticipant] that carries a non-adjacent forward RM jump (a move forward on the RM progress scale to a state that is not adjacent to the recorded one) MUST accept the reported rm state.


The sender is the authority for its own RM progress. If the receiver refuses a forward jump that is not adjacent, its replica stays incorrect, because the sender does not send again the intermediate transitions that it did not report. The receiver accepts every move that a forward path explains. This is the liberal-accept rule (ADR-0067).
RSH-06-002 MUST A CASE_MANAGER receiving an Add(ParticipantStatus)[target=CaseParticipant] that carries a backward RM regression (i.e. the reported rm state is earlier on the RM progress scale than the receiver's current value) MUST refuse the whole update (RSH-09-001, RSH-09-003).


*RM is monotonic: a sender cannot un-validate, un-accept, or un-close a report. A backward assertion indicates either a protocol error or a divergent replica state; neither should update the receiver's record.
*
RSH-06-004 SHOULD Where a case context is available, the CASE_MANAGER SHOULD record a note on the case when an RM anomaly is detected, requesting the sender to clarify the path it took between the two observed states. This holds whether the declaration that carries the anomaly is accepted or refused. The note is the CASE_MANAGER's own act (CLP-10-001): it commits the Add(Note)[target=VulnerabilityCase] carrying the note inline as an add_note_to_case entry and attaches the note to the case, and the sender learns of it from the entry's Announce(CaseLedgerEntry) fan-out (SYNC-02-002). It MUST NOT send the note to the sender as a direct Add(Note), which a participant refuses (HP-01-005).


An RM anomaly needs an answer in the protocol, not only in local logs, because only the sender knows the path that it took between the two states seen. Add(Note)[target=VulnerabilityCase] is the message for a condition that needs a written explanation (MSM-05-001). It replaces the report-management error message that MessageSemantics does not define (ADR-0049). The note is a case act of the CASE_MANAGER, so it goes to the sender through the ledger. The participant refuses a direct Add(Note) (HP-01-005).
RSH-06-005 MUST The note recorded under RSH-06-004 MUST include: the sending actor's identifier, the rm state before the anomaly, the rm state after the anomaly, and a human-readable description identifying whether the anomaly was a non-adjacent forward jump or a backward regression.


*Without these fields the note cannot be acted on by a human or an automated remediation process. The request for clarification is only meaningful if the recipient can identify what they are being asked to clarify.
*
RSH-06-006 MUST The RM acceptance rule of RSH-06-001, RSH-06-002, RSH-06-004 and RSH-06-005 (accept a forward move including a non-adjacent one, refuse a backward move and keep the recorded state, and post the clarification note where a case context exists) MUST be applied by the CASE_MANAGER to every received write of a participant's RM state, whichever wire activity carried the declaration. This includes the report activities Accept(Offer(VulnerabilityReport)) and TentativeReject(Offer(VulnerabilityReport)), the full-case Invite replies Accept, TentativeReject and Reject(Invite(Actor)[target=VulnerabilityCase]) (CM-11-011), and the case activities Join(VulnerabilityCase) and Ignore(VulnerabilityCase), not only Add(ParticipantStatus)[target=CaseParticipant]. Adjacency-only validation, which accepts only a move to an adjacent RM state, MUST NOT be the receive-side acceptance rule for any of these. A declaration from a sender whose closure the case's ledger records is not adjudicated: it is refused before the rule applies, with no note (CM-23-018). A Reject(Offer(VulnerabilityReport)) for a report that belongs to the case is not adjudicated either: it is refused whatever it declares (CM-23-019).

*The acceptance rule belongs to the state machine, not to the message. A transition accepted as a status snapshot but refused as the activity that caused it lets the sender pick its adjudication by picking its message shape, and lets the receiver second-guess a legal self-declaration because it never saw the intermediate step (ADR-0108). Adjacency remains the emit-side rule (ADR-0086): conservative in what you send, liberal in what you accept.
*

Conservative Default Authorization Policy for Status Gates

Adopting a non-owner's reported P, X or A value waits by default for the Case Owner's authorization, which also authorizes the embargo teardown the adopted value calls for (ADR-0166). A permissive policy is a deployment's explicit choice. Derived from CONCERN-2092 and ADR-0076; the mechanism is amended by ADR-0080 (CONCERN-2812, CONCERN-2809) — see RSH-07-004.

ID Priority Requirement Related
RSH-07-001 MUST Before it adopts a P, X or A value that a non-owner participant reports, the CASE_MANAGER MUST by default obtain the Case Owner's authorization. Adoption makes the value canonical case state.


A permissive default lets any admitted participant, a hostile or defective sentinel included, force PXA adoption and start the embargo teardown with no Case Owner confirmation. So the default is conservative, and permissive behavior needs an explicit opt-in (ADR-0076). The authorization also covers the teardown the adopted value calls for (ADR-0166). The rule says what a receiver does with a reported status, so every CASE_MANAGER obeys it. The Case Owner's own report is RSH-07-006.
RSH-07-006 MUST NOT Before it adopts a P, X or A value that the Case Owner reports, the CASE_MANAGER MUST NOT ask the Case Owner to approve it.


The Case Owner is the authority that RSH-07-001 asks. Asking it to approve its own report would be circular and would hold the case's canonical state on a question with only one possible answer. The report is still judged by the acceptance rules of RSH-09.
Refines: RSH-07-001

One Move, One Mover — Received-Side Write Subject and Pipeline

A protocol state transition may reach a receiver in more than one wire shape: the activity that performed the move (TentativeReject(Offer(VulnerabilityReport))) and a declaration of the resulting state (Add(ParticipantStatus) carrying rmState). These are two layers of one move, not competing encodings. Authority is a property of the pipeline, not of the message: the mover announces, the CASE_MANAGER adjudicates and commits, replicas apply the ledger. Derived from CONCERN-3473 and ADR-0108.

ID Priority Requirement Related
RSH-08-001 MUST A received RM status declaration reports the sending actor's own RM state: its subject MUST be the sender, never the receiver. A receiver's own RM state MUST move on receipt only when it is itself the mover: when sender and receiver are the same actor, or when CM-23-002 directs the CASE_MANAGER to advance its own participant on Case Owner departure. The one write whose subject is neither is the CASE_MANAGER's creation of an invitee's participant record when, in response to a received activity, it emits the stub Invite (CM-11-006, CM-17-007, CM-16-006): its subject is the invitee.

A received activity is a statement about the state of the sender (HP-00-001), and each participant declares its own status (ADR-0121). BT-17-006 selects the store in which a received-side tree runs. It does not say whose state the tree writes, and its sender-ID clause assumes that the two are different. If the tree uses the store as the subject, it moves the incorrect participant: the RM of the receiver changes on a declaration about the sender (ADR-0089).
RSH-08-002 MUST When an act and a status declaration for the same move both arrive (for example TentativeReject(Offer(VulnerabilityReport)) followed by Add(ParticipantStatus) with rmState: INVALID), the receiver MUST treat the second as a same-state confirmation and MUST NOT refuse it, fault it, or record it as an anomaly. A declaration whose RM value disagrees with the act the receiver saw MUST be adjudicated under the RM acceptance rule (RSH-06) like any other declaration; the disagreement is not itself grounds for refusal.


*One move producing both messages is expected (ADR-0108). The declaration is the participant's own statement about itself and the participant is the authority on that; a mismatch with the act is a gap to flag under RSH-06-003, not a reason to prefer one message over the other.
*
RSH-08-006 MUST NOT A participant that declares its own RM, VF or D state, or a Case Owner that asserts one on another participant's behalf, MUST NOT append the status to a participant record in its own case replica. It sends the declaration to the CASE_MANAGER, and its replica copies the status the CASE_MANAGER commits for it, as every other replica does. It MAY check the move before sending, and MAY store the status object the activity carries.


A status that the sender writes for itself has its own id and times, or a value that the CASE_MANAGER then filters. The replica of the sender then holds a status that no entry records, and it is different from every replica at the same ledger position. PCR-03-002 and PCR-03-005 stop the Case Owner from writing its own copy. This rule applies the same limit to every sender of a status declaration (CLP-07-013, CM-23-016).
Extends: PCR-03-002
Refines: RSH-08-003
RSH-08-007 MUST Participant status is self-declared. The CASE_MANAGER MUST refuse, before the commit, a received Add(ParticipantStatus) whose target participant record is not the sender's own (the record's attributedTo is not the sender), whichever dimensions it carries: RM, VF, D and the P, X and A of its case status. The only exceptions are the on-behalf assertions of PRM-06-003 and PRM-06-004: a sender holding CASE_MANAGER or CASE_OWNER MAY assert v→V for a Vendor-role participant, d→D for a Deployer-role participant, or both, when the status changes nothing else (RM as the target holds it, and no case status).


A received activity is a statement about the state of the sender (HP-00-001), and RSH-08-001 makes the sender the subject of an RM declaration. With no subject check on Add(ParticipantStatus), a participant can write any status on the record of a different participant, and the CASE_MANAGER commits it. The on-behalf trigger sends only the PRM-06 moves, so the receive side admits those moves and no others (ADR-0121).
Extends: RSH-08-001
Depends on: HP-01-008
Depends on: PRM-06-003
Depends on: PRM-06-004

Whole Accept or Reject of a Received Status

A received Add(ParticipantStatus)[target=CaseParticipant] or Add(CaseStatus)[target=VulnerabilityCase] states a whole status, so the CASE_MANAGER accepts it as sent or refuses all of it. It never records a status the sender did not send. Derived from ADR-0107 and ISSUE-3741.

ID Priority Requirement Related
RSH-09-001 MUST A CASE_MANAGER receiving an Add(ParticipantStatus)[target=CaseParticipant] MUST either accept the status whole, recording exactly the status it received, or refuse the whole update. It MUST NOT record, commit or replicate a status that differs from the one received, and MUST NOT fill a dimension the sender omitted with a value of its own.


*The case ledger records what each participant asserted, and every replica copies what it records (CLP-07-013, CM-23-016). A status rewritten before it is recorded is one nobody sent: the ledger could then neither show what the sender claimed nor let a replica verify it (ADR-0107). An omitted dimension is absent from the status the sender sent (ADR-0075), so recording it as absent is recording the status as sent.
*
RSH-09-002 MUST When any acceptance rule refuses a received status, the CASE_MANAGER MUST refuse the whole update: it appends nothing, commits no ledger entry for it, adopts nothing from it, and its refusal notice to the sender MUST name every refused dimension and the reason for each.


*A sender can correct an update only if it learns everything that is wrong with it in one answer (EH-07-001). Naming one reason at a time would cost a round trip per mistake.
* Refines: RSH-09-001
RSH-09-003 MUST A received ParticipantStatus MUST be refused when any of these holds: the participant's recorded RM state is CLOSED, which is terminal; its RM state moves backwards (RSH-06-002); it states a fix-readiness value but the sender does not hold the VENDOR role, or a deployment value but the sender does not hold the DEPLOYER role; a stated fix-readiness, deployment or public-state (P/X/A) value is neither the participant's most recent recorded value nor a forward move from it. A dimension the sender omits is not refused, and does not reset the recorded value a later status is judged against. Every status states an RM state: one that leaves rmState out states the initial START. The embargo state the status embeds is not judged against the case: the case derives EM from its embargo register.


*Each rule refuses a claim no sequence of events could produce from the participant's recorded state. A non-adjacent forward move is accepted (RSH-06-001, CSB-16-001): a peer may have advanced several steps between messages. EM belongs to the embargo register (ADR-0122), and the report's P/X/A reaches the case only through the adoption gate (RSH-01-003).
* Refines: RSH-09-001
RSH-09-004 MUST A CASE_MANAGER MUST evaluate the cross-machine entailments (RM↔VF and RM↔D per CSB-18-001, VF↔D per CSB-17-001) against a received ParticipantStatus as received, and MUST refuse the whole update when any is violated. It MUST evaluate the same entailment set the emit path evaluates.


Each dimension can be acceptable alone when the combination is not possible. A fix that is ready or deployed requires a report that the participant accepted, and a deployed fix requires a ready fix. A replica refuses to apply such a status (RSH-09-006). Thus a recorded status of this type commits an entry that no replica can apply. One shared evaluator keeps the emit path and the receive path the same.
Refines: RSH-09-001
RSH-09-005 MUST A participant receiving an Add(CaseStatus)[target=VulnerabilityCase] from the case's CASE_MANAGER MUST accept the status whole only when its EM state is identical to the EM state the case derives from its embargo register and its P/X/A state is the case's or a monotone forward move from it, and MUST otherwise refuse the whole update.


*EM changes only through the embargo messages that cause each register step (ADR-0122), so a status can report EM but never move it. P/X/A bits only advance, and a remote peer may report a combined advance that skips intermediate steps, so the received path uses the monotone check rather than the single-step adjacency that applies to local writes (CSB-16-002). A status partly accepted would record one nobody sent (RSH-09-001).
*
RSH-09-006 MUST When applying an Announce(CaseLedgerEntry) whose entry adds a participant status, a receiver MUST evaluate the composite-state entailments (RM↔VF and RM↔D per CSB-18-001, VF↔D per CSB-17-001) against the participant state the entry carries. If any entailment is violated the receiver MUST NOT apply the status update, and MUST send Create(ProcessingFault) with failure class StatusAssertionRefused/ImpossibleState to the CASE_MANAGER.


*A CASE_MANAGER that is buggy or hostile can commit an entry whose participant state violates the entailments the emit and receive paths enforce (RSH-09-004). Without this check every replica silently adopts an impossible combination it would have refused to emit. A Create(ProcessingFault) rather than a Reject(CaseLedgerEntry) avoids a CASE_MANAGER replay loop: the committed entry cannot be uncommitted, so replaying it would be refused forever (ASK-07-001).
*
RSH-09-009 MUST NOT A received ParticipantStatus MUST NOT be refused for the sender's case roles over the public-state (P/X/A) values that its embedded CaseStatus states. A participant holding any role, OBSERVER alone included, can report that the vulnerability is public, that an exploit is public or that attacks are observed. Adoption decides whether a reported value becomes canonical case state (RSH-01-003), not the acceptance rules.


*P, X and A record the state of the world, not anything a participant did, so no role owns them. A Vendor cannot be the only party able to say that an exploit is public: an Observer or a monitoring service is often the first to see it. The adoption gate, not a role check, protects canonical case state from a wrong report.
* Extends: RSH-09-003

Response Format

The Vultron protocol uses ActivityStreams activities for both requests and responses. When handlers process inbound activities, they generate response activities (Accept, Reject, Update, etc.) sent to the originating actor or other participants.

Accept Response

ID Priority Requirement Related
RF-02-001 MUST Accept responses MUST use Accept activity type

A receiver recognizes an acceptance by the activity type, and Accept is the AS2 term peers match on. Any other type would not be read as an acceptance.
Implements: VP-06-004
RF-02-002 MUST Accept responses MUST include object field referencing accepted activity or object

An Accept with no object does not say what it accepts. An initiator with several requests outstanding could not tell which one changed state.
RF-02-004 MUST When accepting an offered object, the object of the Accept activity MUST be the Offer activity itself, carried inline or by reference, not the underlying object being offered.

An Accept of the offered object is ambiguous when that object has been offered more than once, and receivers match an acceptance as Accept(Offer). Naming the Offer says which proposal was accepted. Inline or by reference leaves the form to the implementation (AKM-05-003).
RF-02-005 MUST When accepting an Invite, the object of the Accept activity MUST be the Invite activity itself, carried inline or by reference, not the case, embargo, or any other domain object.

An Accept of the case or embargo does not say which Invite it answers, and several Invites can be open at once. Receivers match the answer as Accept(Invite), so naming the Invite selects the right use case and closes that Invite.
Refines: RF-02-002

Reject Response

ID Priority Requirement Related
RF-03-001 MUST Reject responses MUST use Reject activity type

A receiver recognizes a refusal by the activity type, and Reject is the AS2 term peers match on. Any other type would not be read as a refusal.
Implements: VP-06-004
RF-03-002 SHOULD Reject responses SHOULD include reason in content field

A bare Reject tells the initiator that its proposal failed but not why, so it cannot correct the proposal or decide whether to escalate. A reason gives the participant something to act on; it is a SHOULD because a refusal can stand without one.
RF-03-003 MUST When rejecting an offered object, the object of the Reject activity MUST be the Offer activity itself, carried inline or by reference, not the underlying object being offered.

The Offer is the proposal the initiator tracks. Referencing it ties the Reject to that one proposal, so the initiator can close it, even when the same object has been offered more than once. Inline or by reference leaves the form to the implementation (AKM-05-003).

TentativeReject Response

ID Priority Requirement Related
RF-04-001 MUST TentativeReject responses MUST use TentativeReject activity type

A TentativeReject is a provisional refusal, which the receiver handles differently from a final Reject. Only a distinct activity type lets the receiver tell the two apart.
Implements: VP-06-004
RF-04-002 SHOULD TentativeReject responses SHOULD include reason in content field

A provisional refusal invites a revised proposal. Without a reason the initiator cannot tell what would make the proposal acceptable; it is a SHOULD because a refusal can stand without one.
RF-04-003 MUST When tentatively rejecting an offered object, the object of the TentativeReject activity MUST be the Offer activity itself, carried inline or by reference, not the underlying object being offered.

The Offer is the proposal the initiator tracks. Referencing it ties the TentativeReject to that one proposal, even when the same object has been offered more than once. Inline or by reference leaves the form to the implementation (AKM-05-003).

Error Response

ID Priority Requirement Related
RF-05-001 SHOULD Error responses SHOULD use ActivityStreams error extensions

The protocol defines error messages whose recipients inquire about the error (VP-03-010, VP-03-011, VP-11-005). Expressed in the AS2 vocabulary, an error travels and is parsed through the same inbox pipeline as every other activity, with no second channel.
Implements: VP-03-010
Implements: VP-03-011
Implements: VP-11-005

Response Delivery

ID Priority Requirement Related
RF-06-002 MUST Response activities MUST be addressed to the INITIATING actor's inbox, not to the inbox that received the original activity

Activities are state-change assertions; the initiator must receive the response to update their model of the case state

Response Correlation

ID Priority Requirement Related
RF-08-001 MUST Response activities MUST include inReplyTo field

A participant can have several requests outstanding. inReplyTo names the activity a response answers, so the initiator matches the response to its request without inferring it from the object.
Implements: VP-03-009
Implements: VP-11-004
Implements: VP-11-005

Idempotent Responses

ID Priority Requirement Related
RF-09-001 MUST Response generation MUST be idempotent

Delivery is at-least-once, so a receiver sees some activities more than once. If each copy produced a new response, the initiator would receive duplicate or conflicting answers and could apply the same state change twice.
Depends on: ID-02-001

Behavior Tree Integration

Requirements for integrating py_trees behavior trees into Vultron handler functions. Covers the BT execution model, state management via the DataLayer, handler integration patterns, actor isolation, concurrency constraints, and failure diagnosis. Complex protocol workflows such as report validation, case creation, and embargo management must be implemented as BT nodes or subtrees.

CASE_MANAGER Role Management

ID Priority Requirement Related
BT-10-002 MUST Case creation MUST establish the case's CASE_MANAGER by recording a participant that holds the CASE_MANAGER role.

The CASE_MANAGER is a role that a participant holds. It is not a separate entity. When case creation records the role on a participant, any actor can take the role, and every participant finds the CASE_MANAGER in the participant records of the case.
Refines: CP-04-002
BT-10-007 MUST NOT Case creation MUST NOT require a per-case Service actor.

A Service record for each case only repeats what the CASE_MANAGER role already shows. A container also cannot prepare such a record before use, because the case does not exist until a report arrives.
Extends: BT-10-002
BT-10-008 MUST NOT Case creation MUST NOT mint a per-case actor identity. The actor that enacts the role keeps its own actor identity, and the activity's context carries the case that a message concerns.

An actor takes part in many cases and keeps one identity in all of them. A container cannot host a per-case actor identity. The sender calculates that identity from the report id or case id, so no container can register it, and every message to it fails. A future process that runs one case gets its own actor identity for the same reason.
Extends: BT-10-002
BT-10-003 MUST The actor holding the CASE_MANAGER role for a case MUST process that case's messages. The authority is that role, not any particular actor that enacts it.

When message processing follows the role and not one specific object, any actor that holds the role can do the work. This includes an actor that the Case Owner delegates the role to, and the Case Owner itself. If an obligation names a separate case actor, implementations create one for each case, which BT-10-002 forbids.
Refines: BT-10-002
BT-10-004 MUST The actor holding the CASE_MANAGER role for a case MUST enforce that case's authorization decisions.

Each authorization decision of a case needs one enforcer that every participant can name. The CASE_MANAGER processes the messages of the case (BT-10-003), so it is the actor that admits or refuses them.
Refines: CLP-09-001
Refines: CLP-09-003
Refines: CLP-09-004
BT-10-009 MUST The CASE_MANAGER MUST hold its authority over a case's authorization decisions through its role, not through a per-case actor identity.

When authority follows the role, it moves with a delegation, and it does not depend on one specific software actor. It also operates when the CASE_MANAGER is a person or an organization and not a service, which is the general case. BT-10-006 tells how the gates in this codebase test for the role.
Extends: BT-10-004
BT-10-005 SHOULD The CASE_MANAGER SHOULD record an invitee's RM state as RM.RECEIVED when it emits the stub Invite (CM-11-006).

When the CASE_MANAGER emits the stub Invite, the invitee holds no case yet and has made no triage decision. RM.RECEIVED records that the report reached the invitee, and nothing more.
Implements: CM-11-006
BT-10-010 SHOULD The CASE_MANAGER SHOULD leave the invitee's RM state at RM.RECEIVED when the invitee accepts the stub Invite (CM-11-001).

When the invitee accepts the stub Invite, it only asks for the full case. It does not say if the report is valid. The invitee's replies to the full-case Invite drive its triage (RM.VALID, RM.INVALID or RM.CLOSED, CM-11-011). Then Join(VulnerabilityCase) or Ignore(VulnerabilityCase) moves it to RM.ACCEPTED or RM.DEFERRED. Thus the record of the CASE_MANAGER does not show a decision that the invitee has not made.
Implements: CM-11-001
Extends: BT-10-005

Event-Driven Control Flow

Defines the event-driven processing model for Vultron actors: how primary events enter the system, how cascading consequences are produced automatically, and what constraints apply to demo scripts and integration tests.

Cascade Implementation

ID Priority Requirement Related
EDF-03-004 MUST The cascading consequence of a primary event MUST be fully determined by the handling actor's processing of that event, with no required intermediate local action, except at explicit external decision points (see EDF-04).

A peer sees only what an actor emits. If an actor needs an extra local step before it completes the consequences of an event, its peers wait for something that the protocol does not show them. When the processing of the event alone determines its consequences, every implementation that receives the same event does the same work. The only exception is a point where the actor must wait for a decision (EDF-04).