Tutorial: Run the Receive-Report Demo
In this tutorial, we will run the Vultron receive-report demo end-to-end
using Docker Compose. By the end of this tutorial, we will have:
- started a local Vultron API server,
- run three vulnerability-report workflows, and
- observed how a vendor accepts, invalidates, and closes reports in the Vultron protocol.
What we will learn
This tutorial focuses on the receive-report workflow: a reporter submits a vulnerability report to a vendor, and the vendor decides what to do with it. We will run all three outcomes — accept (validate), hold (invalidate), and reject-and-close — and read the structured log output to understand what happened at each step. The vendor's decision is a move in the Report Management (RM) state machine, and each move has a formal message name and an ActivityStreams wire form. The demo log prints the wire form; this page names both.
Prerequisites
We need the following tools installed before we begin:
- Docker (version 20.10 or later)
- Docker Compose (version 2.x; included with Docker Desktop)
- Git (to clone the repository)
We do not need Python installed locally; the containers include everything required.
Step 1 — Clone the repository
First, let's get a local copy of the Vultron project:
git clone https://github.com/CERTCC/Vultron.git
cd Vultron
Tip
If you already have a local clone, cd into the repository root and run
git pull to make sure you are up to date.
Step 2 — Start the demo container
The demo container depends on the api-dev container. Docker Compose starts
both automatically and waits until the API server is healthy before launching
the demo shell.
docker compose -f docker/docker-compose.yml run --rm demo
After the images are built (this takes a few minutes the first time), we should see a shell prompt inside the container:
root@<container-id>:/app#
Notice that both the api-dev and demo containers are now running.
The api-dev container exposes the Vultron API at
http://localhost:7999/api/v2.
Step 3 — Run the receive-report demo
Inside the container shell, run:
vultron-demo receive-report
The demo runs three separate workflows in sequence.
Each step begins with a 🚥 marker and ends with either 🟢 (success) or 🔴 (failure).
Verification checks are marked with 📋 (start) and ✅ (pass) or ❌ (fail).
Each workflow ends with its own completion line, for example:
✅ DEMO 1 COMPLETE: Report validated, case created, and introduced to the reporter.
If any step or check failed, the demo prints a failure summary after the last workflow.
Step 4 — Read the output
Let's look at what happened in each workflow.
Demo 1: Validate Report
DEMO 1: Validate Report and Create Case
-
Step 1 — The reporter actor submits a
VulnerabilityReportthrough its ownsubmit-reporttrigger, which sends a Report Submission (RS),Offer(VulnerabilityReport), to the vendor. The trigger also records which actor the reporter sent the report to, so the reporter can later check who introduces the case. Notice the log lines showing that the offer and the report are both stored in the vendor's DataLayer. When the vendor receives the report, it asks its CaseActor to open a case for it. -
Step 2 — The vendor posts a Report Valid (RV),
Accept(Offer(VulnerabilityReport)), to its own inbox, which runs the validation behavior tree and moves the report toRM.VALID. Notice the log line confirming the activity was stored. -
Step 3 — The vendor finds the case that was opened for the report, and introduces it to the reporter with its own Create Case,
Create(VulnerabilityCase). The reporter accepts the introduction because it comes from the actor the reporter sent the report to. Notice the final ✅ confirming that the reporter's record of the report now names the vendor and points at the case.
---
title: Demo 1 — validate the report
---
sequenceDiagram
participant R as Reporter
participant V as Vendor
R->>V: Report Submission (RS)<br/>Offer(VulnerabilityReport)
Note over V: Stores offer and report<br/>case opened
V->>R: Create Case<br/>Create(VulnerabilityCase)
V->>V: Report Valid (RV)<br/>Accept(Offer(VulnerabilityReport))
Note over V: Validation BT runs<br/>RM.VALID
Demo 2: Invalidate Report
DEMO 2: Invalidate Report (Hold for Reconsideration)
The reporter submits a second, separate report.
The vendor responds with a Report Invalid (RI), TentativeReject(Offer(VulnerabilityReport)), meaning the report is held open for further investigation rather than closed outright.
The report sits at RM.INVALID, and the vendor sends the same activity to the reporter so the reporter learns the decision.
The vendor still opened a case when the report arrived, so the demo ends by checking that the reporter accepted the vendor's introduction of it.
---
title: Demo 2 — hold the report as invalid
---
sequenceDiagram
participant R as Reporter
participant V as Vendor
R->>V: Report Submission (RS)<br/>Offer(VulnerabilityReport)
V->>V: Report Invalid (RI)<br/>TentativeReject(Offer(VulnerabilityReport))
Note over V: Report held at RM.INVALID<br/>case opened on receipt
V->>R: Report Invalid (RI)<br/>TentativeReject(Offer(VulnerabilityReport))
Demo 3: Invalidate and Close Report
A third report is submitted.
The vendor first invalidates it with a Report Invalid (RI) and then closes the case it opened for the report with Leave(VulnerabilityCase).
Once a case holds a report, its Case Owner closes the case this way, never with a Report Closed (RC), Reject(Offer(VulnerabilityReport)), which the case manager refuses from every sender.
Notice that the invalidation notice appears in the reporter's inbox at the end, and that the reporter again accepted the vendor's introduction of the case.
---
title: Demo 3 — invalidate the report and close its case
---
sequenceDiagram
participant R as Reporter
participant V as Vendor
R->>V: Report Submission (RS)<br/>Offer(VulnerabilityReport)
V->>V: Report Invalid (RI)<br/>TentativeReject(Offer(VulnerabilityReport))
V->>V: Close Case<br/>Leave(VulnerabilityCase)
V->>R: Report Invalid (RI)<br/>TentativeReject(Offer(VulnerabilityReport))
Note over R: Invalidation notice in reporter's inbox
Step 5 — Exit the container
When we are finished, type exit to leave the container shell:
exit
The --rm flag we passed in Step 2 ensures the container is removed
automatically on exit. The api-dev container will continue running;
stop it with:
docker compose -f docker/docker-compose.yml down
What we accomplished
We have:
- started the Vultron API server and demo container with a single
docker compose runcommand, - run three vulnerability-report workflows covering the three main outcomes defined in the Report Management (RM) state machine, and
- read structured log output showing each step and verification check.
Next steps
- Run more demos — see Tutorial: Running the Other Demos to explore case initialization, embargo management, actor invitation, and more.
- Understand the protocol — read How to Report a Vulnerability for a detailed walkthrough of the activities we observed.
- Explore the demo scripts — the source for this demo is in
vultron/demo/exchange/receive_report_demo.py; the shared utilities are invultron/demo/utils.py. - See the formal names — Report Management (RM) Messages maps each RM message to its ActivityStreams wire form.
- Run all demos at once — inside the demo container, run
vultron-demo allto execute every demo in sequence.
Reading path: You handle vulnerability reports and coordinate cases with other organizations
This page is part of Watch a case run.
Before this page: