{"revision":"0ed62d2c742f8e20","language":"en","entry":{"id":"proofs","group":"guides","kind":"guide","title":"Verify a card independently","summary":"Check a card’s signed versions, its place in the log’s index and the witnesses behind the log.","sections":[{"kind":"paragraph","text":"A lookup or summary helps you find the card. Fetch the full card to get its proof. The normal card page and the widget already perform these checks in the reader’s browser. An integration that displays its own verified verdict needs to check the proof itself."},{"kind":"paragraph","text":"Read `format` in GET /v1/log before choosing the checks. The live log, log.veil.rip/staging-1, is in format 2, and a format 2 log also names the Veil organisation in `staffOrg`. Format 1 is frozen on log.veil.rip/test-1: it stays readable and takes no new versions."},{"kind":"heading","text":"Format 2: the index path","anchor":"format-2"},{"kind":"paragraph","text":"A format 2 card brings what these checks need: `proof` for the current version, `index` for the card’s index bundle, `authority` for the versions that change who can sign, and `current` with the current label, body and signature. The work does not grow with the length of the card’s history."},{"kind":"steps","items":["Check the checkpoint in `index.checkpoint.note`: the log signature, the witness quorum from your trusted policy and its freshness. `proof` and `index` refer to that same checkpoint.","Verify `index.entry`, the checkpoint’s last index entry, with its inclusion proof. Check `index.value` against that entry’s root with `index.proof`, then apply the entries of `index.tail` that concern this card.","Check each item of `authority` from its log entry alone: its inclusion at position `i`, its label hash against the `c` that `index.value.auth` gives, and its signature by a key of the authority before it. Version 1 gives the card ID. Follow `authorityNext` until it is null.","Check `current`: its label hash is the one in `index.value.latest`, a key of the authority in force signed it, the state shown has the root in its label, and the keys match the authority.","For an organisation, check the member that `label.signer` names in each authority version and in the current one. That member held the role and signed with an active key of their own card at that log position.","Check the staff statements in `index.statements`. The first is `index.value.standing`, each `prev` is the `i` of the next, and each one is signed by a member of the Veil organisation whose role allows its action. Read more pages from `statementsNext` until it is null.","A closed card answers 410 with `closed`. Show it as closed only when that closing entry verifies."]},{"kind":"code","language":"text","code":"veil-rubrica:identity-version:v2\n<canonical label>\n\nveil-rubrica:statement-entry:v2\n<canonical entry without signature>\n\nveil-rubrica:close-entry:v2\n<canonical entry without signature>"},{"kind":"paragraph","text":"Keep the token, the ID, the last verified version and its authority. On the next visit, reject an index that points to an older version or that changes authority versions you already verified. A full check reads every version from GET /v1/identities/{n}/versions, starting at version 1, and verifies each body, signature and operation without the index."},{"kind":"paragraph","text":"A composite signature requires both Ed25519 and ML-DSA-65 to verify. Check freshness using enough valid witness signatures to satisfy the quorum. A stale quorum can prove history while leaving the current state uncertain."},{"kind":"heading","text":"Format 1 on test-1","anchor":"format-1"},{"kind":"paragraph","text":"These steps apply only to a card served by a log in format 1, today the frozen log.veil.rip/test-1. Reject a format 1 card when GET /v1/log says format 2."},{"kind":"steps","items":["Verify the record signature over the identity-version prefix followed by the exact proof.record bytes.","Recompute SHA-256 over salt and record. It must equal proof.commitment. For version 1 it also determines the card ID and fingerprint.","Check that proof.entry commits to this card’s token and commitment. Recompute the RFC 6962 leaf hash and follow the inclusion proof to the checkpoint root.","Check the log signature against its expected key and require the witness quorum from your trusted policy. Count each witness key once.","Verify the version chain, including prev, signing-key authority and every commitment back to version 1. Follow pagination cursors until the chain is complete.","Keep the last accepted checkpoint and check consistency before accepting a newer one. Reject a rollback or a conflicting root at the same size."]},{"kind":"code","language":"text","code":"veil-rubrica:identity-version:v1\n"},{"kind":"heading","text":"Read log resources"},{"kind":"paragraph","text":"GET /v1/log identifies the log used by this directory. Log resources live under that origin’s HTTPS URL. The full vkey string also contains a key ID and public key data, so it is not a page address."},{"kind":"links","ids":["log-state","log-checkpoint","log-policy","log-witnesses","log-hash-tiles","log-entry-tiles"]}],"related":["versions","version-proof","history","authority","statements"],"errors":[],"pageLinks":[{"anchor":"format-2","label":"Format 2"},{"anchor":"format-1","label":"Format 1 on test-1"}],"documentationUrl":"https://veil.rip/docs/proofs"}}