Skip to content
How-to guide

How to Acknowledge a Report

Use this guide when you have received a report and want to tell the sender it arrived, without yet declaring it valid or invalid. Report Acknowledgment (RK) is implemented in ActivityStreams as Read(Offer(VulnerabilityReport)) — an as:Read whose object is the report's original Offer. You finish with the sender informed and your own Report Management (RM) state unchanged at RECEIVED.


Prerequisites

  • A running Vultron instance you can drive. The demo containers are the quickest route — see Tutorial: Run the Receive-Report Demo for the two steps that get you a container shell.
  • An actor identity on that instance, holding the role each step names.
  • Working knowledge of the ActivityStreams verb, object, and target shape, summarized in Vultron and ActivityPub.
  • Working knowledge of the case model: the objects a case is made of, in The Case Model, and who is allowed to change them, in The CASE_MANAGER and the Case Ledger. Where a guide's messages are case-scoped, they route through the CASE_MANAGER and their results appear on the case ledger.

  • A report you have received, at RM.RECEIVED, whose Offer(VulnerabilityReport) was addressed to you.

  • The sender's actor Uniform Resource Identifier (URI).

Decide whether to acknowledge at all

An acknowledgment carries no verdict, so it is worth sending only while you have no verdict to send.

  • If you are still triaging, send the acknowledgment, Read(Offer(VulnerabilityReport)).
  • If you are ready to accept the report, send Report Valid (RV), Accept(Offer(VulnerabilityReport)), instead. Declaring the report valid implies you read it.
  • If you are ready to reject it, send Report Invalid (RI), TentativeReject(Offer(VulnerabilityReport)), instead. The same implication holds.

Sending Read(Offer(VulnerabilityReport)) immediately before a verdict adds a message without adding information.

You may acknowledge at any time after the report arrives, whether or not a case exists yet; the acknowledgment answers the sender's original Offer and sits outside the case (RMB-08-003, PCR-08-001).


Send the acknowledgment

  1. Send Read(Offer(VulnerabilityReport)) to the sender's inbox, with the original Offer(VulnerabilityReport) activity as its object. The ack-report trigger does this for you: POST /actors/{actor_id}/trigger/ack-report with the Offer's offer_id. It addresses the sender alone.

    Report Acknowledgment (RK)

    Acknowledges receipt of the submitted report. Report submission is not replicated through the case ledger, so the acknowledgment is an explicit message.

    • When… a report receiver has read a submitted report
    • Send: Read(Offer(VulnerabilityReport))
    • Send to: an actor outside any case
    • Sent by: the addressee of the vulnerability report offer (usually: vendor, coordinator)
    • Reference: Report Acknowledgment (RK)
    Example activity
    {
      "@context": "https://www.w3.org/ns/activitystreams",
      "type": "Read",
      "id": "urn:uuid:adbef019-daee-45ea-9c42-f32aa9fd289e",
      "name": "https://vultron.example/organizations/vendorco Read https://vultron.example/users/finndervul Offer FDR-8675309",
      "content": "We've read the report. We'll get back to you soon.",
      "actor": "https://vultron.example/organizations/vendorco",
      "object": {
        "@context": "https://www.w3.org/ns/activitystreams",
        "type": "Offer",
        "id": "urn:uuid:b35c945a-c3ef-4d29-8b63-d670c24ef618",
        "name": "https://vultron.example/users/finndervul Offer FDR-8675309",
        "published": "2026-10-23T20:17:06+00:00",
        "updated": "2026-10-23T20:17:06+00:00",
        "to": [
          "https://vultron.example/organizations/vendorco"
        ],
        "actor": "https://vultron.example/users/finndervul",
        "object": {
          "id": "https://demo.vultron.local/VulnerabilityReport/e28f4965-7838-4644-b4a8-10604c7ee92f",
          "type": "VulnerabilityReport",
          "name": "FDR-8675309",
          "published": "2026-10-23T20:17:06+00:00",
          "updated": "2026-10-23T20:17:06+00:00",
          "content": "I found a vulnerability!",
          "attributedTo": "https://vultron.example/users/finndervul",
          "@context": "https://certcc.github.io/Vultron/ns/context.jsonld"
        }
      }
    }
    
  2. Do not send it to the CASE_MANAGER. The CASE_MANAGER holds no record of who the Offer was addressed to, so nobody sends it an acknowledgment (RMB-08-003).

  3. Leave your RM state at RECEIVED. Acknowledgment is not a transition.

The nested form is the one a receiver recognizes: the acknowledgment answers the Offer, so the Offer is its object (Report Management (RM) Messages, MSM-01-008). A bare Read(VulnerabilityReport) carrying the report itself is not a Vultron activity, and a conformant receiver does not act on it.


Verify

The sender holds a Read(Offer(VulnerabilityReport)) from you, and your RM state is still RECEIVED. The sender accepted it because you are an addressee of its Offer; it refuses an acknowledgment from any other actor (RMB-08-004). No CaseLedgerEntry is written, because report submission is not ledger-replicated.


See it end to end

Try it: vultron-demo acknowledge

vultron-demo acknowledge

Or with Docker Compose:

DEMO=acknowledge docker compose -f docker/docker-compose.yml run --rm demo

The scenario runs all three paths: acknowledge only, acknowledge then validate, and acknowledge then invalidate.


Further reading