Skip to content
How-to guide

How to Establish an Embargo

Use this guide to put a case under embargo. An embargo starts as a proposal, and the Case Owner activates it, either after collecting the other participants' answers or at once when the terms are already settled. You finish with the case at Embargo Management (EM) state EM.ACTIVE and the terms announced to every participant.


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.

  • An existing case with no active embargo. A case carries at most one.

  • The Case Owner role, for activating an embargo. Any participant can propose one.
  • The embargo terms you intend to propose — at minimum, an end date and time.

Embargo Management is a model of its own

The Vultron protocol carries considerable detail about Embargo Management and how participants are expected to interact with embargoes. Read that section before designing embargo behavior.


The exchange

The flowchart below shows the route to EM.ACTIVE. Each participant's answer to the Invite records its own consent; the Case Owner's decision is a separate activity on the embargo itself.

---
title: Establishing an Embargo
---
flowchart TB
    subgraph as:Invite
        EmProposeEmbargo["Embargo Proposal (EP) / Embargo Revision Proposal (EV)<br/>Invite(Event)"]
    end
    subgraph participant answers
        EmAcceptEmbargo["Embargo Proposal Acceptance (EA) / Embargo Revision Acceptance (EC)<br/>Accept(Invite(Event))"]
        EmRejectEmbargo["Embargo Proposal Rejection (ER) / Embargo Revision Rejection (EJ)<br/>Reject(Invite(Event))"]
    end
    subgraph Case Owner decides
        ActivateEmbargo["Activate the embargo<br/>Accept(Event), target: VulnerabilityCase"]
        RejectEmbargoProposal["Reject the proposal<br/>Reject(Event), target: VulnerabilityCase"]
    end
    subgraph as:Announce
        AnnounceEmbargo["Announce the embargo terms<br/>Announce(Event)"]
    end
    start([Start]) --> EmProposeEmbargo
    EmProposeEmbargo --> EmAcceptEmbargo
    EmProposeEmbargo --> EmRejectEmbargo
    EmAcceptEmbargo --> d{Owner activates?}
    EmRejectEmbargo --> d
    d -->|y| ActivateEmbargo
    d -->|n| RejectEmbargoProposal
    ActivateEmbargo --> AnnounceEmbargo

Negotiate the terms

Embargo Proposal (EP) is implemented in ActivityStreams as Invite(Event). The same activity carries a revision, Embargo Revision Proposal (EV); a receiver tells the two apart from the case's EM state rather than from the activity (MSM-02).

  1. Send Invite(Event) with the proposed EmbargoEvent inline as its object and the case as its context. The case moves to EM.PROPOSED. The protocol names Invite(Event) as the one form; a received Create(Event) is not a protocol message (VAM-01-011), and this implementation's inbox does not process it (VAM-01-010). A Create of an EmbargoEvent is the Case Manager's own ledger record of a creation-time embargo, so the inbox reports it as "no use case found"; a Create of any other Event is refused as an unrecognized activity.

    Embargo Proposal (EP)

    Proposes embargo terms, such as an end date and time.

    • When… a Participant proposes embargo terms for a case with no active embargo
    • Send: Invite(Event)[context=VulnerabilityCase]
    • Send to: the case manager
    • Sent by: the case manager or an active participant
    • Distinguished by: EM state NONE or PROPOSED
    • Reference: Embargo Proposal (EP)
    Example activity
    {
      "@context": "https://www.w3.org/ns/activitystreams",
      "type": "Invite",
      "id": "urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0/embargo_proposals/1",
      "name": "https://vultron.example/organizations/vendorco Invite Embargo for VendorCo Case #70930046",
      "context": "urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0",
      "summary": "We propose to embargo case 1 for 90 days.",
      "actor": "https://vultron.example/organizations/vendorco",
      "object": {
        "id": "urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0/embargo_events/2026-08-30T00:00:00+00:00",
        "type": "EmbargoEvent",
        "name": "Embargo for VendorCo Case #70930046",
        "context": "urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0",
        "startTime": "2026-06-01T19:12:00+00:00",
        "endTime": "2026-08-30T00:00:00+00:00",
        "published": "2026-06-01T19:12:00+00:00",
        "updated": "2026-06-01T19:12:00+00:00",
        "content": "We propose to embargo VendorCo Case #70930046 for 90 days.",
        "@context": "https://certcc.github.io/Vultron/ns/context.jsonld"
      }
    }
    
  2. The CASE_MANAGER asks each other participant about the terms with an Invite(Event) of its own, addressed to that participant and naming the proposer in attributedTo. If you are the CASE_MANAGER, this is the Invite you send; if you are not, it is the Invite you answer.

    Ask a participant about proposed embargo terms

    Asks one participant to accept or reject proposed embargo terms, or a proposed revision. The Case Manager sends one to each participant other than the proposer, naming the proposer in attributedTo, and the participant's answer names this Invite.

    • When… the Case Manager asks a Participant to answer embargo terms another Participant proposed
    • Send: Invite(Event)[context=VulnerabilityCase]
    • Send to: a participant
    • Sent by: the case manager
    • Reference: Ask a participant about proposed embargo terms
    Example activity
    {
      "@context": "https://www.w3.org/ns/activitystreams",
      "type": "Invite",
      "id": "urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0/embargo_proposals/1/invites/coordinator",
      "name": "https://vultron.example/case-actors/vendorco-case-actor Invite Embargo for VendorCo Case #70930046",
      "context": "urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0",
      "endTime": "2026-06-08T19:12:00+00:00",
      "summary": "The vendor proposes to embargo case 1 for 90 days.",
      "to": [
        "https://vultron.example/organizations/coordinator"
      ],
      "attributedTo": "https://vultron.example/organizations/vendorco",
      "actor": "https://vultron.example/case-actors/vendorco-case-actor",
      "object": {
        "id": "urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0/embargo_events/2026-08-30T00:00:00+00:00",
        "type": "EmbargoEvent",
        "name": "Embargo for VendorCo Case #70930046",
        "context": "urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0",
        "startTime": "2026-06-01T19:12:00+00:00",
        "endTime": "2026-08-30T00:00:00+00:00",
        "published": "2026-06-01T19:12:00+00:00",
        "updated": "2026-06-01T19:12:00+00:00",
        "content": "We propose to embargo VendorCo Case #70930046 for 90 days.",
        "@context": "https://certcc.github.io/Vultron/ns/context.jsonld"
      }
    }
    
  3. Each participant answers with Embargo Proposal Acceptance (EA), Accept(Invite(Event)), or Embargo Proposal Rejection (ER), Reject(Invite(Event)). Either answer records only that participant's own consent, the Case Owner's included; the case stays at EM.PROPOSED.

    Embargo Proposal Acceptance (EA)

    Records the sender's own consent to the proposed terms. The embargo becomes active only when the Case Owner sends its decision.

    • When… a Participant agrees to proposed embargo terms
    • Send: Accept(Invite(Event)[context=VulnerabilityCase])
    • Send to: the case manager
    • Sent by: the invitee
    • Distinguished by: EM state PROPOSED
    • Reference: Embargo Proposal Acceptance (EA)
    Example activity
    {
      "@context": "https://www.w3.org/ns/activitystreams",
      "type": "Accept",
      "id": "urn:uuid:b2389712-88f4-48ab-9436-19f4237902cf",
      "name": "https://vultron.example/organizations/vendorco Accept https://vultron.example/organizations/vendorco Invite Embargo for VendorCo Case #70930046",
      "context": "urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0",
      "to": "https://vultron.example/cases/1/participants",
      "actor": "https://vultron.example/organizations/vendorco",
      "object": {
        "@context": "https://www.w3.org/ns/activitystreams",
        "type": "Invite",
        "id": "urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0/embargo_proposals/1",
        "name": "https://vultron.example/organizations/vendorco Invite Embargo for VendorCo Case #70930046",
        "context": "urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0",
        "published": "2026-10-23T20:17:07+00:00",
        "updated": "2026-10-23T20:17:07+00:00",
        "summary": "We propose to embargo case 1 for 90 days.",
        "actor": "https://vultron.example/organizations/vendorco",
        "object": {
          "id": "urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0/embargo_events/2026-08-30T00:00:00+00:00",
          "type": "EmbargoEvent",
          "name": "Embargo for VendorCo Case #70930046",
          "context": "urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0",
          "startTime": "2026-06-01T19:12:00+00:00",
          "endTime": "2026-08-30T00:00:00+00:00",
          "published": "2026-06-01T19:12:00+00:00",
          "updated": "2026-06-01T19:12:00+00:00",
          "content": "We propose to embargo VendorCo Case #70930046 for 90 days.",
          "@context": "https://certcc.github.io/Vultron/ns/context.jsonld"
        }
      }
    }
    

    Embargo Proposal Rejection (ER)

    Records the sender's own refusal of the proposed terms. The proposal is rejected for the case only when the Case Owner sends its decision.

    • When… a Participant refuses proposed embargo terms
    • Send: Reject(Invite(Event)[context=VulnerabilityCase])
    • Send to: the case manager
    • Sent by: the invitee
    • Distinguished by: EM state PROPOSED
    • Reference: Embargo Proposal Rejection (ER)
    Example activity
    {
      "@context": "https://www.w3.org/ns/activitystreams",
      "type": "Reject",
      "id": "urn:uuid:e8f7f4c7-0f15-40be-b71d-b83b27316e56",
      "name": "https://vultron.example/organizations/vendorco Reject https://vultron.example/organizations/vendorco Invite Embargo for VendorCo Case #70930046",
      "context": "urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0",
      "to": "https://vultron.example/cases/1/participants",
      "actor": "https://vultron.example/organizations/vendorco",
      "object": {
        "@context": "https://www.w3.org/ns/activitystreams",
        "type": "Invite",
        "id": "urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0/embargo_proposals/1",
        "name": "https://vultron.example/organizations/vendorco Invite Embargo for VendorCo Case #70930046",
        "context": "urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0",
        "published": "2026-10-23T20:17:07+00:00",
        "updated": "2026-10-23T20:17:07+00:00",
        "summary": "We propose to embargo case 1 for 90 days.",
        "actor": "https://vultron.example/organizations/vendorco",
        "object": {
          "id": "urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0/embargo_events/2026-08-30T00:00:00+00:00",
          "type": "EmbargoEvent",
          "name": "Embargo for VendorCo Case #70930046",
          "context": "urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0",
          "startTime": "2026-06-01T19:12:00+00:00",
          "endTime": "2026-08-30T00:00:00+00:00",
          "published": "2026-06-01T19:12:00+00:00",
          "updated": "2026-06-01T19:12:00+00:00",
          "content": "We propose to embargo VendorCo Case #70930046 for 90 days.",
          "@context": "https://certcc.github.io/Vultron/ns/context.jsonld"
        }
      }
    }
    
  4. As Case Owner, send Accept(Event) with the proposed EmbargoEvent as its object and the case as its target, once you judge the proposal has carried. The case moves to EM.ACTIVE, and your own consent to the terms is recorded with it.

    Activate embargo

    Activates the proposal for the case. If an embargo is already active, the proposal replaces it.

    • When… the Case Owner decides to activate a proposed embargo
    • Send: Accept(Event)[target=VulnerabilityCase]
    • Send to: the case manager
    • Sent by: the case owner
    • Reference: Activate embargo
    Example activity
    {
      "@context": "https://www.w3.org/ns/activitystreams",
      "type": "Accept",
      "id": "urn:uuid:8e328f3e-45c2-47d7-b5dd-d8d52cad3e2f",
      "name": "https://vultron.example/organizations/vendorco Accept Embargo for VendorCo Case #70930046 to urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0",
      "context": "urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0",
      "actor": "https://vultron.example/organizations/vendorco",
      "target": "urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0",
      "object": {
        "id": "urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0/embargo_events/2026-08-30T00:00:00+00:00",
        "type": "EmbargoEvent",
        "name": "Embargo for VendorCo Case #70930046",
        "context": "urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0",
        "startTime": "2026-06-01T19:12:00+00:00",
        "endTime": "2026-08-30T00:00:00+00:00",
        "published": "2026-06-01T19:12:00+00:00",
        "updated": "2026-06-01T19:12:00+00:00",
        "content": "We propose to embargo VendorCo Case #70930046 for 90 days.",
        "@context": "https://certcc.github.io/Vultron/ns/context.jsonld"
      }
    }
    
  5. Send Announce(Event) so every participant has the terms in hand.

    Announce embargo

    Tells participants the terms of the active embargo. It proposes nothing.

    • When… the Case Manager states the terms of the embargo in force
    • Send: Announce(Event)[context=VulnerabilityCase]
    • Send to: a participant
    • Sent by: the case manager
    • Distinguished by: register status of the named embargo ACTIVE
    • Reference: Announce embargo
    Example activity
    {
      "@context": "https://www.w3.org/ns/activitystreams",
      "type": "Announce",
      "id": "urn:uuid:47ba6266-54b3-4008-9c4f-b4a7f18818e7",
      "name": "https://vultron.example/organizations/vendorco Announce Embargo for VendorCo Case #70930046",
      "context": "urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0",
      "to": "urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0/participants",
      "actor": "https://vultron.example/organizations/vendorco",
      "object": {
        "id": "urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0/embargo_events/2026-08-30T00:00:00+00:00",
        "type": "EmbargoEvent",
        "name": "Embargo for VendorCo Case #70930046",
        "context": "urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0",
        "startTime": "2026-06-01T19:12:00+00:00",
        "endTime": "2026-08-30T00:00:00+00:00",
        "published": "2026-06-01T19:12:00+00:00",
        "updated": "2026-06-01T19:12:00+00:00",
        "content": "We propose to embargo VendorCo Case #70930046 for 90 days.",
        "@context": "https://certcc.github.io/Vultron/ns/context.jsonld"
      }
    }
    

To turn the proposal down, send Reject(Event) with the same object and target as Case Owner instead. The case returns to EM.NONE and the negotiation is open again. Propose revised terms with another Invite(Event).

Reject embargo proposal

Rejects the proposal for the case. An active embargo stays in force on its current terms.

  • When… the Case Owner decides to reject a proposed embargo
  • Send: Reject(Event)[target=VulnerabilityCase]
  • Send to: the case manager
  • Sent by: the case owner
  • Reference: Reject embargo proposal
Example activity
{
  "@context": "https://www.w3.org/ns/activitystreams",
  "type": "Reject",
  "id": "urn:uuid:5e971ae4-5dfd-4f93-98a0-3cc9fdc0f78e",
  "name": "https://vultron.example/organizations/vendorco Reject Embargo for VendorCo Case #70930046 to urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0",
  "context": "urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0",
  "actor": "https://vultron.example/organizations/vendorco",
  "target": "urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0",
  "object": {
    "id": "urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0/embargo_events/2026-08-30T00:00:00+00:00",
    "type": "EmbargoEvent",
    "name": "Embargo for VendorCo Case #70930046",
    "context": "urn:uuid:d483d06d-400e-47fd-9b8a-9a2643e3a4b0",
    "startTime": "2026-06-01T19:12:00+00:00",
    "endTime": "2026-08-30T00:00:00+00:00",
    "published": "2026-06-01T19:12:00+00:00",
    "updated": "2026-06-01T19:12:00+00:00",
    "content": "We propose to embargo VendorCo Case #70930046 for 90 days.",
    "@context": "https://certcc.github.io/Vultron/ns/context.jsonld"
  }
}

Propose one set of terms at a time. That is how the protocol resolves competing terms, not a workaround: there is no poll across candidate embargoes (ADR-0100). You may put several sets of terms on the table by sending a separate Invite(Event) for each, and each is then accepted or rejected on its own. When more than one proposal is open, Participants take the one with the earliest end date first and treat the rest as revisions (EP-08). See Default Embargoes.


Activate settled terms without waiting

There is no way to put an embargo on a case except by proposing it: adding an embargo to the case is what proposing does. When the terms need no negotiation, propose them as Case Owner and activate them at once, without waiting for any answer.

  1. Send Invite(Event) as above. Proposing the terms records your consent to them.
  2. Send Accept(Event) with the case as its target.
  3. Send Announce(Event).

Use this route when the terms need no negotiation:

  • no other participant has joined the case yet, or
  • your published EmbargoPolicy applies and nobody has proposed anything to the contrary.

The other participants still receive the Invite and answer it, and only those that agree are bound, so activating at once binds nobody who did not agree.

A new case may already be embargoed

An embargo-eligible case begins with an active embargo even when no proposal exchange is visible, because a Protocol Default applies when no proposal and no actor default does. A Reporter who wants other terms states them on the report Offer rather than by a proposal exchange; see How to Report a Vulnerability and the report-with-embargo demo there. See Default Embargoes.


Answer an embargo already in force

The Case Owner can activate an embargo before every participant has answered its Invite. Once the embargo is in force it is no longer an open proposal, but a participant that has not answered can still answer it.

  1. Find the Invite(Event) you received for the embargo in force.
  2. Call accept-embargo or reject-embargo with that Invite's ID as proposal_id. You send the same Accept(Invite(Event)) or Reject(Invite(Event)) as for an open proposal, addressed to the CASE_MANAGER.

You can answer this way while your consent row for the embargo in force is INVITED or TIMED_OUT. An Accept sent after the reply deadline is honored, because the terms you agree to are the terms in force (EMB-17-002). The trigger reports not found when you hold no such Invite, or when your row is already AGREED or DECLINED.

Name the Invite. A call without proposal_id answers only an open proposal and never the embargo in force (EP-09-012), so it reports not found when no proposal is open.


Verify

What you sent What to confirm
Invite(Event) The case em_state is PROPOSED.
Accept(Invite(Event)) Your embargo consent row for the embargo is AGREED (you are a signatory), as §9 Participant Embargo Consent (PEC) State Machine in the specification defines it.
Accept(Event) with the case as target, as Case Owner The case em_state is ACTIVE, the case names one active embargo, and your consent row for it is AGREED.
Reject(Event) with the case as target, as Case Owner The case em_state is NONE and no participant's consent row changed.
Announce(Event) Every participant's replica carries the same active embargo ID.

See it end to end

Try it: vultron-demo establish-embargo

vultron-demo establish-embargo

Or with Docker Compose:

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

The scenario runs both the accepted and the rejected proposal.


Further reading