Evidence & Attestation Guide
Evidence Model
Section titled “Evidence Model”Every assessment finding produces an immutable evidence record:
EvidenceRecord { id: UUID requirement_id: "CRYPTO-01-R1" evidence_type: Auto | Semi | Doc | Test source: Scanner | LLM | Manual | CI | Questionnaire content: "Requirement CRYPTO-01-R1 assessed as pass (confidence: 92%)..." content_hash: "sha256:a1b2c3..." signature: (optional HMAC-SHA256) commit_sha: "abc12345" llm_provenance: { backend, model, prompt_version, confidence } status: Draft | Reviewed | Attested created_at: 2026-04-03T12:00:00Z}Immutability
Section titled “Immutability”Evidence records have no updated_at column. They are append-only by design:
- New scans create new evidence records
- Old records are never modified or deleted
- If a finding changes status, a new record is created — the old one persists
- This satisfies the CRA 10-year retention obligation (Article 23)
Content Hashing
Section titled “Content Hashing”Every evidence record is hashed with SHA-256:
content_hash = "sha256:" + hex(SHA256(content))This allows tamper detection: if the content is modified after creation, the hash won’t match.
Evidence Sources
Section titled “Evidence Sources”| Source | Created By | Evidence Type |
|---|---|---|
| Scanner | Static detector findings | Auto |
| LLM | AI-reviewed Semi findings | Semi |
| Manual | Human-uploaded documents via API | Doc, Test |
| CI | CI pipeline attestation | Auto |
| Questionnaire | Interview/questionnaire answers | Doc |
Attestation
Section titled “Attestation”HMAC-SHA256 Signing
Section titled “HMAC-SHA256 Signing”Every CI scan can produce a signed attestation envelope:
{ "version": "1.0.0", "subject": { "evidence_hash": "sha256:a1b2c3...", "commit_sha": "abc12345", "scan_id": "uuid", "product_name": "my-product", "findings_count": 152, "summary": { "pass": 55, "fail": 40, "needs_review": 57 } }, "identity": { "ci_provider": "github", "workflow": "CRA Compliance", "actor": "james", "repository": "org/repo", "oidc_issuer": "https://token.actions.githubusercontent.com" }, "signature": { "method": "hmac-sha256", "value": "hex...", "key_hint": "fleet_te..." }, "timestamp": "2026-04-03T12:00:00Z"}Verification
Section titled “Verification”// The signature is verified by recomputing HMAC over the subject JSONverify_hmac(&subject, &signature, api_key) -> boolUses timing-safe comparison (subtle::ConstantTimeEq) to prevent timing attacks.
CI Identity Detection
Section titled “CI Identity Detection”The attestation module auto-detects CI identity from environment variables:
| Provider | Detection | Fields Captured |
|---|---|---|
| GitHub Actions | GITHUB_ACTIONS | workflow, actor, repository, OIDC issuer |
| GitLab CI | GITLAB_CI | pipeline name, user, project path, server URL |
| Jenkins | JENKINS_URL | job name, build user |
| Bitbucket | BITBUCKET_PIPELINE_UUID | pipeline UUID, repo name |
| Azure DevOps | BUILD_BUILDID | (build ID) |
| CircleCI | CIRCLECI | (detected) |
| Drone | DRONE | (detected) |
| Woodpecker | CI_WOODPECKER | (detected) |
Evidence Lifecycle
Section titled “Evidence Lifecycle”Scanner/LLM creates finding │ ▼ EvidenceRecord (status: draft) │ ├─── Manual review ──▶ status: reviewed │ ├─── Attestation signing ──▶ status: attested │ └─── Stored permanently (append-only, 10-year retention)Manual Evidence Upload
Section titled “Manual Evidence Upload”For Doc and Test evidence types, upload via API:
curl -X POST /api/v1/assessment/products/{id}/evidence/upload \ -H "Authorization: Bearer $KEY" \ -d '{ "requirement_id": "VH-DISC-01-R1", "evidence_type": "doc", "content": "Disclosure policy published at...", "created_by": "james@crabnebula.dev" }'Retention Strategy
Section titled “Retention Strategy”| Storage | Retention | Purpose |
|---|---|---|
PostgreSQL evidence_records | Indefinite (append-only) | Primary evidence store |
| S3 artifacts | Indefinite | Document attachments, SBOM/CBOM files |
| CI artifacts | 10 years (configure per platform) | Backup evidence trail |
| Attestation envelopes | Indefinite | Cryptographic proof of scan provenance |