Chio/Docs
LOGIN · JOIN

Pheromone Store

Share time-sensitive observations between agents and organizations, then rank work by their decayed, receiver-weighted strength.

Concept chio@5f63e6e

On this page

Agents can tell each other what they have seen. One agent deposits a signed signal, such as a suspected prompt injection in a support workflow, and agents behind other kernels rank their next work by how strong that signal still is.

Each receiving kernel decides which deposits it admits, how quickly they fade, and how far it trusts their origin. Signals rank work and grant nothing: acting on one still takes a grant the kernel admits. The running example is the support-workflow fixture, a support.prompt_injection observation in the dev.chio.support namespace.

A signal fades on a clock the deposit carries

Every deposit names a starting confidence and its own half-life. Its strength halves with each half-life until it falls below its evaporation floor and leaves the sum.

Three pheromone deposits decaying over time, with their weighted sum10.10.01012345678evaporation floor φ = 0.01t (h)s(t)log scaleAllamaworksBdatacoCnewcoTt = 5.50 hwB = 0.60peer weight of dataco00.51dC = 2/8epoch age of newco12345678e − eC + 1
si(t)=ci⋅2−(t−ti)/hi\htmlClass{role-call}{s_i}(t) = c_i \cdot 2^{-(t - t_i)/h_i}
T(t)=∑i : si(t) ≥ φisi(t) wi di\htmlClass{role-seal}{T}(t) = \sum_{i \,:\, s_i(t) \,\ge\, \varphi_i} \htmlClass{role-call}{s_i}(t)\, \htmlClass{role-grant}{w_i}\, \htmlClass{role-grant}{d_i}
di=min⁡ ⁣(e−ei+1H, 1),  H=8\htmlClass{role-grant}{d_i} = \min\!\left(\tfrac{e - e_i + 1}{H},\ 1\right),\ \ H = 8
T(5.50)=0.018+0.126⋅0.60+0.050⋅0.25=0.106\begin{aligned} \htmlClass{role-seal}{T}(5.50) &= 0.018 \\ &\quad + 0.126 \cdot \htmlClass{role-grant}{0.60} \\ &\quad + 0.050 \cdot \htmlClass{role-grant}{0.25} \\ &= \htmlClass{role-seal}{0.106} \end{aligned}
Three pheromone deposits decaying over time, with their weighted sum10.10.0102468evaporation floor φ = 0.01ts(t)log scaleAllamaworksBdatacoCnewcoTt = 5.50wB = 0.6000.51dC = 2/813578
T(5.50)=0.018+0.076+0.013=0.106\htmlClass{role-seal}{T}(5.50) = 0.018 + 0.076 + 0.013 = \htmlClass{role-seal}{0.106}

At hour 5.5, the weighted concentration is 0.106. 3 of 3 deposits count.

si(t)=ci⋅2−(t−ti)/hiT(t)=∑i : si(t) ≥ φisi(t) wi didi=min⁡ ⁣(e−ei+1H, 1),  H=8\begin{aligned} \htmlClass{role-call}{s_i}(t) &= c_i \cdot 2^{-(t - t_i)/h_i} \\[6pt] \htmlClass{role-seal}{T}(t) &= \sum_{i \,:\, s_i(t) \,\ge\, \varphi_i} \htmlClass{role-call}{s_i}(t)\, \htmlClass{role-grant}{w_i}\, \htmlClass{role-grant}{d_i} \\[6pt] \htmlClass{role-grant}{d_i} &= \min\!\left(\tfrac{e - e_i + 1}{H},\ 1\right),\ \ H = 8 \end{aligned}
In Rust · substrate.rs:21312 lines
let strength = strength_at(deposit, now_unix_ms);
if let Some(floor) = deposit.body.evaporation_floor {
    if strength < floor {
        continue;
    }
}
let weight = peer_weight(&deposit.body.kernel_id, reputation_epoch);
// … WeightOutOfRange unless 0.0 <= weight <= 1.0
let discount = newcomer_discount_for_deposit(
    // … deposit, context, epoch, subject class
)?;
total_strength += strength * weight * discount;
Three illustrative deposits on one subject class; A uses the fixture’s confidence 0.82 and one-hour half-life, and hours are illustrative. A strong signal does not become a receipt, a grant, or reputation evidence, and its decay says nothing about whether the observation was true.Federation and swarms

The receiver scales each remaining deposit by two factors it owns: the origin kernel’s peer weight and a newcomer discount for young passports. The sum, the concentration, changes with evaluation time even when no new deposit arrives. The mechanism and its CLI keep the name pheromone (chio pheromone, the chio-pheromone crate); the public name is signals.

The agent signs the observation, and a verifier can sign its cost

A deposit is an agent-signed observation carrying identity, treaty scope, confidence, time, and optional workflow evidence. Gossip carries deposits to a recipient in batches; the receiver checks the envelope and each frame on its own.

Deposit admissionChoose a stage.
aagentpassport keyσcost verifiersigns bodybinds hashesdepositframeσwcnBKreceivercommitsBstorenonce · counters · deposittime · epoch · peer weightsTconcentrationranks workaagentσverifiersigns bodybinds hashesdepositframeσwcnBKcommitsBstoretime · epoch · weightsTconcentrationranks work

σsignaturewworkflowccostnnonceBcapacity

Two signers sign two different things

The agent’s passport key signs the canonical body with cost_commitment removed, so workflow_context is covered. A cost verifier signs a separate statement that binds depositBodySha256 and depositSignatureSha256 to its policy and telemetry evidence. Deposit fields.

One deposit from the support-workflow example on its way into one receiving kernel. The five checks are drawn in the order the receive path reads them; gossip batching, hub transit, and the scarcity policy’s own signed envelope are left out.

The agent’s passport key signs canonical JSON of the deposit body with cost_commitment removed, so workflow_context stays covered and a changed workflow reference breaks the signature. A cost statement carries its own signature. It binds depositBodySha256, the hash of that same body projection, and depositSignatureSha256, the hash of the signature’s hex text as bytes.

Where policy requires observation-cost evidence, the receiver checks the statement’s deposit and policy bindings, cost unit, and observation window. It resolves an authorized verifier root, checks the trust-floor state, verifies the statement signature, and rebuilds the telemetry leaf for its RFC 6962 inclusion proof against the signed telemetry root. That authenticates a scoped verifier statement; the receiver still judges the verifier and its measurement policy.

Read more in Deposit fields, which lists every signed field and the retained fixture.

The receiver admits each frame on its own, in one transaction

A gossip batch addresses one or more deposit frames to a recipient under a treaty. The receive path runs four boundaries in order:

  1. Batch and frame. Recipient, treaty, and origin are checked against the authenticated sender and the transit policy. Direct-origin frames and hub transit verify under different context, so a sender cannot relabel an origin.
  2. Deposit evidence. Schema, fields, time, subject policy, the resolved passport, and the agent signature, then workflow references and any required cost evidence.
  3. Admission state. The nonce, scoped capacity, per-pair count, and distinct-passport limit. The SQLite store commits these with the passport and the deposit in one transaction.
  4. Batch result. Each frame runs under its own savepoint, so a refused frame rolls back while valid siblings commit. The report carries per-frame verdicts and batch_outcome.

The report’s accepted flag is true only when the whole batch is accepted. A partial batch reports accepted=false and batch_outcome=partial with some deposits stored, and the CLI writes that report before it exits nonzero, so read the frame verdicts before assuming nothing was admitted.

The outbound gossip queue is bounded FIFO per peer and treaty. It keeps separate deposits, never replacing an older observation with a newer one, and drops the oldest frame when full. A queued frame is not stored until the receiver admits it. A custom store calls validate_deposit_for_admission for the stateless checks, then runs the stateful admission that consumes the nonce and capacity counters.

Read more in Relay Operations, which covers durable forwarding and recovery.

Three counters bound how much one origin can deposit

The receiver applies three overlapping limits, all counters inside a scoped window. Successful admission consumes capacity; a signature check alone consumes none, and the counters do not refill continuously.

LimitScopeRefusal
Shared capacity, tokenCapacityEpoch, window, treaty, namespace, and class, across every originrate_limit_exhausted
Per pair, maxDepositsPerPairThe same scope plus origin kernel and passport keydiversity_cap_exceeded
Passports per kernelActive-peers epoch, window, treaty, namespace, class, origin kernelsqrt_n_passport_cap_exceeded

One kernel can use at most ceil(sqrt(max(activePeers, 1))) distinct passport keys in that scope. With 64 active peers that is 8, and a ninth passport is refused; 16 peers allow 4 and 256 allow 16, so a population four times larger doubles the cap. The cap limits passport proliferation per admitted kernel; who counts as a peer is the receiver’s admission decision.

The limits come from a PheromoneScarcityPolicy: hash-bound, windowed material inside the runtime policy document. The loader verifies the enclosing signed envelope and requires its signer in both the externally trusted issuer roots and the admission policy’s issuer roots. Admission resolves exactly one active policy for the scope; a missing, invalid, stale, or ambiguous policy refuses the deposit.

Read more in Reputation & Scarcity, which models the capacity these counters allow.

The fixture’s query keeps 0.5625 of its decayed strength

The public fixture queries support.prompt_injection half a second after the observation, at reputation epoch 42, with a peer weight of 0.75 for the origin kernel. From the three-vendor example directory (chio new three-vendor --example three-vendor), run against a store that admitted the fixture deposit, the query writes this report:

Terminal · three-vendorbash
chio pheromone query \
  --store ./pheromone.db \
  --subject-class support.prompt_injection \
  --namespace dev.chio.support \
  --reputation-epoch 42 \
  --peer-weights \
    ./fixtures/pheromone/peer-weights.json \
  --now-unix-ms 1766000000500 \
  --report ./concentration-report.json
concentration-report.json
{
  "schema": "chio.pheromone.query-report.v1",
  "accepted": true,
  "concentration": {
    "schema": "chio.pheromone-concentration.v1",
    "subject_class": "support.prompt_injection",
    "subject_class_namespace": "dev.chio.support",
    "total_strength": 0.4612055973961088,
    "unweighted_total_strength": 0.8199210620375268,
    "distinct_origin_pairs": 1,
    "peak_confidence": 0.82,
    "reputation_epoch": 42,
    "evaluated_at_unix_ms": 1766000000500,
    "treaty_scopes": [
      "treaty:buyer-dataco:support-ops",
      "treaty:buyer-llamaworks:support-ops"
    ]
  }
}

You should see the raw decayed strength, 0.82 halved over 0.5 of 3,600 seconds, as unweighted_total_strength. The passport was first seen in epoch 37, so its age at epoch 42 is 6 epochs and its newcomer factor 6/8 = 0.75. Peer weight times newcomer factor is 0.5625, which brings the contribution to total_strength 0.4612055973961088.

Newcomer age is max(epoch − firstSeenEpoch, 0) + 1. With the default horizon of 8, a first-epoch passport counts one eighth and reaches full weight after eight epochs; a passport missing at query time also takes the first-epoch discount. The standard weights provider requires the weights file’s epoch to match and gives an unlisted kernel weight 1.0, so list explicit zero weights where your policy needs them.

The retained report is fixtures/pheromone/query-report.json in the three-vendor example. Read more in the 3-Vendor Walkthrough, which supplies the surrounding workflow.

The example project refuses a replayed nonce and a kernel-signed deposit

Create separate passport and kernel keys, supply receiver-owned passport admission and scarcity policy, and deposit a signed observation. Query it at an explicit reputation epoch with a peer weight of 0.8. Download the project and follow the Rust example setup.

Terminalbash
cargo run --locked --features pheromone --bin pheromone
Captured output
concentration: {"schema":"chio.pheromone-concentration.v1","subject_class":"support.prompt_injection","subject_class_namespace":"dev.chio.support","total_strength":0.49195263722251614,"unweighted_total_strength":0.8199210620375268,"distinct_origin_pairs":1,"peak_confidence":0.82,"reputation_epoch":42,"evaluated_at_unix_ms":1789230068161,"treaty_scopes":["treaty:buyer-llamaworks:support-ops"]}
replayed observation: replay_window_exceeded: nonce-001
kernel-signed observation: signature_invalid: deposit signature does not verify

You should see a concentration whose total_strength is 0.6 of the decayed confidence, the 0.8 peer weight times a 0.75 newcomer discount, then two refusals: the repeated nonce fails with replay_window_exceeded, and the deposit signed with the kernel key instead of the admitted passport fails with signature_invalid. The store is local and in memory; a shared reputation service is a separate component.

Example source: src/bin/pheromone.rs
pheromone.rsrust
use chio_core_types::Keypair;
use chio_pheromone::*;
use observation::{body, live_context};
use swarm_authority_examples::now_ms;
use swarm_authority_examples::observation;
fn main() -> Result<(), Box<dyn std::error::Error>> {
    let passport = Keypair::generate();
    let kernel = Keypair::generate();
    let context = live_context(&passport, &kernel);
    let store = InMemoryPheromoneSubstrate::new();
    let deposit = sign_deposit(body(&passport), &passport)?;
    store.deposit(deposit.clone(), &context)?;
    let concentration = store.query_concentration(
        "support.prompt_injection",
        "dev.chio.support",
        now_ms(),
        42,
        &context,
        &|_peer, _epoch| 0.8,
    )?;
    println!("concentration: {}", serde_json::to_string(&concentration)?);
    println!(
        "replayed observation: {}",
        store
            .deposit(deposit, &context)
            .expect_err("nonce cannot repeat")
    );
    let mut new_body = body(&passport);
    new_body.nonce = "nonce-002".into();
    let wrong_signer = sign_deposit(new_body, &kernel)?;
    println!(
        "kernel-signed observation: {}",
        store
            .deposit(wrong_signer, &context)
            .expect_err("passport must sign")
    );
    Ok(())
}

Reconstructing a result takes its inputs, beyond the epoch

A concentration identifies its inputs by reputation_epoch and evaluated_at_unix_ms. Rebuilding it takes the same retained deposits, evaluation time, peer weights, passport first-seen data, and newcomer policy. The query checks the context it is given; it fetches no anchored reputation snapshot and does not recheck stored passports against a live revocation service.

The standalone chio pheromone query supplies no scarcity policies, so it uses the default horizon of 8 even where admission used a custom one. The receiver API can query with explicit policy context. SQLite fills in stored passport records beside the caller’s context.

An explicit evaporation floor excludes deposits below that raw strength. The in-memory garbage collector uses 0.01 when the field is absent, while the query applies no fallback floor, so collection changes the corpus. Retain the records you need before reconstructing a past result; results are reproducible from retained inputs, with summation order able to move the last bits.

Deposit fields, refusal codes, and commands

Deposit fields

The wire schema is chio.pheromone-deposit.v1. The serialized deposit flattens its body fields beside signature.

kernel_id, agent_passport_key_hash, agent_passport_jwk_thumbprint

Origin kernel and agent signing-key references. Admission resolves an allowed passport in the receiver context, checks its validity and revocation flag, and refuses a configured kernel key used as the depositor key.

subject_class_namespace, subject_class, indicator

The domain and exact class reported, plus an application-defined indicator. Concentration matches class and namespace exactly, with no automatic class hierarchy.

confidence, severity

Confidence is the starting strength. Severity is descriptive metadata and stays out of the concentration formula.

timestamp_unix_ms, decay_half_life_secs, evaporation_floor

Observation time, a positive per-deposit half-life, and an optional strength cutoff, validated against the receiver’s time policy before admission.

nonce, treaty_scope

Replay identity and allowed treaty scopes. The reference admission path requires an accepted treaty; an empty list admits no local-only deposit.

workflow_context

Signed references to the workflow, receipt, intersection, step, tool receipt, bilateral envelope digest, and consistency anchor. The runtime resolver checks them against verified workflow evidence.

cost_commitment

A separately signed observation-cost statement. Subject-class and scarcity policy decide whether it is required and verified.

The retained fixture below is the observation the query report reads. Body and workflow fields use snake_case; the cost statement uses camelCase, while its nested inclusionProof keeps tree_size, leaf_index, and audit_path.

examples/chio-3vendor/fixtures/pheromone/deposit.jsonjson
{
  "schema": "chio.pheromone-deposit.v1",
  "kernel_id": "did:chio:llamaworks",
  "agent_passport_key_hash": "1f5fe64ec2f8c901be3aad94a41d70c12d2f3a8f43755f902e665c96f41c87e3",
  "agent_passport_jwk_thumbprint": "lWkhqaoVMPD-k3r62OPO7M8ak2U9g1rMbZFNL9STZ50",
  "subject_class": "support.prompt_injection",
  "subject_class_namespace": "dev.chio.support",
  "indicator": {
    "indicatorDigest": "ec8cf4e0ba62aa1d2a7609e2653fc88aa1df035bbbab9b9c82781053fc27bf24",
    "kind": "prompt_injection",
    "workflowId": "wf-chio-refund-001"
  },
  "severity": "high",
  "confidence": 0.82,
  "timestamp_unix_ms": 1766000000000,
  "decay_half_life_secs": 3600.0,
  "evaporation_floor": 0.01,
  "nonce": "pheromone-nonce-llamaworks-001",
  "treaty_scope": [
    "treaty:buyer-llamaworks:support-ops",
    "treaty:buyer-dataco:support-ops"
  ],
  "cost_commitment": {
    "schema": "chio.pheromone-cost-commitment.v1",
    "statement": {
      "schema": "chio.pheromone-observation-cost-statement.v1",
      "commitmentId": "cost-commitment-llamaworks-001",
      "verifierId": "did:chio:dataco-cost-verifier",
      "verifierKeyId": "cost-root-key-1",
      "runtimePolicySha256": "b0cfe65c6f966a7e7b3abbc3490bcc9a2c2546e46df757aa15ab6473096f6955",
      "scarcityPolicySha256": "69073f134018c3cc27f53726aa2a98a63bdfedf6bd68bfde7bb2b3777f3050d4",
      "depositBodySha256": "acd0286d4d079536cd32c91c0b48f214195ae0cb1e8e6ff62662ddc95e81ce9f",
      "depositSignatureSha256": "a69c9d048e8bd66cf6bdccbb3ad0f0e086d29597cfe4ead175881831c3ac4f1c",
      "kernelId": "did:chio:llamaworks",
      "agentPassportKeyHash": "1f5fe64ec2f8c901be3aad94a41d70c12d2f3a8f43755f902e665c96f41c87e3",
      "treatyId": "treaty:buyer-dataco:support-ops",
      "subjectClassNamespace": "dev.chio.support",
      "subjectClass": "support.prompt_injection",
      "observationWindowStartUnixMs": 1765999940000,
      "observationWindowEndUnixMs": 1766000060000,
      "observedAtUnixMs": 1766000000000,
      "eventDigestSha256": "b012fd9cf705d6123879782db8db9fc964ffd0ac0ce1ae3873c562f0120739bd",
      "cost": {
        "unit": "chio.observation.microunit.v1",
        "amount": 125
      },
      "telemetry": {
        "schema": "chio.pheromone-observation-cost-telemetry-root.v1",
        "algorithm": "rfc6962-sha256-v1",
        "rootHash": "0x03d57ee94d7c00e7980572b34426973abe83bd7e7c38a3c230f187b3d4cad693",
        "treeSize": 1,
        "verifierId": "did:chio:dataco-cost-verifier",
        "verifierKeyId": "cost-root-key-1",
        "closedAtUnixMs": 1766000000250
      },
      "inclusionProof": {
        "tree_size": 1,
        "leaf_index": 0,
        "audit_path": []
      },
      "leafPreimageSha256": "87e975957c8a68e5ef9fad681bfd5b9635eec4f3c261186808a9b1ece26d9576"
    },
    "signature": "a18ba6cbaeb3689c098f980bcbc7fdf215777155644ec4b79b679ead7f221a674255b49e07a15e0244d54fe82a593963066be36d676757cb331d35c0e3e9090e"
  },
  "workflow_context": {
    "schema": "chio.pheromone-workflow-context.v1",
    "workflow_id": "wf-chio-refund-001",
    "workflow_receipt_id": "wf-chio-refund-001",
    "workflow_receipt_sha256": "7fffe9c72b7113a65d0abadf73f98099e92e31ee5a5595a304199608a2ae0a10",
    "workflow_intersection_id": "workflow-intersection:buyer-refund:001",
    "workflow_intersection_sha256": "c852513d4ebcee42d0d5d020eb9bf3be40b85c7dec78e4c2578898ecd89892dd",
    "step_index": 0,
    "tool_receipt_id": "ea1f74990eaef54beaac9d7af04893a2320390b773b556fbd310fa9246ed3b3d",
    "bilateral_dsse_sha256": "3e7ac510ae43d4668966534d81c7592d2c7bb62d10e7938e1bb7c9e477c49077",
    "consistency_anchor": "chio:consistency:wf-chio-refund-001:0"
  },
  "signature": "5a6f94c43773d1f97174d5aa65a32ac9fe16e9900593617a30ecfb33197db54d6a1eed72ec8fcf142ce404c48e8c173eb9f7590b622c0b916fd6ff20b5a60d0d"
}

Concentration recipe and result fields

For each matching deposit above its evaporation floor, the query multiplies decayed confidence by the origin’s peer weight and the passport’s newcomer discount.

Concentration recipetext
raw = confidence * 2^(-age_s / half_life_s)
contribution = raw * peer * newcomer

total_strength = sum(contribution)
unweighted_total_strength = sum(raw)

total_strength

Sum after decay, peer weighting, and newcomer discount. An aggregate strength, unbounded by [0, 1].

unweighted_total_strength

Sum of decayed confidence before peer weighting and newcomer discount.

distinct_origin_pairs

Distinct kernel and passport-key pairs among the non-evaporated deposits considered. A zero peer weight keeps a pair in this count.

peak_confidence

The largest original confidence among those deposits, before decay or weighting.

reputation_epoch, evaluated_at_unix_ms

The requested weighting epoch and evaluation time. They name inputs; the corpus and trust context are retained separately.

treaty_scopes

Sorted union of the included deposits’ treaty scopes. It describes the deposits, and makes no claim that the query was authorized under each treaty.

Scarcity policy fields

The policy’s camelCase wire fields include tokenCapacity, newcomerHorizonEpochs, reputationEpoch, activePeersEpoch, treatyScope, subjectClassNamespace, subjectClass, and the window bounds. Policy and window hashes bind the configured material.

Refusal codes

CodeInspect next
signature_invalid
The agent signing key, the canonical body projection, and post-signing changes. workflow_context is inside the signature.
kernel_key_used_for_deposit
Sign with the admitted agent passport key, never a configured kernel key.
replay_window_exceeded
Elapsed age and duplicate nonce state. A reused origin, passport, and nonce is refused; the exact window endpoint counts as expired.
deposit_from_future
timestamp_unix_ms against the receiver’s explicit evaluation time.
treaty_scope_violation
The deposit’s treaty list and the receiver’s allowed subject-class scope.
rate_limit_exhausted
The shared epoch, window, treaty, and class capacity is spent. A fresh passport does not create a new shared bucket.
diversity_cap_exceeded
The origin kernel and passport pair reached its scoped deposit limit.
sqrt_n_passport_cap_exceeded
The origin kernel reached its distinct-passport cap for that scope.
scarcity_policy_missing, scarcity_policy_ambiguous, scarcity_window_stale
Resolve one valid active scarcity window for the receiver’s epoch, treaty, namespace, and class.
observation_cost_policy_mismatch
Deposit hashes, origin, passport, treaty, subject, and verifier against the cost statement and policy.
observation_cost_inclusion_invalid
Rebuild the bound leaf and verify the audit path against the signed telemetry root.
unknown_reputation_epoch, weight_out_of_range
Query context, the weights-file epoch, and finite peer weights in [0, 1]. These are query failures, distinct from admission.

Receive and query flags

chio pheromone receive requires --batch, --transit-policy, --proof-package, --trust-bundle, --context, --store, and --report, with the verified workflow material that belongs to the batch. Its evaluation time defaults to the batch’s flushed_at_unix_ms; pass --now-unix-ms to evaluate at the receiver’s current time. chio pheromone query defaults to wall-clock time.

An empty store returns a successful zero concentration with accepted=true. Compare the origin count, raw strength, and frame results before reading a zero as no signal.

Crates

CrateResponsibility
chio-pheromone
Deposit types, signing projection, validation, scarcity rules, and the PheromoneSubstrate trait with an in-memory implementation. Peer weighting is injected.
chio-federation
Scoped gossip frames, bounded push queues, and transit verification.
chio-pheromone-runtime
Verified workflow resolution, receiver orchestration, SQLite admission state, concentration queries, and reports.
chio-pheromone-relay
Durable forwarding, catch-up, and operator lifecycle.

Next: size the capacity a receiver offers