ServicesRumor

Rumor

Information propagating through a graph, hop by hop. Nodes are NPCs, users, settlements, or agents; directed edges carry delay and fidelity. An injected fact spreads on a schedule, mutates as fidelity decays, is forgotten after a TTL, and can be chased by a correction through the same edges. Query what any node currently believes, or trace who knows what, in which form, via which path.

When to reach for it: living-world news that travels at travel speed, guards two towns over hearing a mutated version of your crime, viral-spread modeling over customer graphs, correction-strategy testing, engine-managed asymmetric information for social deduction games.

When not to: instant delivery to subscribers with no graph, delay, or distortion is PubSub; recording what actually happened in an ordered feed is Timeline.

Concepts

  • A graph is a world. Its edge defaults (latency, fidelity, bandwidth), distortion model, forgetting TTL, and optional deterministic seed are set at creation and govern everything spreading inside it.
  • Facts vs beliefs. A fact is the injected ground truth plus its append-only propagation history. A belief is one node's copy: a possibly mutated form, fidelity_remaining, confidence, the provenance path in received_via, and an expiry.
  • Propagation is scheduled, not instant. Each edge delays delivery by its latency; a background scheduler (1 s resolution by default) delivers hops as they come due. tick drains due hops on demand.
  • Distortion is fidelity-driven. Fidelity multiplies down each edge; below 0.6 the payload mutates. The full model - mutation table, trust weighting, saturation, forgetting - is on the propagation model page.
  • Writes are platform-admin operations. Creating graphs, adding topology, injecting, correcting, quarantining, and ticking require a platform admin token - other callers get 403 insufficient_permissions. Reads (graph get/list, beliefs, trace) and subscribe work for any authenticated user.

Build a graph

Requires a platform admin token:

snug rumor graph create --graph-id docs4-gossip --seed 42 \
  --edge-latency 1 --edge-fidelity 0.7 --ttl 3600
snug rumor nodes add docs4-gossip --file nodes.json
snug rumor edges add docs4-gossip --from docs4-page --to docs4-innkeeper
snug rumor edges add docs4-gossip --from docs4-innkeeper --to docs4-guard

nodes.json is a bare array - here [{"id": "docs4-page"}, {"id": "docs4-innkeeper"}, {"id": "docs4-guard"}] - and each node takes an optional entity_ref (bind to a real user, agent, or ECS entity) and trust_source (a Reputation index for trust weighting).

Edges are directed - add the reverse edge for two-way gossip. Omitted per-edge properties fall back to the graph defaults (latency 3600 s, fidelity 0.85, bandwidth 4; an edge with bandwidth 0 is inert). An edge to a missing node fails with 404 edge_target_not_found; re-creating an existing graph fails with 409 graph_already_exists. The HTTP nodes and edges endpoints take bulk arrays; the CLI adds one edge per call. snug rumor graph list paginates and sorts by created_at, node_count, or edge_count.

Inject a fact

snug rumor inject docs4-gossip --topic king_health \
  --payload fact.json --origin docs4-page --truth

--truth is a bare flag: present marks the fact true, omitted injects a false rumor. Over HTTP, the enveloped response, captured live:

curl -X POST -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"topic":"king_health","payload":{"claim":"the king is wounded","severity":2},
       "truth_value":true,"origin_node":"docs4-page"}' \
  http://localhost:4000/api/v1/rumor/graphs/docs4-gossip/inject
{
  "status": 201,
  "msg": "Created",
  "data": {
    "fact_id": "fact_CstRfMjbmVzupqdMRuZE",
    "graph_id": "docs4-gossip",
    "topic": "king_health",
    "origin_node": "docs4-page",
    "injected_at": "2026-08-28T14:16:11.110710Z",
    "beliefs_recorded": 1,
    "first_hops": [
      { "to_node": "docs4-innkeeper", "scheduled_at": "2026-08-28T14:16:12.110710Z" }
    ]
  }
}

The origin believes immediately at full fidelity and confidence; each first hop is scheduled one edge latency in the future. Injecting at a missing node fails with 404 node_not_found.

Query beliefs and trace spread

snug rumor beliefs docs4-gossip --node docs4-guard --topic king_health

Two hops from the origin (0.7 x 0.7 = 0.49 fidelity, below the 0.6 mutation threshold) the guard believes a distorted form - captured live:

{
  "node_id": "docs4-guard",
  "beliefs": [
    {
      "node_id": "docs4-guard",
      "fact_id": "fact_CstRfMjbmVzupqdMRuZE",
      "topic": "king_health",
      "form": { "claim": "the king is slain", "severity": 2 },
      "fidelity_remaining": 0.49,
      "confidence": 0.49,
      "received_via": ["docs4-page", "docs4-innkeeper", "docs4-guard"],
      "received_at": "2026-08-28T14:16:13.840118Z",
      "expires_at": "2026-08-28T15:16:13.840118Z",
      "corrected": false,
      "truth_value": true
    }
  ],
  "count": 1
}

Expired beliefs are purged on read - after the graph's TTL a node has genuinely forgotten. snug rumor trace <graph> <fact> returns the epidemiological view: every node that knows the fact and the form it holds, the hop-by-hop propagation history, and a prevalence summary - for the fact above, {"true_form": 2, "distorted": 1, "corrected": 0, "total_known": 3}.

Countermeasures

snug rumor correct docs4-gossip --fact fact_CstRfMjbmVzupqdMRuZE \
  --payload correction.json --origin docs4-page
snug rumor quarantine docs4-gossip --node docs4-guard
snug rumor quarantine docs4-gossip --node docs4-guard --release

A correction reports original_footprint (3 nodes knew the rumor), then chases it through the same edges at edge speed, propagating verbatim - corrections never mutate - and overwriting what it finds. After two hops the guard held the corrected payload with "corrected": true, and prevalence read {"true_form": 0, "distorted": 0, "corrected": 3, "total_known": 3}.

An incoming hop to a quarantined node is dropped: no belief recorded, no onward hops scheduled. Verified live - with the guard quarantined, a new fact reached the innkeeper while the guard's belief count stayed 0. Dropped hops are consumed, not parked: releasing the node later does not replay rumors it missed.

Belief-change events

Any authenticated user can provision fanout, optionally filtered to one node and/or one topic, with an optional webhook sink:

snug rumor subscribe docs4-gossip --topic king_health
snug pubsub listen -c rumor.docs4-gossip.beliefs

subscribe returns a subscription_id and the graph's PubSub channel (rumor.docs4-gossip.beliefs). Every belief change matching a subscription - origin write, hop delivery, correction - is published to that channel as the full belief document; verified live end to end. But nothing is published unless some subscription matches: a bare pubsub listen stays silent until one exists (reproduced with a fact on an unsubscribed topic). Webhook deliveries are signed - Snug-Signature (t=...,v1=<HMAC>), X-Webhook-Timestamp, and X-Rumor-Event: belief.changed headers, verified against a local listener. Subscriptions persist for the life of the graph; there is no unsubscribe endpoint.

Limits and configuration

By default: 10,000 graphs, 100,000 nodes per graph, 1,000 edges per node, 32 hops per fact, and a 1 s scheduler tick - all tunable via the RUMOR_* variables in the Rumor CONFIG reference.

Reference

On this page