← Developers

05.3 — Oracolo Pubblico & Scope delle Chiavi

Spec riferita a api v1

L’Invariante

Non fidarti di noi — verifica il record. L’oracolo eventi è pubblico, rate-limited e non perde nulla: niente tenant_id, niente credenziali, niente payload — solo il verdetto di integrità.

Il Contratto

GET /api/v1/public/verify/event/:id   — unauthenticated, rate-limited 30/min

Tre controlli indipendenti, tutti necessari per proof: "attributable_integrity_verified":

  1. signature_valid — Ed25519 sul raw_body conservato vs pubkey del nodo registrata;
  2. event_in_signed_batch — l’event_id appartiene all’envelope firmato;
  3. stored_payload_matches — la riga cp_events non è stata alterata post-ingest.

Framing: questa è evidenza di integrità attribuibile — ciò che serve a un auditor, un’assicurazione o una controparte per verifiche in stile ISO-9001. Non è marcatura temporale qualificata eIDAS: è un’estensione esplicitamente dichiarata fuori scope.

Scope delle chiavi (key_scope)

Una API key per tenant, ruotabile via POST /admin/tenants/:id/api-key — la rotazione uccide la chiave precedente all’istante (è anche la demo di revoca live). Negato → 403 SCOPE_INSUFFICIENT da authTenant prima che l’heartbeat venga toccato.

Scope Capability Uso
full Tutto (default back-compat) console operative, legacy
sync solo /api/v1/sync/* nodi edge (emessa al claim)
read-only solo GET/HEAD/OPTIONS, ogni superficie BI, auditor, dashboard
ingest-only solo POST /sync/ingest + /sync/edge-audit produttori di telemetria puri

Il Flusso

auditor: GET /public/verify/event/<id>
  → cp_events.batch_id → cp_ingest_batches (raw_body + firma)
  → cp_edge_nodes.node_pubkey → verifica Ed25519 sui byte raw
  → evento presente in events[] firmato? → payload memorizzato == payload firmato?
  → proof: attributable_integrity_verified

Esempio di Payload

// 200 — verificato
{ "event_id": "…", "signed": true, "node_id": "…",
  "signature_valid": true, "event_in_signed_batch": true,
  "stored_payload_matches": true,
  "proof": "attributable_integrity_verified" }

// 200 — evento sconosciuto o non firmato
{ "event_id": "…", "found": false }   // o signed: false — mai falsi positivi

I Fallimenti Noti

Sintomo Causa → fix
403 SCOPE_INSUFFICIENT Lo scope della chiave non copre metodo+path → ruota con lo scope giusto
found:false su un evento noto Evento arrivato via legacy non-firmato (nessun batch) o id errato — controlla il flag signed
Oracolo 429 Rate limit pubblico 30/min — attendi Retry-After
EDGE_SIGNATURE_REQUIRED Il nodo ha pubkey registrata → invia x-edge-signature sui byte esatti del body