-
Notifications
You must be signed in to change notification settings - Fork 1
integrity oracle
title: integrity-oracle created: 2026-07-07 updated: 2026-07-15 type: entity tags: [infrastructure, metrics, layer-2, tokenomics] confidence: high source_files:
- integrity-oracle/scoring-core/src/lib.rs
- integrity-oracle/backend/src/handlers.rs
- integrity-oracle/backend/src/derive.rs
- integrity-oracle/backend/src/otlp.rs
- integrity-oracle/backend/src/chain.rs
- integrity-oracle/backend/src/db.rs
- integrity-oracle/backend/src/phi.rs
- integrity-oracle/backend/src/crypto/mod.rs
- integrity-oracle/backend/src/openapi.rs
- integrity-oracle/backend/migrations/0001_init.sql
- integrity-oracle/backend/migrations/0002_markets_and_judge.sql
- integrity-oracle/backend/migrations/0003_agent_did_document.sql
The off-chain brain (Rust, Axum, Postgres, Redis, alloy): it ingests agent
telemetry, computes the Agent Integrity Score, and
independently verifies agents' on-chain state — including, as of this pass, the
market/application layer (§6.9) — so nothing
downstream has to trust an agent's own word. Per §6.10 of the interface contract,
this is the only backend that ever reads on-chain state.
-
scoring-core— dependency-free; the one place the AIS formula is computed. Everyone else reads the HTTP API. -
backend— Axum server, sqlx persistence, Redis rate limiting,bb verifyZK verification, Merkle building, and thealloyon-chain read client.
POST /v1/agent/register
GET /v1/agent/{id}
GET /v1/agents
GET /v1/agent/{id}/ais
GET /v1/agent/{id}/ais/history
GET /v1/agent/{id}/compliance
GET /v1/agent/{id}/wallet
GET /v1/agent/{id}/telemetry
GET /v1/agent/{id}/telemetry/volume
GET /v1/agent/{id}/otel/volume
GET /v1/agent/{id}/traces
GET /v1/traces/{trace_id}
POST /v1/telemetry/ingest
GET /v1/markets
GET /v1/markets/{id}
GET /v1/leaderboard
GET /v1/stream
GET /v1/agent/{id}/stream
GET /healthz
Full request-pipeline order for POST /v1/telemetry/ingest (PHI scan →
agent lookup → rate limit → signature → server-side re-derivation → ZK →
compliance → nonce replay → storage):
Telemetry Ingestion Pipeline.
Real history queries returning lists of past telemetry events and judge traces respectively, queried directly from Postgres (telemetry_events and judge_evaluations tables) and returned as structured DTO arrays. Used by the MVP UI to render live feeds and historical execution logs.
The honesty crux: POST /v1/agent/register independently calls
XibalbaAgentRegistry.resolveDID and rejects (400) if the client's claimed
7 primitives don't match on-chain state. This
is what makes "the chain is the source of truth" real, not decorative.
Request body (RegisterAgentRequest, handlers.rs): did (required — not
agent_id), did_document, primitives (exactly the 7 PrimitiveSetDto
fields), ed25519_pubkey_hex/eth_address_hex (Option<String> each, but
the handler 400s if both are absent), verification_tier (i32, defaults
to 0). Now documented in docs/INTERFACE_CONTRACT.md §6.3 — it was silent
on this schema until 2026-07-09, which is how integrity-sdk's client drifted
from it undetected (see integrity-sdk's "Fixed 2026-07-09"
note).
Real reads of every IntegrityMarket clone via MarketFactory. Enumeration is
concurrent (futures::future::join_all), not a serial loop, and cached in Postgres
(markets_cache + a single-row markets_index_sync marker) behind a 30s staleness
window (handlers::MARKETS_CACHE_STALENESS_SECS) — a documented tradeoff between RPC
load and freshness, not silent staleness. On a cache miss/stale hit, list_markets
re-enumerates MarketFactory.allMarketsCount() (not just refreshes existing rows) so a
newly-created market is actually discovered.
GET /v1/markets/{id} ({id} = the market's contract address) returns question,
outcomeCount, resolved, winningOutcome, resolveDeadline, totalStaked, and
outcomeStaked per outcome (the real pari-mutuel pool — cheap public-getter reads).
An optional ?agent=0x... query param adds a real, single getPosition read as
your_position. Documented gap: enumerating every holder's position across a
market requires indexing PositionEntered events, which this pass does not build —
positions_note in the response says so explicitly rather than silently omitting it.
Uint256 amounts (min_ais_to_enter, total_staked, outcome_staked[i]) are always
decimal strings in both the DB (TEXT, not a numeric type sqlx isn't configured
for) and the JSON DTOs — a uint256 can exceed both i64 and f64's safe integer
range.
Contract-vs-brief discrepancy found while building this: MarketFactory has no
allMarkets() returns (address[]) getter — address[] public allMarkets only
auto-generates an indexed allMarkets(uint256) returns (address) getter. Enumeration
is allMarketsCount() + concurrent allMarkets(i) reads
(ChainClient::all_market_addresses). By-creator listing uses the real
getMarketsByCreator(address) returns (address[]) function, not the
marketsByCreator mapping's auto-getter (which also needs an index).
Ranks agents by real ReputationRegistry.effectiveScore (decimal string, same
ChainClient::effective_score method chain.rs already had). No fabricated P&L:
realized_pnl is always null — computing it for real would require indexing
IntegrityMarket PositionEntered/MarketResolved/PayoutClaimed events across
every market, out of scope for this pass. An honestly-incomplete ranking, not a silent
mock. Only agents with a resolvable on-chain PrimitiveSet (cached, or live-resolved
and cached on the fly) appear.
Real IntegrityToken.balanceOf(sovereignAgent) read (decimal string), plus open
positions: a real getPosition read against every cached market for that agent's
SovereignAgent address, filtered to amount > 0 && !claimed. Documented gap:
transaction_history is always null — transfer/stake/payout history requires
indexing on-chain events (Transfer, PositionEntered, PayoutClaimed, ...), not
built this pass.
POST /v1/telemetry/ingest does not trust a client's self-reported
derived_signals — it independently recomputes entropy/grounding/sacrifice
server-side from the same signed request's raw otel_spans content
(backend/src/derive.rs, mirroring integrity_sdk/telemetry/derive.py's
algorithms closely enough that results agree), and derives compliance
separately via a live on-chain "wins" check. Only the oracle's own
recomputation feeds telemetry_events/AIS — the
client's claim is stored purely as an audit-trail comparison. Two
polarity/calibration bugs were fixed at this exact call site in the same
pass: performance_variance was receiving the SDK's stability-score
polarity (1.0=best) into a column scoring-core treats as a true variance
(0.0=best) — backwards for every agent until fixed; and gpu_hours_verified
now receives an hours-equivalent proxy rather than a pre-normalized [0,1]
index, removing a double-log-compression that capped max-sacrifice agents
around ~100/1000 instead of ~1000. Full pipeline-order writeup (this is
step 5 of an 11-step ordered handler sequence — PHI scan, agent lookup,
rate limit, and signature verification all run first):
Telemetry Ingestion Pipeline. Formula
and trust-model detail: AIS.
Lights up the SDK's already-real OTLPSpanExporter/OTLPMetricExporter
(telemetry/core.py::init_telemetry, gRPC localhost:4317), which
previously exported into a void — nothing listened on that port. Real spans
arrive with no Ed25519/secp256k1 signature envelope (unlike
POST /v1/telemetry/ingest), so this deliberately does NOT touch
telemetry_events/AIS — feeding unauthenticated spans into scoring would
let anyone move an agent's score. Trace export is fully implemented
(PHI-scanned via the same crate::phi backstop, persisted to a separate
otel_spans table, broadcast over SSE); metrics export is accepted (so the
SDK's metric exporter gets a real gRPC response) but not yet parsed or
persisted.
src/phi.rs mirrors integrity-sdk/integrity_sdk/security/redactor.py's regex
categories (PRIVATE_KEY, API_KEY, SSN, CREDIT_CARD, EMAIL, PHONE, MRN)
as a server-side defense-in-depth backstop: it recursively scans every JSON string
leaf in otel_spans (and an optional judge_evaluation), and rejects (400,
AppError::PhiDetected) if any raw, unredacted pattern is found — belt-and-suspenders
for a buggy/bypassed client, never a replacement for the SDK's own client-side
redaction. Runs before any DB/RPC work in ingest_telemetry, so it fails fast. Only
string values are scanned (numeric/structural fields are skipped) — the SDK's redactor
never touches those either, so scanning them here would just produce false positives
on values nobody was ever going to redact.
TelemetryIngestRequest.judge_evaluation (optional) persists into the new
judge_evaluations table (run_id, judge_model, verdict, score,
rationale_summary, linked to the telemetry event). Deliberately NOT part of the
signed envelope — ingest_telemetry's signable JSON (what crypto::verify_agent_signature
checks) does not include this field, so adding a judge evaluation never requires a
client to re-sign, and no existing client's signature breaks. It rides along as an
unauthenticated sidecar on an otherwise-authenticated request. No judge/rubric
implementation exists anywhere in this codebase — this is plumbing only, per the task
scope (a Xibalba Solutions product decision not yet made).
Read-only via alloy (stored as DynProvider so sol! bindings work): resolves
an agent's PrimitiveSet, reads ReputationRegistry.effectiveScore/isZkBoosted,
ComplianceGate.vertical/isHealthcareCompliant, and — new — MarketFactory
enumeration, per-market IntegrityMarket view state, getPosition, and
IntegrityToken.balanceOf. MarketFactory/IntegrityToken singleton addresses are
Option<Address> in the deployments-file parse (Singletons), not required: a
genesis-only deployments file that predates the market layer still parses, and
handlers needing them return a clean ChainError::MissingSingleton (400) instead of
this client failing to even connect.
Telemetry leaves batched into a keccak256 Merkle tree;
because StateAnchor is per-agent, the same epoch root is submitted to each
participating agent's own StateAnchor clone — a documented gas tradeoff.
crypto::canonical_json_bytes (the byte representation ingest_telemetry
verifies an agent's telemetry-envelope signature against) used
serde_json's default compact formatter, which emits non-ASCII string
content as raw UTF-8. Every producer this oracle must verify against
(integrity-sdk/integrity_sdk/bcc.py, bcc_middleware/app/canonical.py)
instead pins Python's ensure_ascii=True (non-ASCII escaped as \uXXXX,
surrogate pairs for astral code points) — both modules' own docstrings
explicitly warned a Rust implementation using a different default here
would produce a different signature, and this one did. Was masked until
now because nothing successfully reached signature verification at all
(see integrity-sdk's matching fix — the SDK's own
request used to fail JSON deserialization before ever reaching this check).
Fixed with a custom AsciiEscapingFormatter overriding only
write_string_fragment (the rest of serde_json::ser::Formatter's default
methods, which CompactFormatter also just uses unmodified, are
inherited).
80 backend + scoring-core lib tests (confirmed via a real run — 72
backend, 8 scoring-core; up from 54 with the derive.rs re-derivation
module's own parity-with-derive.py unit tests among the additions),
including src/phi.rs unit tests covering
every PHI category, the already-redacted-marker non-reflag case, and the
numeric-vs-string-field scan boundary), plus a real full-stack e2e (tests/e2e.rs,
9 tests, opt-in via ORACLE_E2E=1) that stands up live anvil + Deploy.s.sol (which now
deploys the market layer as part of genesis) + a real SDK-registered agent + Postgres
- Redis + the HTTP server, and asserts accept-correct / reject-fabricated primitives,
AIS scoring, the live compliance read, a real (empty, pre-any-market)
GET /v1/markets, a real one-entryGET /v1/leaderboard, a realGET /v1/agent/{id}/walletbalance read, the PHI backstop's real HTTP-level 400, and — new —oracle_e2e_recomputed_grounding_overrides_inflated_client_claim, proving an agent that claims an inflated grounding score while its own signedotel_spanscontain hallucination markers gets the oracle's real, low recomputation stored and scored, never the client's claim. Documented follow-up, not built this pass: a full market lifecycle e2e (enterPosition/resolve/claimPayoutthrough a second real registered agent) — out of scope for this task; the light e2e above proves the real binding/parse/handler path without that heavier setup.
Related: Telemetry Ingestion Pipeline, AIS, agent primitives, Interface Contract.
Generated from INTEGRITY-LATEST/docs/wiki. Edit the canonical repository files, not this mirror.
- A2A Negotiation Protocol [PLANNED]
- AIS API — Versioned Wire Spec
- Agent Integrity Score (AIS)
- Agent Primitives (Self-Sovereign Identity)
- Behavioral Commitment Chain (BCC)
- ComplianceGate & Integrity Health
- Cross-Chain Reputation Sync [PLANNED]
- Decentralized Identifier (DID)
- Identity Ceiling & Verification Ladder [BUILT]
- Integrity Market (Prediction Markets, Binary Options, A2A Capital Allocation)
- Integrity Protocol Specification
- Local Metrology (Client-Side AIS Signal Derivation)
- Merkle Batching & Anchoring Convention
- Observability & PHI Safety Pipeline
- On-Chain Governance
- Persistent Memory Bridge
- Persistent Memory, Genesis Root & Lineage [PARTIALLY BUILT]
- Smart BAA (On-Chain Business Associate Agreement Escrow)
- Telemetry Ingestion Pipeline
- Testing Strategy
- The Four Foundational Primitives
- Xibalba Agent Operating Model
- ZK-ML Model-Inference Verification [PLANNED]
- Zero-Knowledge Proving Pipeline