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":
signature_valid— Ed25519 sulraw_bodyconservato vs pubkey del nodo registrata;event_in_signed_batch— l’event_idappartiene all’envelope firmato;stored_payload_matches— la rigacp_eventsnon è 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 |