What has changed

All notable changes. Follows Keep a Changelog and Semantic Versioning.

All notable changes to this project will be documented in this file. The format is based on Keep a Changelog and this project adheres to Semantic Versioning.

[Unreleased]

Added

  • infra: HAProxy (port 8080) + Caddy reverse proxy (ports 80/443) serving all three Astro static apps via systemd services (`verbitas-landing`, `verbitas-web`, `verbitas-docs`) on ports 4321–4323; Cloudflare DNS A records for verbitas.io, www, v, verify, console, and docs subdomains all set to proxied mode pointing at 195.201.225.182
  • `apps/docs`: Complete developer documentation site — 33 new pages: Introduction, Quickstart, Concepts (Provenance & C2PA, Watermarking, Verification States, Recipes), API Reference (Authentication, Sign, Verify, Lookup, Recipes, Audit Log, Errors), SDK Guides (Python, TypeScript, Go), MCP Server (Overview, Installation, Tools Reference, Security), Guides (First Sign, EU AI Act, BYOK KMS, Webhooks, Custom Recipes, Enterprise Onboarding), Reference (Verification States, Meter Kinds, Recipe Schema, OpenAPI), and Compliance (Positioning, GDPR, Sub-Processors); Starlight dark editorial theme with Verbitas OKLCH palette and Instrument Sans / JetBrains Mono typography; `src/content/config.ts` with Starlight docs collection schema; `@astrojs/[email protected]` pinned for Astro 4 compatibility; build passes with 57 pages
  • `apps/landing` complete marketing site: 38 pages covering homepage, pricing, verify, developer/enterprise/AI-company/newsroom/compliance audience pages, product pages (API, SDK, MCP, watermark, C2PA), blog hub with 5 sample posts, changelog page, and programmatic SEO routes for industries, use cases, comparisons, and frameworks — built on Astro 4 + Tailwind + React with the Verbitas dark editorial navy design system (Cormorant Garamond / Instrument Sans / JetBrains Mono, OKLCH palette), sitemap generation at `sitemap-index.xml`, and all copy/disclaimer rules from `COMPLIANCE-POSITIONING.md` enforced throughout
  • `apps/web` complete redesign: dark editorial navy design system (Cormorant Garamond / Instrument Sans / JetBrains Mono), public provenance verifier with multi-signal breakdown, manifest permalink pages (`/m/[manifest_id]`), widget embed documentation, security policy page, and full admin console (`/console/*`) with dashboard, asset browser, audit log, billing, settings (API keys, webhooks, KMS, team), recipe editor, and tenant management gate — all building to static output with `pnpm build`

Fixed

  • G-E3: charge.dispute.created webhook now freezes tenant status in DB (was only logging SECURITY_ALERT)

Changed

  • Arbitrum Tier 2 anchoring: removed AWS KMS dependency; `arbitrum.py` now signs EIP-1559 transactions with `VB_ARB_PRIVATE_KEY` (Ethereum private key) from Doppler/Vault — no AWS account required
  • `web3` dependency bumped to `>=7.0.0` in `apps/worker/pyproject.toml`
  • G-D5 gap matrix entry updated: AWS KMS anchor key replica replaced by `VB_ARB_PRIVATE_KEY` Doppler secret pattern

Added

  • infra: HAProxy (port 8080) + Caddy reverse proxy (ports 80/443) serving all three Astro static apps via systemd services (`verbitas-landing`, `verbitas-web`, `verbitas-docs`) on ports 4321–4323; Cloudflare DNS A records for verbitas.io, www, v, verify, console, and docs subdomains all set to proxied mode pointing at 195.201.225.182
  • `infra/contracts/DEPLOY.md` — LogRoot.sol deployment guide for Arbitrum One (Foundry-based, no AWS)
  • 7 security tests for Arbitrum anchoring in `test_arbitrum_no_aws.py`: key-not-logged assertion, 32-byte size guards, graceful skip when unconfigured, 0x prefix normalisation, empty-hash guard

Added

  • infra: HAProxy (port 8080) + Caddy reverse proxy (ports 80/443) serving all three Astro static apps via systemd services (`verbitas-landing`, `verbitas-web`, `verbitas-docs`) on ports 4321–4323; Cloudflare DNS A records for verbitas.io, www, v, verify, console, and docs subdomains all set to proxied mode pointing at 195.201.225.182
  • OpenTimestamps Tier 1 anchoring activated — real OTS calendar submissions via opentimestamps-client
  • TrustMark, AudioSeal, VideoSeal real model packages confirmed installed and importable (all MIT licensed; CPU mode)
  • Watermark model smoke tests (`test_watermark_models.py`) confirming library imports for all four packages
  • docs/product/marketing/LANDING-PAGE-BRIEF.md — landing page design brief with 8 sections, copy for all plans, trust strip, use case tiles, and analytics instrumentation
  • docs/product/marketing/PAGES-BRIEF.md — design briefs for 20 sub-pages (7 product, 4 use case, 5 compliance/sector, 3 demo, 1 pricing, 1 trust)
  • docs/product/marketing/EU-AI-ACT-REPORT.md — Article 50 technical whitepaper: C2PA + watermark coverage map, case study, appendix, and required disclaimers
  • docs/product/marketing/TECH-STACK-REPORT.md — security badge copy, full tech stack table, data architecture, and 4 SEO blog clusters (32 posts total)
  • docs/product/marketing/TESTING-SCENARIOS.md — 7 demo video scripts with narrator lines + 7 matching 400-500 word how-to guides with code
  • docs/IMPLEMENTATION-STATUS.md — full audit of all 21 phases: test results (412/412 passing), layer-by-layer status, blockchain anchoring detail, security QA checklist, and pre-launch blockers

Changed

  • README.md — replaced implementation-plan index with clean professional README (quick start, architecture table, services, SDKs, dev commands)
  • CLAUDE.md — updated cross-session context paths from `implementation-plan/` to `docs/` after reorganization
  • Reorganized root-level MD files into `docs/`: 00-system-context.md, autonomous-agent-guide.md, MODEL-GUIDE.md, PROJECT_PLAN.md, RISK-GATES.md
  • docs/product/ARCHITECTURE.md — full system architecture with Mermaid diagrams, component table, security boundaries, and infrastructure topology
  • docs/product/IO-FLOWS.md — customer personas, 10 use cases with code examples and API contracts
  • docs/product/PLATFORM-AS-BACKEND.md — integration guide for product families (CMS, mobile, wire service, etc.) with 6 integration patterns
  • docs/product/AI-AGENT-DESIGN.md — Workflow Designer LLM agent design document with decision logic, example runs, and MCP integration guide
  • Signer: HashiCorp Vault Transit KMS adapter — interim backend for pre-AWS Hetzner deployment, selected via `VB_KMS_BACKEND=vault_transit`; preserves existing AWS KMS path as the default
  • Signer: KMS backend factory at `apps/signer/src/verbitas_signer/kms/factory.py` — fail-closed backend selector; `kms.py` split into a package alongside `vault_transit.py`
  • Signer: Vault Transit setup runbook at `docs/runbooks/vault-transit-setup.md` (Doppler wiring, token rotation, audit log, AWS migration steps)
  • Signer: 20 security tests for `VaultTransitClient` covering T1–T10 (TLS-only, token redaction, payload bounds, fail-closed on Vault errors, signature prefix stripping, health degradation)

Security

  • Signer: Vault token never logged or echoed; `_redact()` strips `hvs.*` and `s.*` patterns from every Vault error string before it surfaces
  • Signer: `VaultTransitClient` refuses non-`https://` URLs and has no `verify=False` switch — TLS verification is mandatory
  • Signer: payload size hard-capped at 48 bytes (SHA-384 max) before any HTTP call to Vault, matching the existing AWS KMS validator
  • Phase 21: BetterStack status page config — 5 components, alert-to-incident wiring
  • Phase 21: Disaster recovery runbook — Postgres streaming replication, backup/restore, KMS failover
  • Phase 21: Cross-region object storage replication script (Hetzner → Wasabi)
  • Phase 21: KMS multi-region failover runbook + object storage replication failure runbook
  • Phase 21: Backup drill automation script targeting ≤30 min RTO
  • Phase 21: k6 load test scripts — image sign/verify/lookup, audio, video, MCP
  • Phase 21: Launch checklist, enterprise questionnaire, support runbook
  • Phase 21 Task 8 (Opus): Game-day scenarios — 6 failure modes (signer, Postgres, Cloudflare, KMS, Stripe, verifier abuse) with detection, recovery, RTO/RPO, gap priority matrix (5 P0, 8 P1), Gate F checklist, and run log; 9-assert validator test in `apps/api/tests/test_gameday_doc.py`
  • Phase 20: OpenTelemetry tracing for API + worker with PII redaction processor (T25 compliance)
  • Phase 20: Sentry SDK integration with PII scrubbing before_send processor
  • Phase 20: Prometheus metrics — 7 custom metrics, /metrics endpoint, cardinality guard test
  • Phase 20: Grafana dashboard configs — overview, sign-latency, worker-health, anchor-pipeline
  • Phase 20: Prometheus alert rules + SECURITY-RUNBOOKS.md (5 runbooks: API, KMS, billing, worker, anchor)
  • Phase 20: SAST/dependency scanning CI jobs (bandit, pip-audit, npm audit)
  • Phase 20: SBOM generation script (CycloneDX), cosign release signing, make sbom target
  • Phase 20: security.txt at apps/web/public/.well-known/security.txt, responsible disclosure page
  • Phase 20: Cloudflare WAF Terraform — rate-limit 100 req/s/IP + ASN block rules (simulate mode)
  • Phase 20: Upload scanning, PNG decompression bomb defense (T11), 11 negative tests
  • Phase 19: Python SDK (`packages/sdk-py`) — Client, typed errors, Pydantic models, webhook verifier, 11 tests
  • Phase 19: TypeScript SDK (`packages/sdk-ts`) — VerbitasClient, browser-safe subpackage, typed errors, 8 tests
  • Phase 19: Go SDK (`packages/sdk-go`) — Client, stdlib-only, Sign/Verify/VerifyWebhook, 6 tests
  • Phase 19: SDK compatibility matrix at `docs/sdk-compatibility.md`
  • Phase 18: verbitas-mcp npm package — 8 MCP tools for AI agent access
  • Phase 18: vb_sign, vb_verify, vb_lookup — core provenance tools
  • Phase 18: vb_recipes_list, vb_recipe_create (admin scope) — recipe management
  • Phase 18: vb_usage, vb_report_generate, vb_api_key_info — account tools
  • Phase 18: Safety rules — URL rejection, 100MB cap, MIME allow-list, API key never echoed
  • Phase 18: verbitas-mcp init CLI command for Claude Desktop/Cursor setup
  • Phase 18: Prompt injection hardening — structured responses, injected text never executed
  • Phase 17: Astro Starlight docs site (`apps/docs`) at docs.verbitas.io
  • Phase 17: Quickstart pages — curl, Python, TypeScript, Go, MCP
  • Phase 17: API reference pages — sign, verify, lookup, recipes, anchor, webhooks
  • Phase 17: Concepts pages — C2PA, watermarking, soft-binding, anchoring, verification states, Article 50
  • Phase 17: Recipe library — all 10 preset recipes with YAML and sign examples
  • Phase 17: Trust section — trust policy, compliance positioning, sub-processors
  • Phase 17: Pagefind offline search (built in to Starlight)
  • Phase 17: Versioning shell at /v0.1/
  • Phase 16: Astro 4 static landing site (`apps/landing`) with Tailwind + sitemap integration, `robots.txt`, `favicon.svg`
  • Phase 16: 61-page programmatic SEO — compliance×40 (10 countries × 4 modalities), framework×5, compare×5, industry×10
  • Phase 16: Hero, TrustStrip, HowItWorks, PricingCards, Footer components
  • Phase 16: JSON-LD Organization schema, OpenGraph, per-page canonical, CSP header (no unsafe-inline for scripts)
  • Phase 16: Plausible privacy-first analytics with `key_request`/`docs_click`/`pricing_click` event goals
  • Phase 16: Python compliance language linter CI gate (`scripts/lint_compliance_language.py`) — scans `apps/landing/src` and `apps/web/src`
  • Phase 15: Stripe metered billing — 7-plan config (`developer`, `growth`, `enterprise_saas`, `enterprise_byok`, `self_hosted`, `compliance_newsroom`, `agent_native`), 10 meter kinds, env-backed price + meter IDs, `BILLING_TO_TENANT_PLAN` projection
  • Phase 15: Stripe client (`billing/stripe_client.py`) — idempotent `create_customer`, `attach_payment_method`, `create_subscription` (billing*thresholds spending cap), `report_usage` (action=set), with hard `sk_test*\*`+`VB_ENV=prod` interlock
  • Phase 15: `POST /v1/checkout` — Stripe Checkout subscription session for the human sign-up path; 503 when per-plan price ID is unconfigured
  • Phase 15: `POST /v1/onboard` (Opus) — agent path with required `Idempotency-Key` (≥8 chars), required non-browser `User-Agent`, spending cap, fresh API key minted with `scope=sign`, dev/stub fallback when Stripe creds absent
  • Phase 15: `POST /v1/webhooks/stripe` (Opus) — raw-body signature verify, 7-day Redis replay-id cache (`stripe_evt:{event.id}`), unknown-event pass-through (200), `charge.dispute.created` → `SECURITY_ALERT.billing.webhook.dispute_created` log line for tenant freeze
  • Phase 15: usage reporter Celery tasks — `report_daily_usage` (00:05 UTC daily, idempotency key `meter-{tenant}-{date}-{op}`) + `reconcile_usage` (hourly, drift alert > 0.5%) routed to the anchor queue
  • Phase 15: monthly ops limit enforcement (`auth/ratelimit.check_monthly_ops_limit`) — HTTP 402 with `code=monthly_limit_exceeded` + `upsell_url` on the 1,001st free-plan op; per-tenant Redis counter `monthly_ops:{tenant}:{YYYY-MM}` with 33-day TTL; fail-open on Redis outage
  • Phase 15: 23 new tests (`apps/api/tests/test_billing.py`) — plan-config integrity, Stripe key prod interlock, all `/v1/onboard` header guards, webhook signature/replay/unknown/dispute paths, monthly cap 402, idempotency-key format
  • Phase 14: Astro 4 + React + Tailwind web app scaffold (`apps/web`) — `astro.config.mjs`, `tsconfig.json`, `tailwind.config.mjs`
  • Phase 14: Verification state types and `STATUS_CONFIG` trust-language constants derived from `docs/VERIFICATION-STATES.md` (16-code closed enum); API client for `/v1/verify` and `/v1/lookup`
  • Phase 14: Public verifier page (`v.verbitas.io`) — drag-and-drop zone, manifest + watermark fallback, status badge, proves/does-not-prove panel, signals grid, blockchain anchors panel, developer JSON toggle
  • Phase 14: Admin console — sidebar navigation layout, overview metrics page, 7 section stubs (API Keys, Recipes, Usage, Verification logs, Anchors, Review queue, Billing)
  • Phase 14: Magic-link login stub (`/console/login`) — single-use 15-min TTL design, rate-limit comment; Resend integration deferred to Phase 15
  • Phase 14: i18n stubs for en / de / fr (machine-translated de/fr; all 16 status codes + verifier + console strings in en base)
  • Phase 14: Compliance language linter (`src/lib/complianceLinter.ts`) — catches 24 banned phrases (tamper-proof, authentic, real or fake, Article 50 compliant, etc.); 15 unit tests all pass
  • Phase 14: Embeddable iframe widget (`/widget/[id]`) with postMessage origin allow-list guard — unknown origins silently rejected, no error thrown to parent
  • Phase 13: AnchorBatch DB model with OTS/Arb/BYOC status enums (OtsStatus, ArbStatus, ByocStatus), batch_hour key, manifest_ids JSONB, and composite arb-pending index; Alembic migration b7e3f1a2d9c5
  • Phase 13: Merkle tree — domain-separated leaves SHA256("verbitas-anchor-v1" || digest), binary tree, inclusion proof, verify_inclusion; 32-byte hard assert on root before any on-chain submission
  • Phase 13: Tier 1 OpenTimestamps — multi-calendar submission (4 default calendars), Bitcoin timestamp upgrade polling; stub mode when library unavailable
  • Phase 13: Tier 2 Arbitrum One — EIP-1559 KMS-sign placeholder (pending AWS provisioning), L2 confirmation polling (anchored_l2 → anchored_strong at 256 blocks); LogRoot.sol minimal on-chain event emitter
  • Phase 13: Tier 3 BYOC — per-tenant chain, 32-byte hard assert, per-tenant daily cap (Redis), Doppler-backed RPC URL secret
  • Phase 13: Hourly batch builder Celery task, OTS upgrade beat task, Arbitrum poll beat task (queue=anchor)
  • Phase 13: GET /v1/anchor/{batch_id} and GET /v1/manifest/{id}/anchor API routes (auth-gated, stub responses until DB available)
  • Phase 13: 20 new tests — Merkle collision/proof/domain-separation, 32-byte hard asserts (Arb/BYOC), OTS/Arb/BYOC stub modes, daily cap enforcement, API 401/OpenAPI presence
  • Phase 12: Soft-binding DB models (SoftBinding, LookupEvent) — exact/fuzzy/semantic index with hash_int Hamming-distance column, binding_class enum, BLAKE3/pHash/dHash/aHash/Chromaprint algorithm set
  • Phase 12: Fingerprint library (worker) — pHash/dHash/aHash (image), BLAKE3 (all), Chromaprint (audio, optional), keyframe pHash (video); `hamming_distance` utility; size-limit guards
  • Phase 12: Lookup engine — exact → fuzzy → semantic pipeline; confidence capping (fuzzy ≤ 0.85, semantic ≤ 0.75); ambiguity threshold Δ0.02 → `multiple_candidates`; graceful NOT_FOUND when DB unavailable
  • Phase 12: Manual review queue stub — `enqueue_review()` structured-log audit trail for ambiguous matches; Phase 13 DB table planned
  • Phase 12: `index_asset` Celery task — fingerprint dispatch (image/audio/video), partial-failure logging, BLAKE3 + perceptual hashes
  • Phase 12: POST /v1/lookup API route — accepts `(algorithm, binding_hash)` or `(asset_b64, media_type)`; exact and fuzzy lookup via session dependency; auth-gated (read scope)
  • Phase 12: `LOOKUP_PLAN_LIMITS` rate-limit constants (free 1 rps, growth 50 rps) in `ratelimit.py`; confidence calibration stub at `artifacts/calibration/`
  • Phase 12: Alembic migration `a4f2c9d1e8b3` — drops/recreates `soft_bindings` and `lookup_events` with Phase 12 schema; adds `binding_class_enum` Postgres type; adds `ix_soft_binding_hash_int` and `ix_soft_binding_manifest` indexes
  • Phase 12: 77 new tests (15 API + 13 worker) — fingerprint, Hamming, lookup pipeline, candidate ranking, API endpoint, review queue
  • Phase 11: Recipe engine — JSON Schema (Draft 2020-12) + Pydantic v2 models with closed enums for media_type, watermark.engine, soft_binding.methods, anchoring.methods, ambiguous_match_behavior, billing.meter, c2pa.assertions, c2pa.kms_mode, and `step.kind` (10 values, frozen forever — adding a kind requires an RFC)
  • Phase 11: Validator with cross-field rules (C2PA mandatory, media_type↔engine compatibility, enterprise→BYOK, signer_pinning admin approval, billing.meter↔media_type, id↔version match) and custom-recipe inheritance rules (`extends: @`, no kms_mode downgrade)
  • Phase 11: 10 frozen preset YAMLs — `image-genai-v1`, `image-editorial-v1`, `image-deepfake-v1`, `image-legal-evidence-v1`, `audio-genai-v1`, `audio-voiceclone-v1`, `video-genai-v1`, `video-advertising-v1`, `text-genai-disclosure-v1`, `enterprise-strict-v1`
  • Phase 11: Recipe executor — ordered layer dispatch (build → watermark → soft-binding → anchor → embed → finalize → meter), per-layer enabled/skip, and fail-closed rejection of unknown step kinds (no fallthrough)
  • Phase 11: Loader with `load_preset` / `load_all_presets` (cached) + immutability guard (`mark_used` / `is_used` / `check_immutability`); recipes locked after first use, new versions only
  • Phase 11: GET /v1/recipes (public catalog), GET /v1/recipes/{id} (public), POST /v1/recipes (auth-gated tenant custom recipes)
  • Phase 11: 32 tests covering preset round-trip, executor pipeline, every closed-enum negative case, immutability + version-bump, custom-recipe inheritance rules, and StepKind dispatch coverage guard
  • Phase 10: VideoSeal video watermark wrapper — real VideoSeal v1.0 (256-bit, full 13-byte Verbitas payload) + frame-LSB stub fallback with lossless H.264 round-trip
  • Phase 10: VFR detection, HEVC 10-bit/AV1/VP9/Theora rejection, 5 GiB size cap; bundled attenuation config injection for PyPI package compatibility
  • Phase 10: `embed_videoseal` and `detect_videoseal` Celery tasks (`video` queue, 15 min / 5 min time limits, `worker_prefetch_multiplier=1`)
  • Phase 10: Video robustness benchmark suite (`scripts/bench_video_watermark.py`)
  • Phase 10: Benchmark artifact at `artifacts/bench/video-*.md`; 20 new tests covering roundtrip, oversized/codec/HDR rejection, VFR, stub internals, Celery tasks
  • Phase 09: AudioSeal audio watermark wrapper — real AudioSeal 16-bit + LSB stub fallback (scale-fixed, PCM-16 lossless)
  • Phase 09: Sample-rate normalization (→ 16 kHz internal), multichannel downmix/restore, 30 s streaming windows
  • Phase 09: `embed_audioseal` and `detect_audioseal` Celery tasks (`audio` queue, 5 min / 2 min time limits)
  • Phase 09: Audio robustness benchmark — MP3 128k (100%), noise SNR 20 dB (100%), crop 20% (100%), speed 5% (0% — known limitation)
  • Phase 09: Benchmark artifact at `artifacts/bench/audio-*.md`; 11 new tests including stub HMAC round-trip, stereo, oversized/SR rejection
  • Phase 08: TrustMark image watermark wrapper — real TrustMark (CPU+GPU) with LSB stub fallback; cached model instance
  • Phase 08: WatermarkPayload v1 — 4-bit version | 64-bit watermark_id | 32-bit tenant HMAC-SHA256, 100 bits total (TrustMark B capacity)
  • Phase 08: `embed_trustmark` and `detect_trustmark` Celery tasks — magic-byte validated, HMAC-verified
  • Phase 08: Robustness benchmark suite (`scripts/bench_image_watermark.py`) — JPEG, resize, crop, screenshot; PSNR metric; artifact output
  • Phase 08: Benchmark artifact at `artifacts/bench/image-*.md`
  • Phase 08: 18 tests — payload round-trip, HMAC tamper, oversized image rejection, embed/detect round-trip
  • Phase 07: Magic-byte file detector — ftyp box for AVIF/HEIC, extension/content mismatch guard
  • Phase 07: C2PA 2.4 manifest builder — canonical CBOR claim, SHA-256 digest, closed-set assertions
  • Phase 07: RemoteSigner — HMAC-signed canonical-body dispatch to signer microservice (no digest leakage)
  • Phase 07: Embedder — JPEG APP11, PNG caBX inline; sidecar fallback for AVIF/HEIC/video/audio
  • Phase 07: OCSP stapling — Redis 6h cache, prod fail-degraded (`ocsp_unavailable`), dev warn-and-allow
  • Phase 07: Trust list — seed JSON, fingerprint check, explicit signer pinning, 24h refresh
  • Phase 07: Parser bounds — 32 MiB cap, CBOR depth ≤32, recursion-limit guard, hypothesis property test
  • Phase 07: Verifier — closed-enum status, tamper detection, multi-signal output (no `real`/`fake`/`authentic`)
  • Phase 07: Test corpus seed (JPEG, PNG, polyglot JPEG-with-ZIP) at `tests/c2pa-corpus/`
  • Phase 07: Worker deps — `c2pa-python` 0.32.6, `cbor2`, `Pillow`, `cryptography`; API deps — `cbor2`, `python-magic`
  • Phase 06: KMS signer microservice — `dev_local` and `kms` modes (kms tested via moto)
  • Phase 06: Canonical payload validator — digest length, tenant/recipe allow-list, CBOR size band
  • Phase 06: Internal HMAC-SHA256 auth — 30s timestamp window, constant-time compare
  • Phase 06: Mode interlock — dev marker in `kms` refused + `SECURITY_ALERT` log; non-Tailscale IP in `dev_local` refused
  • Phase 06: `/sign`, `/pubkey`, `/health` endpoints; allow-list refresh every 60s
  • Phase 06: Non-root signer Dockerfile (port 8001)
  • Phase 06: IAM policy artifact (`artifacts/iam/signer-policy.json`)
  • Phase 06: ADR-0002 signer architecture decision
  • Phase 06: Signer security runbooks — Tailscale check, KMS region outage, HMAC clock skew, dev-marker-in-prod, pubkey cache load failure
  • Phase 05: Celery app — Redis broker/backend, 5 queues, acks_late, reject_on_worker_lost
  • Phase 05: Stub tasks — sign_image (60s), sign_audio (120s), sign_video (900s), submit_batch, sign_callback
  • Phase 05: GET /v1/jobs/{id} router (auth-gated), enqueue helper (no cross-service imports)
  • Phase 05: Worker Dockerfile (non-root), worker-video (pool=solo), flower (dev profile)
  • Phase 04: API token format vb*{env}*{32b32}, SHA-256 hashing, constant-time verify (hmac.compare_digest)
  • Phase 04: current_tenant FastAPI dependency, scope resolution, last_used_at update
  • Phase 04: slowapi rate limiter — plan-based (5/50/200/1000 rps), Redis-backed, fail-closed
  • Phase 04: POST /v1/keys, GET /v1/keys, DELETE /v1/keys/{id} with audit log
  • Phase 04: POST /v1/tenants, GET /v1/tenants (admin-only) with audit log
  • Phase 04: Auth wired into sign, verify, lookup stubs (require_scope); admin scope blocked via API
  • Phase 03: SQLAlchemy 2 async base with UUID7 PK + timezone-aware created_at / updated_at
  • Phase 03: 10 data models — tenant, api_key, recipe, manifest, soft_binding, audit_log, anchor_batch, usage_meter, lookup_event, verification_result
  • Phase 03: closed-set `VerificationStatus` enum (8 explainable states; never `real`/`fake`/`authentic`) and `BindingAlgorithm` enum covering manifest, trustmark, audioseal, videoseal, phash, audio_fp, video_fp, embedding
  • Phase 03: Alembic async setup + initial migration enabling pgcrypto, pg_trgm, uuid-ossp; covering index `(tenant_id, asset_sha256)` on manifests; composite index `(algorithm, binding_hash)` on soft_bindings
  • Phase 03: append-only audit_log DB trigger (UPDATE / DELETE raise) — RISK-GATES Gate E
  • Phase 03: async engine + sessionmaker + `get_db` FastAPI dependency
  • Phase 03: typed Redis wrapper with key/value, hash, list, stream helpers (JSON-serialised values)
  • Phase 03: S3-compatible client wrapper (path-style + SigV4) with bucket bootstrap, presign get/put, head_object — Hetzner Object Storage / MinIO compatible
  • Phase 03: seed script — demo `acme` tenant + 10 preset recipe placeholders (idempotent)
  • Phase 03: `/v1/health` now reports real DB + Redis + S3 latencies and per-dependency status
  • Phase 03: tests — model imports, enum closure, forbidden-value guards, audit-log trigger negative tests (UPDATE + DELETE), cross-tenant soft-binding isolation
  • Phase 03: SQLAlchemy 2 + asyncpg + alembic + redis[hiredis] + boto3 + aiobotocore + uuid_utils + python-ulid + testcontainers[postgres,redis,minio] dependencies
  • docs(specs): C2PA 2.4 specification PDFs, 37 CDDL schemas, implementation notes, and PDF-extracted markdown at docs/specs/c2pa/
  • Phase 02: FastAPI app factory with security headers, request-ID middleware, CORS
  • Phase 02: Settings (pydantic-settings v2, VB\_ prefix)
  • Phase 02: structlog JSON logging with dev console renderer
  • Phase 02: Route stubs — sign, verify, lookup, keys, tenants, webhooks (all 501)
  • Phase 02: /v1/health, /v1/version, /v1/recipes (live endpoints)
  • Phase 02: Multi-stage distroless Dockerfile for apps/api
  • Phase 02: Tests — health, request-ID echo, OpenAPI 3.1 validity
  • Phase 00: DNS A records for verbitas.io, api., www., v., docs. via Cloudflare API (proxy OFF for Let's Encrypt HTTP-01)
  • Phase 00: Tailscale SSH lockdown — port 22 restricted to Tailscale CGNAT 100.64.0.0/10
  • Phase 00: infra/secrets.example.env environment variable reference template

Added

  • infra: HAProxy (port 8080) + Caddy reverse proxy (ports 80/443) serving all three Astro static apps via systemd services (`verbitas-landing`, `verbitas-web`, `verbitas-docs`) on ports 4321–4323; Cloudflare DNS A records for verbitas.io, www, v, verify, console, and docs subdomains all set to proxied mode pointing at 195.201.225.182
  • Phase 00: UFW firewall rules (22/tcp SSH, 80/tcp HTTP, 443/tcp HTTPS)
  • Phase 00: fail2ban with 90-day bantime (7776000s), maxretry=3, findtime=600
  • Phase 00: Caddy placeholder config for verbitas.io, api., v., docs. subdomains
  • Phase 00: Docker CE 29, Doppler CLI 3.76, ffmpeg, python3-magic, AWS CLI v2 installed
  • Phase 00: infra/ directory skeleton (caddy/, cloud-init/runs/, docker/)

Changed

  • Phase 03: pyproject ruff/mypy now skip `**/alembic/versions/**` (auto-generated migration scripts)

Fixed

  • Phase 03: marked dev-only S3 secret_key default with `noqa: S105` so `ruff check .` passes from repo root (was a pre-existing CI failure)

Removed

Security

  • Phase 03: append-only audit_log DB trigger blocks UPDATE/DELETE at the database level (defence-in-depth; required by RISK-GATES.md Gate E)

[0.1.0] — TBD

Added

  • infra: HAProxy (port 8080) + Caddy reverse proxy (ports 80/443) serving all three Astro static apps via systemd services (`verbitas-landing`, `verbitas-web`, `verbitas-docs`) on ports 4321–4323; Cloudflare DNS A records for verbitas.io, www, v, verify, console, and docs subdomains all set to proxied mode pointing at 195.201.225.182
  • Phase 01: tooling installed — uv 0.11.12, pnpm 10.33.4, pre-commit 4.6.0, gitleaks 8.18.4
  • Phase 01: full monorepo directory layout (apps/, packages/, infra/, docs/, scripts/, .github/)
  • Phase 01: root pyproject.toml — uv workspace, ruff/black/mypy/pytest config
  • Phase 01: per-app pyproject.toml files for api, signer, worker, recipes, sdk-py
  • Phase 01: pnpm workspace, package.json files, tsconfig.json, pnpm-workspace.yaml
  • Phase 01: Makefile with all CLAUDE.md §3 targets (bootstrap, dev, test, lint, typecheck, fmt, db._, bench._, security.scan, sbom, release, verify.openapi, e2e, load.\*)
  • Phase 01: docker-compose.yml (postgres, redis, minio, mailhog, api, signer, worker, web, landing) and .env.example
  • Phase 01: .pre-commit-config.yaml (ruff, black, prettier, conventional-commits, gitleaks, pre-commit-hooks)
  • Phase 01: .pre-commit-config.yaml (ruff, black, prettier, conventional-commits, gitleaks, pre-commit-hooks)
  • Phase 01: GitHub Actions ci.yml (lint, typecheck, test, security, changelog-check) and release.yml (multi-arch image build + cosign)
  • Phase 01: CODEOWNERS covering all no-go paths from CLAUDE.md §4
  • Phase 01: ADR-0000 bootstrap decision record, LICENSE (BUSL 1.1 → Apache-2.0 2030-05-09), .editorconfig, .gitattributes (LFS for test fixtures), docs/runbooks/README.md