Skip to content

GeBüV anchoring pipeline: Merkle + OpenTimestamps + WORM provisioning (Säule 2)#3934

Merged
TaprootFreak merged 8 commits into
feature/storage-service-sketchfrom
feature/compliance-archive-anchoring
Jun 18, 2026
Merged

GeBüV anchoring pipeline: Merkle + OpenTimestamps + WORM provisioning (Säule 2)#3934
TaprootFreak merged 8 commits into
feature/storage-service-sketchfrom
feature/compliance-archive-anchoring

Conversation

@TaprootFreak

Copy link
Copy Markdown
Collaborator

Was

Säule 2 des GeBüV-Archivs: die Anchoring-Pipeline, die aus dem Storage (Säule 1, #3921) ein revisionssicheres Archiv macht. Aufbauend auf der StorageService-Abstraktion.

Gestapelt auf feature/storage-service-sketch (#3921). Base ist bewusst dieser Branch, damit der Diff nur Säule 2 zeigt; nach Merge von #3921 auf develop retargeten.

Komponenten

  • Merkle-Kern (anchoring/merkle.ts): SHA-256, buildMerkleRoot, merkleInclusionProof, verifyMerkleProof (parent = sha256(left||right), ungerader Knoten dupliziert).
  • OpenTimestamps (anchoring/opentimestamps.service.ts): stamp/upgrade/verify über die öffentlichen Calendars → Bitcoin. Nur der Hash verlässt das Haus.
  • ArchiveService: recordHash (idempotent, schützt geankerte Leaves), anchorPending (Tages-Merkle-Root → OTS-Stamp, transaktional), upgradeBatches (Bitcoin-Attestation), verifyDocument (Hash-Match + Inclusion-Proof + OTS).
  • DB: ArchiveBatch / ArchiveFile (IEntity/BaseRepository) + Migration.
  • Scheduler (@DfxCron + Process-Guard): täglich anchorPending, stündlich upgradeBatches.
  • Hash-Erfassung im Upload-Pfad (KYC + EP2), Best-Effort (rollt Upload nie zurück).
  • WORM-Provisioning (scripts/storage/provision-bucket.ts): Bucket mit Object Lock + Versioning + COMPLIANCE-Default-Retention, idempotent.

Lokal auf m5me getestet (real)

Test Ergebnis
Merkle/Archive/Scheduler Unit-Tests 49/49 grün
Voller App-tsc --noEmit clean
MinIO Object Lock (echt) Version-Delete blockiert (InvalidRequest); Overwrite → neue Version, Original unveränderlich
OpenTimestamps (echt, öffentliche Calendars) Stamp → .ots (700 B), verify → pending

Inhärentes Limit: die finale Bitcoin-Attestation reift in Stunden (Blockzeit) — synchron nicht abwartbar; bis dahin korrekt pending.

Review

Zwei adversariale Subagenten-Runden (Korrektheit + Konventionen). Runde 1 fand 1 Major (recordHash überschrieb geankerte Hashes) + Minors (Upload-Kopplung, EP2-WORM-Retry-Falle, upgrade-Persistenz) → alle gefixt + Tests ergänzt. Runde 2: Freigabe, keine offenen Findings.

Bewusst offen / Hinweise

  • uuid-Override: opentimestamps zieht das deprecatete request (→ uuid/v4); per nested Override requestuuid@3.4.0 gelöst, App-weites uuid@9 unangetastet. Mittelfristig: OTS-Calendar-I/O hinter fetch-basierten Transport legen, um request repo-weit loszuwerden.
  • verifyDocument ist bisher ein internes Audit-Primitiv (kein Endpoint) — On-Demand-Verifikation per Admin-Endpoint ist Folgeschritt.
  • EP2-Container sind dynamisch (pro Merchant) — Anchoring greift, aber das WORM-Bucket-Provisioning muss pro Container vor dem ersten PUT laufen (im Skript dokumentiert).
  • Monitoring-Alerts (Anchor-Job, OTS-Upgrade, Disk) = Infra (Loki/Grafana), separat.

Status

Draft. Nichts deployt. Build/Tests grün, real auf m5me verifiziert.

- merkle.ts: sha256, buildMerkleRoot, merkleInclusionProof, verifyMerkleProof
  (SHA-256, parent=sha256(left||right), odd-node duplicated, 0=throw/1=leaf)
- opentimestamps.service.ts: stamp/upgrade/verify wrapper over the public calendars
- merkle.spec.ts: 19 unit tests (roots, per-leaf inclusion proofs, tamper detection)
- add opentimestamps dependency
- ArchiveBatch / ArchiveFile entities (IEntity base, BaseRepository pattern),
  migration for both tables + unique(bucket,name) + batch FK
- ArchiveService: recordHash (idempotent), anchorPending (Merkle root over
  pending hashes -> batch -> OTS stamp), upgradeBatches (Bitcoin attestation),
  verifyDocument (hash match + Merkle inclusion proof + OTS verify)
- ArchiveModule; archive.service.spec.ts (round-trip + tamper, real Merkle, mocked OTS)
…ule 2, stage 3)

- Capture SHA-256 on compliance uploads: kyc-document (kyc) + fiat-output (EP2)
  call archiveService.recordHash after uploadBlob; support bucket untouched
- ArchiveScheduler (@DfxCron + process guard): daily anchorPending, hourly
  upgradeBatches; ARCHIVE_ANCHOR/ARCHIVE_UPGRADE process entries
- scripts/storage/provision-bucket.ts: create bucket with Object Lock +
  versioning + COMPLIANCE default retention (idempotent); note for dynamic EP2
- wire ArchiveModule into kyc + fiat-output modules; scheduler spec
The opentimestamps lib pulls request->request-promise->uuid via the legacy
'uuid/v4' subpath, which the repo-wide uuid@9 override breaks
(ERR_PACKAGE_PATH_NOT_EXPORTED). Scope a uuid@3.4.0 override under 'request'
only (nested override) so its calendar HTTP works, without touching the app's
uuid@9. Surfaced by the m5me integration test.
…upgrade persistence)

- recordHash: never overwrite the hash of an already-anchored file; identical
  -> no-op, differing -> hard error+log (deterministic blob names could
  otherwise silently rewrite an anchored leaf and break verifyDocument)
- KYC + EP2: recordHash is best-effort (try/catch + log), never rolls back the
  completed upload; EP2 sets reportCreated before anchoring to avoid a WORM
  re-PUT retry trap
- upgradeBatches: persist upgraded .ots bytes whenever they change, even while
  still pending; confirm only on a real Bitcoin attestation
- verify result exposes confirmed vs pending; verifyDocument loads batch via
  relations (removed dead branch); tests extended
- s3-storage.service.spec (aws-sdk-client-mock): config validation, listBlobs
  pagination + per-key HeadObject, getBlob empty-body guard, uploadBlob,
  copyBlobs CopySource encoding -> 100%
- mock-storage.service.spec: roundtrip, prefix filter, dummy-file fallbacks -> 100%
- storage.factory.spec: LOC->Mock, otherwise->S3 -> 100%
- opentimestamps.service.spec (lib mocked): stamp/upgrade/verify, confirmed vs
  pending -> 100%
- archive.service.spec: closed upgrade-no-change + verifyDocument confirmed branches
- kyc-document + fiat-output wiring specs: recordHash called with sha256, best-effort
  (upload not rolled back; reportCreated set before anchoring)
- add aws-sdk-client-mock devDependency
- archive.service.spec fakeStore now honors relations:['batch'] (batch only
  hydrated when requested) + explicit assertion that recordHash/verifyDocument
  request the relation -> removing it in prod now fails the suite
- fiat-output spec asserts invocationCallOrder uploadBlob < reportCreated <
  recordHash (WORM re-PUT deadlock guard)
- fiat-output spec covers the routeId -> sell.id fallback
Mutation-checked: each guard fails when the corresponding prod behavior is removed
@DFXswiss DFXswiss deleted a comment from github-actions Bot Jun 18, 2026
@TaprootFreak TaprootFreak marked this pull request as ready for review June 18, 2026 15:36
@TaprootFreak TaprootFreak merged commit 627f9e6 into feature/storage-service-sketch Jun 18, 2026
1 check passed
@TaprootFreak TaprootFreak deleted the feature/compliance-archive-anchoring branch June 18, 2026 15:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant