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
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 onlyto 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 activitiestarget 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 casecc 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.
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.
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 typeA 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 objectAn 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 typeA 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 fieldA 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 typeA 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 fieldA 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
Response Correlation
| ID | Priority | Requirement | Related |
|---|---|---|---|
RF-08-001 |
MUST | Response activities MUST include inReplyTo fieldA 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.