Files
boc/QUIXZOOM_EVIDENCE_GRAPH.md
T
Bernt bae705aa97 ARCHITECTURE: NFC roadmap, edge AI, audit logging
- Add NFC ePassport roadmap (ICAO 9303, eIDAS)
- Add TensorFlow.js edge face detection (BlazeFace)
- Add structured audit logger (GDPR-compliant)
- Risk scoring support

Part of KYC Apple Native UX v1.1.0
2026-06-29 16:24:48 +00:00

318 lines
8.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# quiXzoom — Evidence Graph & Verified Claim
*Beslutad: 2026-06-17. Ägare: Erik Svensson.*
*Bygger på: QUIXZOOM_VISION.md, QUIXZOOM_VERIFIED_REALITY.md, QUIXZOOM_AVO_MASTERPROMPT_V2.md*
---
## Enheten är utsagan — inte bilden, inte evidenspaketet
Det ni säljer, lagrar, fakturerar och kan bli stämda över är **påståendet**:
> "Livbojen vid kaj 14B existerade och var i korrekt skick 2026-06-17 kl 12:22 UTC."
Det är en `verified_claim`. Allt annat — frames, OCR, gyrodata, liveness — är evidens *för* utsagan.
---
## Claim-schemat
```json
{
"claim_id": "clm_9f3a8c1b",
"claim_type": "object_existence_and_condition",
"claim_text": "Livbojen vid kaj 14B existerar och är i korrekt skick.",
"control_point": "lifebuoy_kaj14b",
"knowledge_pack_id": "maritime_safety_v2",
"trust_threshold": 0.92,
"trust_achieved": 0.961,
"decision": "verified",
"evidence_bundle_id": "evb_7d2f1a9e",
"evidence_bundle_hash": "sha256:a3f8b2...",
"zoomer_id": "z_9981",
"device_id": "dev_ios_xyz",
"session_id": "sess_abc123",
"geo": { "lat": 59.334, "lng": 18.063, "accuracy_m": 4 },
"timestamp_start": "2026-06-17T12:20:41Z",
"timestamp_decision": "2026-06-17T12:22:04Z",
"signature": "ed25519:9f3a...",
"immutable": true
}
```
`trust_threshold` ägs av kunskapspaketet — per claim-typ, aldrig globalt.
`decision` är append-only. Korrektion sker som ny claim som refererar bakåt.
---
## Sufficiency-funktionen — förtroende-ackumulator
Systemet samlar evidens tills `trust_achieved ≥ trust_threshold`. Sedan stannar det.
```
trust_achieved = Σ (weight_i × confidence_i × liveness_factor)
```
Varje evidenselement bidrar med viktad konfidens:
| Element | Vikt (example) | Bidrar till |
|---------|---------------|-------------|
| Visual coverage frame | 0.15 | Objekt existerar |
| OCR serial match | 0.25 | Objekt identifierat |
| Liveness Score >90 | 0.20 | Observation äkta |
| GPS match ±15m | 0.15 | Objekt på rätt plats |
| App Attest valid | 0.10 | Enhet omodifierad |
| Challenge passed (per st) | 0.08 | Anti-spoofing |
| Reference match | 0.07 | Korrekt skick |
**Vikterna och trösklarna ägs av kunskapspaketet**, inte av AVO-koden.
### Konsekvensen
Ibland räcker 4 frames och 2 challenges. Ibland krävs 40 frames och 5 challenges.
Systemet vet när det är klart — Zoomern vet det aldrig förrän det är grönt.
### Sufficiency kan misslyckas
Om systemet inte når tröskeln inom:
- Max scan-tid (konfigurerbart, default 120s)
- Max challenges (default 5)
- Max frames (default 600)
`decision: insufficient_evidence` → eskalera till manuell granskning.
**Aldrig:** tvinga fram ett svagt godkännande för att undvika eskalering.
```json
{
"claim_id": "clm_4b2e9d7a",
"decision": "insufficient_evidence",
"trust_achieved": 0.71,
"trust_threshold": 0.92,
"limiting_factor": "liveness_score_too_low",
"escalation": "manual_review",
"photographer_directive": null
}
```
---
## Evidence Bundle — schemat
Det fullständiga, signerade paketet som claim:en refererar till:
```json
{
"bundle_id": "evb_7d2f1a9e",
"claim_id": "clm_9f3a8c1b",
"created_at": "2026-06-17T12:22:04Z",
"liveness": {
"score": 97,
"signals": {
"gyro_natural": true,
"parallax_verified": true,
"focus_changes": 4,
"gps_match": true,
"app_attest": true,
"stream_continuous": true,
"challenges_issued": 3,
"challenges_passed": 3
}
},
"evidence_elements": [
{
"element_id": "e1",
"type": "visual_frame",
"frame_index": 142,
"timestamp_ms": 3820,
"confidence": 0.97,
"weight": 0.15,
"contribution": 0.1455,
"hash": "sha256:f1a2b3..."
},
{
"element_id": "e2",
"type": "ocr_extraction",
"frame_index": 198,
"timestamp_ms": 5180,
"confidence": 0.94,
"weight": 0.25,
"contribution": 0.2350,
"extracted_value": "SE-4821-9938-01",
"hash": "sha256:c4d5e6..."
},
{
"element_id": "e3",
"type": "challenge_response",
"challenge_issued_at_ms": 6200,
"challenge_directive": "Gå ett steg bakåt",
"challenge_class": "liveness",
"response_detected_at_ms": 8100,
"confidence": 0.96,
"weight": 0.08,
"contribution": 0.0768
}
],
"trust_accumulation": [
{ "after_element": "e1", "trust_so_far": 0.1455 },
{ "after_element": "e2", "trust_so_far": 0.3805 },
{ "after_element": "e3", "trust_so_far": 0.4573 }
],
"bundle_hash": "sha256:a3f8b2...",
"signature": "ed25519:9f3a...",
"immutable": true
}
```
`trust_accumulation` loggar hur förtroendet byggdes upp steg för steg.
Vid återgranskning kan man se exakt varför beslutet fattades — och vid vilken evidens tröskeln passerades.
---
## Challenge-direktivets vokabulär
**Designkrav:** Zoomern kan aldrig avgöra om ett direktiv är guidning eller liveness-challenge.
```
"Flytta kameran 30 cm åt vänster." ← kan vara guidning (framing_ok)
eller challenge (liveness, kräver parallax)
"Höj kameran lite." ← kan vara guidning (angle_ok)
eller challenge (liveness, kräver rörelse)
"Visa objektets nedre del." ← kan vara guidning (coverage)
eller challenge (liveness, kräver ny vinkel)
```
Samma fras, samma röst, samma UI. Challenge-klassen är intern — aldrig exponerad.
```json
{
"directive": "Gå ett steg bakåt",
"directive_class": "liveness_challenge", INTERN, visas aldrig för Zoomer
"expected_signal": "zoom_out + parallax_change",
"timeout_ms": 8000
}
```
---
## Korrektion av en verified_claim
En verifierad utsaga är immutable. Korrektion sker som ny claim som refererar bakåt:
```json
{
"claim_id": "clm_4c7f2b8d",
"claim_type": "correction",
"corrects_claim_id": "clm_9f3a8c1b",
"correction_reason": "Manuell granskning: serienummer OCR-fel, korrekt värde SE-4821-9938-02",
"corrected_by": "reviewer_human_012",
"timestamp": "2026-06-17T15:33:11Z",
"signature": "ed25519:7b2c...",
"immutable": true
}
```
Originalclaim:en finns kvar. Granskningskedjan är komplett.
---
## Kunskapspaketet äger tröskeln
```json
{
"object": "lifebuoy",
"claim_type": "existence_and_condition",
"trust_threshold": 0.92,
"evidence_weights": {
"visual_frame": 0.15,
"ocr_serial": 0.25,
"liveness_score_component": 0.20,
"gps_match": 0.15,
"app_attest": 0.10,
"challenge_response": 0.08,
"reference_match": 0.07
},
"min_liveness_score": 80,
"max_scan_seconds": 120,
"compliance": "maritime_safety"
}
```
```json
{
"object": "passport",
"claim_type": "identity_document_authenticity",
"trust_threshold": 0.97,
"evidence_weights": {
"mrz_validation": 0.30,
"photo_match": 0.25,
"tamper_detection": 0.20,
"liveness_score_component": 0.15,
"challenge_response": 0.10
},
"min_liveness_score": 95,
"max_scan_seconds": 60,
"compliance": "regulated"
}
```
Passet kräver 97% förtroende och minst 95 i liveness.
Livbojen kräver 92% och minst 80 i liveness.
Samma motor — olika parametrar. Aldrig hårdkodad logik.
---
## Persistens och juridisk hållbarhet
| Krav | Implementation |
|------|---------------|
| Immutability | Append-only storage (S3 Object Lock / DynamoDB Streams) |
| Signering | Ed25519 per claim och per bundle |
| Återgranskningsbarhet | `trust_accumulation`-log i varje bundle |
| Temporal binding | Timestamp signerat med bundle-hash — kan inte backdateras |
| Separation | Bundle sparas separat från claim — kan granskas oberoende |
**Samma mönster som ledger:** en post ändras aldrig. Korrektion = ny post med bakåtreferens.
---
## Vad AVO v2-kontraktet fortfarande styr
Evidence Graph och Verified Claim är ett lager *ovanpå* AVO v2 — inte en ersättning.
```
Smart Scan (insamling)
AVO Fas A — Liveguidning (on-device, per frame)
AVO Fas B — Slutanalys (server, per exponering)
Sufficiency-funktion (ackumulerar trust mot claim-tröskel)
Evidence Bundle (signerat paket)
Verified Claim (persisterad, immutable utsaga)
QuickSum (summary till uppdragsgivare)
```
---
## Öppna frågor
1. **Vem äger kunskapspaketen?** quiXzoom sätter defaults — kan uppdragsgivare överskriva? Sätt ett golv.
2. **Juridisk beviskraft** — ed25519-signatur + immutable storage: räcker det i EU-rätt för försäkringsärenden? Behöver vi en ackrediterad tidsstämpel (RFC 3161)?
3. **RFC 3161 trusted timestamp** — binds claim-hashen till en extern tidsstämpelserver ger oberoende temporalt bevis. Troligtvis nödvändigt för regulated-klassen.
4. **Liveness-score för offline** — App Attest kräver nät. Offline-scanning ger lägre liveness-score. Accepteras av knowledge_pack?
5. **Granskningsgränssnitt** — hur ser det ut när en försäkringshandläggare öppnar ett bundle 6 månader senare? UI behöver designas för människa, inte för AVO.