Trust, Provenance & Freshness
com.os.retailagent.shopping.trust
Plane 0 · Foundation · Tier 0 · Discoverable · Reference implementation in Playground
trustMode: "asserted" and the signature prefix sim:. The swap to real HMAC-SHA256 or ECDSA is mechanical (one function body in trust.ts). See WP-19.Abstract
RAOS-0008 operationalizes the provenance and freshness envelope that RAOS-0000 §9 defined at contract level. It answers three questions every trust-conscious agent must ask:
- Is this data from who it claims to be from? — The signed payload envelope: issuer, key identifier, and a deterministic signature.
- Is this data still current? — Per-stage TTL defaults (price 300s, inventory 60s, eligibility 3600s). Staleness has a behavior matrix: refuse for QUOTE, degrade+flag for advisory.
- Can I trust the key? — Merchant manifest carries keys[] with validity windows. Key rotation is a first-class concept.
How long is data considered fresh?
| Stage | Default TTL |
|---|---|
| ELIGIBILITY / VISIBILITY | 3600s (1h) |
| PRICE | 300s (5m) |
| INVENTORY | 60s (1m) |
| QUOTE | verified at use |
Reason code registry
One namespace, six codes. Four are synthetic-only (require injected failures; tested in trust.test.ts).
| Code | Meaning | Severity | Resolvable? |
|---|---|---|---|
TRUST_SIMULATED | Crypto is simulated. Every envelope produced while B3/D2 is in effect carries this INFO code. Always emitted — it is the "this is not real security" label that prevents simulation being mistaken for production trust. | INFO | Advisory / not resolvable |
DATA_STALE | Data has exceeded its TTL. For advisory stages (PRICE, ELIGIBILITY): degrade and flag. For QUOTE stage: refuse. The agent must re-fetch before acting. | CONDITION | Re-fetch and re-evaluate with a fresh envelope |
SIGNATURE_INVALIDsynthetic-only | Signature does not match the canonicalized payload. The payload may have been tampered with. Most-restrictive: BLOCK. | BLOCK | Advisory / not resolvable |
ISSUER_UNKNOWNsynthetic-only | The keyId in the envelope does not match any key in the merchant's published keys[]. Cannot verify provenance of this data. | BLOCK | Advisory / not resolvable |
KEY_EXPIREDsynthetic-only | Key found in merchant manifest but validTo < now (±60s skew allowance). The merchant must rotate to a new key. | BLOCK | Merchant rotates to a new key in keys[]; re-issue envelope with new keyId |
CLOCK_SKEW_SUSPECTEDsynthetic-only | computedAt is in the future beyond the clock-skew tolerance (±60s). The agent and merchant clocks may be out of sync. | CONDITION | Re-synchronize agent clock; re-request with correct now |
What attacks does this prevent?
| Attack | Defeated by |
|---|---|
Spoofed merchant | keyId lookup in merchant keys[]. Unknown key → ISSUER_UNKNOWN BLOCK. The agent rejects data from issuers it cannot verify. |
Replayed envelope | computedAt + TTL freshness. Replayed envelopes expire. At QUOTE stage all stale envelopes are refused (not just flagged). |
Stale promo served as fresh | computedAt comes from injected now (never wall-clock). Manipulating computedAt is detectable via CLOCK_SKEW_SUSPECTED if the agent's clock differs. |
Tampered price | signEnvelope canonicalizes the entire payload including unitPrice. A single-character change → different signature → SIGNATURE_INVALID BLOCK. |
Open questions
These are genuine forks. Tell me I'm wrong.
#1Real crypto seam — HMAC-SHA256 or ECDSA-P256?
The interface is real; the simulation is the placeholder. WP-19 swaps in real crypto. HMAC-SHA256 is simpler (shared secret per merchant); ECDSA-P256 enables decentralized verification (public key published in manifest, no shared secret distribution).
Leaning: HMAC-SHA256 for v1 (simpler key management for small merchants), ECDSA as a v2 upgrade path. The seam makes either mechanical.
#2Per-field freshness vs per-result freshness
The envelope today attaches to each ExtensionResult (per-evaluator). A richer model would expose per-field freshness (price is fresh, inventory is stale within the same result). WP-05 already has per-item freshness on ComputedAvailability. Should the outer envelope gain a fieldFreshness slot?
Defer to WP-07 (Quote Integrity) which has the most pressing need for field-level trust. Quote tokens need to know whether the price they are locking was fresh at issue time.
#3Clock-skew tolerance: ±60s uniform or tighter for QUOTE?
Banking systems use ±5s; mobile agents in intermittent connectivity can drift more. ±60s may be generous for high-stakes data (QUOTE). Should the tolerance be configurable per stage?
Leaning: tighten to ±30s for QUOTE-stage data, ±60s for advisory stages. Stage-configurable tolerance is cleaner than one global value.
#4DATA_STALE vs STOCK_STALE disambiguation
RAOS-0008 emits DATA_STALE (trust namespace) and RAOS-0005 emits STOCK_STALE (inventory namespace). Both can fire in the same evaluation. Are they additive? Does one supersede the other?
Additive — they are genuinely different signals. STOCK_STALE is the inventory-domain code (the data source is stale); DATA_STALE is the trust-domain code (the pipeline evaluation result is stale). Agents reading both get richer information.
Have a view on any of these?
The real-crypto algorithm and clock-skew tolerance questions are the most consequential. If you've operated a distributed commerce system and dealt with clock drift or key rotation in production, your experience is exactly what this spec needs.
rikbanerjee007@gmail.comWhy this is foundational
Freshness and provenance are not premium features. An agent that doesn't know whether its data is current or authenticated cannot safely act. RAOS-0008 is Tier 0 because every other spec produces data that can go stale — without this spec, there is no protocol-level way to signal staleness to the agent. WP-07 (Quote Integrity) directly depends on the signing seam: a quote token is only as tamper-resistant as the envelope it carries. The TTL defaults are themselves a data model — they encode the merchant's implicit promise about data freshness.