Commit graph

53 commits

Author SHA256 Message Date
CTO (Paperclip)
d6efabd96f ci(images): pass Harbor secrets as env vars so robot $ is not expanded (DEV-617)
All checks were successful
validate / JSON Schema (ajv) (push) Successful in 21s
build-images / signing-service image (push) Successful in 25s
validate / DB (migrate up/down dry-run) (push) Successful in 25s
validate / Go (vet + test) (push) Successful in 1m15s
validate / Rego (opa fmt + opa test) (push) Successful in 14s
Harbor robot usernames are of the form `robot$basicstack+forgejo-ci`.
Forgejo Actions substitutes `${{ secrets.HARBOR_USERNAME }}` into the
`run:` script before bash runs it, so `set -u` sees the resulting
`robot$basicstack+forgejo-ci` and dies with "basicstack: parameter
not set" trying to expand `$basicstack`. Pass the Harbor credentials
as env vars on the step; bash then reads them from the environment as
plain strings instead of parsing them for parameter expansion.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-25 20:17:41 +00:00
CTO (Paperclip)
a4d5391f55 ci(images): re-run after setting HARBOR_USERNAME/PASSWORD secrets (DEV-617)
Some checks failed
build-images / signing-service image (push) Failing after 19s
validate / DB (migrate up/down dry-run) (push) Successful in 27s
validate / Go (vet + test) (push) Has been cancelled
validate / JSON Schema (ajv) (push) Successful in 21s
validate / Rego (opa fmt + opa test) (push) Successful in 13s
The signing-service image build itself was green after 770e71d (buildx
plugin installed, --mount=type=cache works). The push step then failed
with "Must provide --username with --password-stdin" because the
HARBOR_USERNAME / HARBOR_PASSWORD Forgejo repo secrets were never
configured. Robot account `robot$basicstack+forgejo-ci` created in
Harbor (project `basicstack`, push+pull) and both secrets set. Empty
commit to trigger a re-run and verify the push.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-25 20:16:18 +00:00
CTO (Paperclip)
770e71daab ci(images): install docker-buildx so --mount=type=cache works (DEV-617)
Some checks failed
validate / JSON Schema (ajv) (push) Successful in 18s
validate / Rego (opa fmt + opa test) (push) Successful in 15s
build-images / signing-service image (push) Failing after 1m32s
validate / DB (migrate up/down dry-run) (push) Successful in 24s
validate / Go (vet + test) (push) Successful in 1m34s
The signing-service Dockerfile uses the BuildKit `--mount=type=cache`
syntax, which the legacy Docker builder rejects. Debian trixie's
`docker-cli` package does not pull in a BuildKit builder, so even
`DOCKER_BUILDKIT=1` fails with "the --mount option requires BuildKit".

Install Debian trixie's `docker-buildx` package (registers the plugin
at /usr/libexec/docker/cli-plugins/docker-buildx) and switch the build
step to an explicit `docker buildx build --load` so BuildKit frontend
features work regardless of docker-cli aliasing. `--load` ensures the
result is in the local daemon for the subsequent `docker push`.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-25 20:10:56 +00:00
CTO (Paperclip)
174a74435a ci(images): enable BuildKit for signing-service image build
Some checks failed
build-images / signing-service image (push) Failing after 15s
validate / JSON Schema (ajv) (push) Successful in 20s
validate / Rego (opa fmt + opa test) (push) Successful in 13s
validate / DB (migrate up/down dry-run) (push) Successful in 25s
validate / Go (vet + test) (push) Successful in 1m13s
The signing-service Dockerfile ([DEV-609](/DEV/issues/DEV-609))
starts with `# syntax=docker/dockerfile:1.7` and uses
`RUN --mount=type=cache,...` for Go build/module caches. The legacy
Docker builder rejects both; the image-build job has been failing on
main since DEV-609 landed:

    the --mount option requires BuildKit. Refer to
    https://docs.docker.com/go/buildkit/ to learn how to build images
    with BuildKit enabled

Fix: set `DOCKER_BUILDKIT=1` on the job env so the daemon routes the
build through the declared Dockerfile frontend.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-25 20:06:05 +00:00
CTO (Paperclip)
89da9b0414 ci(db): migrate down above per_diem_rates and verify all four tables
Some checks failed
build-images / signing-service image (push) Failing after 15s
validate / JSON Schema (ajv) (push) Successful in 19s
validate / Rego (opa fmt + opa test) (push) Successful in 14s
validate / DB (migrate up/down dry-run) (push) Successful in 25s
validate / Go (vet + test) (push) Successful in 1m11s
[DEV-608](/DEV/issues/DEV-608) added migration
0004_approval_sessions.{up,down}.sql but did not update the DB job's
rollback step. The step still ran `migrate down 2`, which with four
migrations rolls back only 0004 + 0003 and leaves 0002_agents in place
— the hard-coded check "agents must be gone after down 2" then fails:

    FAIL: agents still present after migrate down 2

Fix: roll back all three migrations layered on top of
0001_per_diem_rates (`down 3`) and verify agents, agent_keys, AND
approval_sessions are all gone while per_diem_rates remains. Also
cover approval_sessions in the up-direction assertion so a future
"missed table" shows up in both halves of the test. A comment at the
new step names the invariant so the next migration bump is obvious.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-25 20:03:50 +00:00
CTO (Paperclip)
cfb5e29b54 admin-api: go mod tidy after signing-service go 1.26 bump
Some checks failed
build-images / signing-service image (push) Failing after 15s
validate / JSON Schema (ajv) (push) Successful in 19s
validate / Rego (opa fmt + opa test) (push) Successful in 13s
validate / DB (migrate up/down dry-run) (push) Failing after 25s
validate / Go (vet + test) (push) Successful in 1m13s
[DEV-609](/DEV/issues/DEV-609) bumped services/signing-service to
go 1.26.0 (required by github.com/open-policy-agent/opa v1.21.4).
admin-api replaces basicstack.de/proofedmesh/services/signing-service
with the sibling via a replace directive, so its go.mod effective
minimum is also 1.26 and the transitive indirect requirements (opa,
pgx, jwx, go-jose, levenshtein, …) must be listed. CI surfaced
this as:

    go: updates to go.mod needed; to update it:
        go mod tidy

Fix: `go mod tidy` on services/admin-api with the 1.26 toolchain.
Local `go vet` + `go test ./...` pass on all admin-api packages.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-25 20:00:58 +00:00
CTO (Paperclip)
47f295d864 ci: bump Go image to 1.26-alpine for signing-service dep
Some checks failed
validate / JSON Schema (ajv) (push) Successful in 19s
build-images / signing-service image (push) Failing after 24s
validate / Go (vet + test) (push) Failing after 5s
validate / Rego (opa fmt + opa test) (push) Successful in 13s
validate / DB (migrate up/down dry-run) (push) Failing after 20s
signing-service depends on github.com/open-policy-agent/opa v1.21.4,
which requires Go >= 1.26.0. The CI Go image and the signing-service
Dockerfile were still pinned to golang:1.23-alpine3.21, so the
"Go (vet + test)" job and the signing-service image build both
failed on main as soon as [DEV-609](/DEV/issues/DEV-609) landed:

    go: module ../signing-service requires go >= 1.26.0
         (running go 1.23.12; GOTOOLCHAIN=local)

Fix: pin both to golang:1.26-alpine3.23 (oldest alpine tag that
exists for the 1.26 line). All other modules declare go 1.23 which
1.26 still accepts, so no downstream churn.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-25 19:59:05 +00:00
CTO (Paperclip)
ba1b88d490 testagent-transform: Phase-1 trust-chain demo agent (DEV-610)
Some checks failed
build-images / signing-service image (push) Failing after 16s
validate / JSON Schema (ajv) (push) Successful in 19s
validate / Go (vet + test) (push) Failing after 4s
validate / Rego (opa fmt + opa test) (push) Successful in 13s
validate / DB (migrate up/down dry-run) (push) Failing after 21s
Minimal CLI agent process that end-to-end demonstrates the Transform step
of the ProofedMesh trust chain:

  1. Fetches an approval attestation from the transparency-log by
     attestation_ref (`sha256:<hex>`).
  2. DSSE-verifies the fetched envelope against a pinned approval-signer
     ed25519 keyring; stops before any signing call on failure.
  3. Builds an in-toto v1 Statement with predicateType
     `basicstack.de/attestations/transform/v1`, operation
     `expense.map_to_sink`, materials[0].attestation_ref = approval_ref,
     and subject[0].digest.sha256 = sha256(canonical(handover_payload)).
  4. Calls signing-service /api/v1/sign/dsse with BOTH identities:
     Authorization: Bearer <pocket-id client_credentials token> AND
     X-Workload-Token: <projected SA token from
     /var/run/secrets/tokens/trust-api>.
  5. Submits the returned DSSE envelope + content_hash to the
     transparency-log /api/v1/log/entries.
  6. POSTs the canonical handover_payload bytes to the dummy-sink
     (schema sink.dummy.v1), carrying the transform content_hash on the
     X-Attestation-Ref header.

Tests use httptest for all four backends:

  - Happy path (verifies the bytes sent to the sink match the signed
    subject digest and that the TLog recorded the same content_hash the
    agent returned).
  - Invalid approval signature -> stops before sign-call; TLog and sink
    untouched.
  - Signing-service returns 403 signing_denied -> surfaced as
    signer.ErrSigningDenied; TLog and sink untouched.
  - Missing Pocket-ID token -> fails before any network call.

Includes PocketID client_credentials TokenSource (OIDC discovery + cache)
and ProjectedSATokenSource (reads kubelet-managed Volume) mirroring
admin-api/approval-service.

Non-scope: no read-side attestations (Phase 2), no K8s/CI image build
wiring (follow-up ticket).

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-25 19:55:02 +00:00
CTO (Paperclip)
30dd2f8b65 signing-service: Pocket-ID JWKS + K8s TokenReview + OPA (DEV-609)
Wires the signing-service's production path for ProofedMesh Phase 1:
the service now performs a double identity check (Pocket-ID OIDC JWT
+ K8s ServiceAccount TokenReview), reconciles both against the
trust-api `agents` registry, and asks an OPA/Rego policy whether the
caller may sign with the requested key_ref.

Code
- internal/token/pocket_id.go: OIDC-Discovery-backed JWKS validator
  with automatic kid rotation (coreos/go-oidc).
- internal/token/k8s_tokenreview.go: hand-rolled TokenReview client;
  reads X-Workload-Token, posts to authentication.k8s.io/v1 via the
  pod's in-cluster SA token + CA.
- internal/registry: Postgres resolver for the agents table +
  in-memory Static resolver for tests.
- internal/policy/opa.go: OPA/rego StructuredDecider; input carries
  both lookups so the policy can enforce identity consistency AND
  the key_ref ↔ role mapping.
- internal/httpapi/sign.go: new structured flow when
  Validator+TokenReviewer+Registry+StructuredPolicy are all wired;
  dev path (static bearers + AllowAll) remains intact.
- cmd/signing-service/main.go: -pocket-id-issuer,
  -pocket-id-audience, -k8s-tokenreview, -registry-dsn,
  -policy-bundle flags.

Policy
- policies/sinks/signing_service.rego: identities_agree +
  key_ref_matches_role rules; agent → key_ref prefix map covers
  approval-service, testagent-transform, dummy-sink.

Deploy
- deploy/signing-service/01-rbac.yaml: ClusterRole with
  tokenreviews.create + binding to the signing-service SA.
- deploy/signing-service/20-deployment.yaml: adds POCKET_ID_ISSUER,
  POCKET_ID_AUDIENCE, TRUST_PG_DSN env, projected SA token volume,
  OPA policy ConfigMap mount, structured flags.
- deploy/signing-service/00-serviceaccount.yaml: disables default
  auto-mount; a projected volume is used.
- deploy/signing-service/README.md: documents prod flags +
  ConfigMap generation.

Tests
- httptest-mock Pocket-ID (OIDC discovery + JWKS, kid rotation),
  StaticTokenReviewer fake, Static registry, real Rego policy.
- Happy paths: Approval-Service → trust-api/approval-signer-2026-q4;
  testagent-transform → trust-api/agent-testagent-transform-2026-q4.
- Denies: registry mismatch, wrong key_ref for role, missing /
  bad workload token, bad Pocket-ID token, kid rotation.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-25 19:42:40 +00:00
Paperclip
3a01cc651f approval-service: Phase-1 content-binding approval service (DEV-608)
New services/approval-service with the Session → Preview → Confirm
pipeline required by DEV-606/DEV-608:

* POST /api/v1/sessions starts a Pocket-ID-bound approval session.
* POST /api/v1/approvals/preview renders content server-side, computes
  H_content = sha256(canonical JSON), stores the (nonce, salt, canonical,
  challenge) tuple and returns the human verification digits plus the
  WebAuthn challenge = nonce || H_content.
* POST /api/v1/approvals/confirm verifies the WebAuthn assertion against
  the stored challenge, DSSE-signs the Approval statement via the
  signing-service (key_ref=trust-api/approval-signer-2026-q4), submits
  to the transparency-log, and returns {approval_id, attestation_ref,
  log_index}. TLog failures leave the row at attestation-pending for
  the reconciler.

* db/trust/migrations/0004_approval_sessions.{up,down}.sql: new tables
  approval_sessions, approval_previews, approvals (schema matches the
  Phase 1 store contract).

* internal/content: canonical JSON + H_content + 6-digit verification
  code (byte-compatible with signing-service/internal/canonical).
* internal/webauthn: stdlib-only Phase-1 verifier; wire shape stays
  WebAuthn-compatible for the Phase-2 swap to go-webauthn/webauthn.
* internal/signer, internal/tlog: HTTP clients mapping 5xx / connection
  errors to typed ErrSignerUnavailable / ErrTLogUnavailable so the
  HTTP layer can return 502 for signing failures and persist
  attestation-pending on TLog failure.
* internal/service.Orchestrator ties the pipeline together and persists
  the Approval BEFORE the TLog submit (never return a log_index for
  an un-persisted approval).

Tests cover: happy path, tampered H_content → 400, expired challenge
→ 403, signer 5xx + conn-refused → 502, TLog failure → row stays
attestation-pending, duplicate confirm → 409, session expired → 403.
go vet clean, go test ./... passes.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-25 19:42:06 +00:00
CTO (Paperclip)
b99801f0df ci: install docker-cli explicitly on debian trixie (DEV-607)
Some checks failed
build-images / signing-service image (push) Failing after 20s
validate / JSON Schema (ajv) (push) Successful in 19s
validate / Rego (opa fmt + opa test) (push) Successful in 14s
validate / Go (vet + test) (push) Successful in 34s
validate / DB (migrate up/down dry-run) (push) Successful in 28s
Debian trixie split the Docker CLI into its own `docker-cli` package.
`docker.io` now only pulls in the daemon, and the CLI dep is a
Recommends — which `--no-install-recommends` skips. Both the new DB
dry-run job in validate.yml and the pre-existing signing-service image
job in build-images.yml were failing with `docker: not found` after
install.

Add `docker-cli` explicitly to both apt install lines.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-25 19:27:24 +00:00
CTO (Paperclip)
51733d8b36 db+ci: Agent Registry schema (agents + agent_keys), Phase-1 seed, migrate up/down CI (DEV-607)
Some checks failed
build-images / signing-service image (push) Failing after 14s
validate / JSON Schema (ajv) (push) Successful in 19s
validate / Rego (opa fmt + opa test) (push) Successful in 14s
validate / DB (migrate up/down dry-run) (push) Failing after 18s
validate / Go (vet + test) (push) Successful in 34s
- 0002_agents / 0003_agent_keys migrations per architecture v1.1 §4.4
  (incl. (k8s_namespace, k8s_service_account) uniqueness for the signing
  service's dual identity check and an agent_id, valid_from index for the
  verification lookup path)
- idempotent seed db/trust/seeds/0001_phase1_agents.sql with the three
  Phase-1 identities: agent-approval-service, agent-testagent-transform,
  agent-dummy-sink (namespace agent-trust)
- new db CI job in validate.yml: runs postgres:16-alpine, migrate up ->
  down 2 -> up, applies the seed twice, verifies three agents

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-25 19:23:20 +00:00
CTO
951f22f80e transparency-log: real Trillian backend wiring + agent-trust deploy (DEV-604)
Some checks failed
build-images / signing-service image (push) Failing after 14s
validate / JSON Schema (ajv) (push) Successful in 21s
validate / Rego (opa fmt + opa test) (push) Successful in 14s
validate / Go (vet + test) (push) Successful in 30s
- services/transparency-log/pkg/backend/trillian.go: real gRPC client
  on github.com/google/trillian v1.6.1. Submit() does QueueLeaf with the
  canonical DSSE envelope as LeafValue and the raw content_hash bytes as
  LeafIdentityHash, then polls GetLatestSignedLogRoot +
  GetInclusionProofByHash until the leaf is sequenced. Idempotency uses
  Trillian's ALREADY_EXISTS signal on the inner QueuedLogLeaf.Status, so
  a repeated submit still returns 200 OK with the originally-assigned
  log_index + a valid proof against the current STH.
- Unconfigured backend (missing endpoint or LogID) returns the historical
  ErrTrillianUnavailable so the httpapi layer keeps mapping it to 503
  instead of silently 500. Existing stub-returns-503 test updated.
- cmd/transparency-log: pointer-receiver Trillian wiring; flag docs
  mention the production SRV name.
- services/transparency-log/Dockerfile: distroless multi-stage build
  used by deploy/trillian/40-transparency-log.yaml.

- deploy/trillian/: full K8s stack for the agent-trust namespace —
  MySQL 8.4 StatefulSet (schema init ConfigMap vendored from upstream
  storage.sql v1.6.1), Trillian LogServer + LogSigner Deployments on
  gcr.io/trillian-opensource-ci:v1.6.1, createtree Job that writes
  the TreeID to a ConfigMap (re-apply safe), transparency-log
  Deployment with --backend=trillian pulling TRILLIAN_LOG_ID from
  the ConfigMap.
- deploy/trillian/README.md: first-time apply + smoke-test + secret
  rotation + signer-key clarification (STH key is Trillian-internal;
  DSSE envelope signing stays with signing-service / OpenBao).

- services/admin-api: go.mod / go.sum pick up the new transitive deps
  (trillian + grpc) via the local replace on transparency-log.
- services/transparency-log/README.md: Backends table updated; next-
  steps list no longer carries Trillian-deployment as a follow-up.

Deployed to the live cluster from this heartbeat: MySQL + LogServer +
LogSigner running in agent-trust, tree id 8482455225499059043, service
smoke-tested end-to-end (201 on first POST, 200 on idempotent repeat,
409 on content_hash mismatch, tree_size=2 with correct audit path).

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-25 18:39:44 +00:00
CTO
b52936ce9d signing-service: Dockerfile, K8s manifests (agent-trust), image CI, secret convention
Some checks failed
validate / JSON Schema (ajv) (push) Successful in 20s
validate / Rego (opa fmt + opa test) (push) Successful in 14s
validate / Go (vet + test) (push) Successful in 21s
build-images / signing-service image (push) Failing after 13s
- Dockerfile: multi-stage (golang:1.23-alpine3.21 → distroless/static:nonroot),
  CGO off, trimpath, stripped, UID/GID 65532, read-only rootfs friendly.
- deploy/signing-service/: ServiceAccount, Deployment (2 replicas, probes,
  tight SecurityContext), ClusterIP Service on :8081, NetworkPolicy
  (ingress from agent-trust, DNS egress), example Secret, and README with
  the full dev + prod secret convention.
- .forgejo/workflows/build-images.yml: builds image on push + PR,
  pushes :<git-sha> + :main to harbor.basicstack.de on main pushes only.
  Clone pattern matches validate.yml (sha256 repo, manual git clone).
- README updates: service README gains Deployment section; repo README
  layout now mentions services/signing-service, services/transparency-log,
  deploy/, and the new workflow.

Scope matches DEV-605. OpenBao, OPA and Pocket-ID JWKS stay stubs and move
in their own follow-up subtasks; the prod secret shape is documented so
those subtasks can plug in without reshaping the Deployment.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-25 18:19:51 +00:00
CTO Agent
5d338dd725 signing-service: public pkg/httpapi test surface + admin-api interop test (DEV-599)
All checks were successful
validate / Rego (opa fmt + opa test) (push) Successful in 13s
validate / JSON Schema (ajv) (push) Successful in 19s
validate / Go (vet + test) (push) Successful in 22s
- services/signing-service/pkg/httpapi exposes NewTestHandler so sibling
  modules can build the real signing-service in-process (same pattern
  transparency-log already uses).
- services/admin-api/internal/attest/interop_signing_test.go drives the real
  attest.HTTPSigner against the real signing-service handler and asserts the
  end-to-end ADR-0005 round-trip: envelope shape, keyid, content_hash
  self-consistency across canonicalisers, Ed25519 signature verification
  with the server's public key, and canonical-payload byte equality.

Closes the "Interop-Test: admin-api attest.HTTPSigner-Client erfolgreich
gegen diesen Server" acceptance criterion from DEV-599.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-25 18:11:40 +00:00
CTO (Paperclip)
9f1eb84032 admin-api: Pocket-ID session-cookie verifier (OIDC Authorization-Code + PKCE, JWKS, group check)
All checks were successful
validate / Go (vet + test) (push) Successful in 23s
validate / Rego (opa fmt + opa test) (push) Successful in 15s
validate / JSON Schema (ajv) (push) Successful in 20s
- internal/auth/pocket_id.go: Middleware mit /login + /callback,
  OIDC-Discovery + JWKS-Cache, RS256-Signatur-Prüfung, PKCE S256,
  AES-256-GCM-versiegeltes Session-Cookie, Erzwingung von
  Gruppenmitgliedschaft 'proofedmesh-admin' (konfigurierbar).
  Protect(h) setzt WithPrincipal(sub) + WithSession(sid) auf ctx;
  fehlendes/ungültiges Cookie → 302 nach /login (next= bewahrt).
- internal/auth/context.go: WithPrincipal/WithSession/PrincipalFrom
  /SessionFrom aus httpapi/rates.go in internal/auth gezogen,
  damit auth und httpapi dieselben Kontext-Keys teilen (keine
  import cycle: httpapi importiert auth).
- httpapi/rates.go: delegiert jetzt an auth.*; die Shim-Exports
  WithPrincipal/WithSession bleiben für bestehende Tests.
- cmd/admin-api/main.go: Pocket-ID-Middleware aktivieren, sobald
  -oidc-client-id, -oidc-client-secret, -oidc-redirect-url und
  -session-cookie-secret gesetzt sind; ohne sie bleibt das
  Stub-Admin-Prinzipal (bestehendes Verhalten für Dev-Mode).
- Tests (10 neue, 36/36 insgesamt grün, go vet clean):
  httptest-Mock-Pocket-ID mit JWKS + /token-Endpoint deckt den
  vollständigen Flow ab, inkl. missing cookie, falsche Gruppe,
  Nonce/State-Mismatch, Audience-Mismatch, abgelaufenes ID-Token,
  unbekannte kid, und Session-Ablauf nach TTL.
- README: Auth-Abschnitt + Konfigurations-Flags aktualisiert.

DEV-603

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-25 18:10:02 +00:00
CTO
69b3f3012f services: add transparency-log Go service (ADR-0005 POST /api/v1/log/entries)
All checks were successful
validate / Rego (opa fmt + opa test) (push) Successful in 14s
validate / JSON Schema (ajv) (push) Successful in 20s
validate / Go (vet + test) (push) Successful in 20s
- services/transparency-log/pkg/backend: Backend interface with in-memory
  append-log (RFC 6962 Merkle tree) + Trillian wrapper (prod stub).
- Idempotenz-Pflicht: Wiederaufruf mit identischem content_hash liefert
  200 + gleichen log_index + aktuellen inclusion_proof.
- content_hash-Verifikation serverseitig; Mismatch → 409.
- pkg/httpapi: ADR-0005-konformer Handler (201/200/400/404/409/503).
- cmd/transparency-log: minimaler HTTP-Server, memory default,
  --backend trillian als Stub (503).
- Interop-Test in services/admin-api/internal/tlog/interop_test.go
  fährt die echte transparency-log pkg gegen die admin-api
  HTTPSubmitter — Happy-Path, Idempotenz und content_hash-Mismatch
  abgedeckt.
- go test: 10 Tests transparency-log + 2 Interop-Tests admin-api, go
  vet clean in beiden Modulen.

Folge-Subtask: Trillian-Deployment + Keys (siehe ADR-0005 Open Points).

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-25 18:08:53 +00:00
CTO
2e44147a71 services: add attest-reconciler Go service (periodic worker re-signs per_diem_rates rows without rate_change_attestation_ref via Signer+TLog, 11/11 go tests pass, go vet clean)
All checks were successful
validate / JSON Schema (ajv) (push) Successful in 19s
validate / Go (vet + test) (push) Successful in 19s
validate / Rego (opa fmt + opa test) (push) Successful in 14s
- services/attest-reconciler/ as its own go.mod (admin-api internal/ is not importable; shared code gets extracted when a 3rd consumer appears).
- Scheduler accepts "@every <dur>" or a bare Go duration; context-cancellation stops the loop cleanly.
- Store client defines PendingRateStore + Postgres skeleton for SELECT ... WHERE rate_change_attestation_ref IS NULL.
- Orchestrator + HTTPSigner + tlog.HTTPSubmitter mirror admin-api byte-for-byte so the TLog idempotency key collides on resubmits (kein Doppelsignieren).
- Prometheus text-format /metrics endpoint, dependency-free: attest_reconciler_dirty_rows_total, _reconciliation_attempts_total, _reconciliation_failures_total, _idempotent_noop_total.
- Tests with fake store + fake signer + fake submitter cover: happy path, already-attested rows are NOT re-signed, TLog idempotency across ticks, TLog-down failure path, pre-cancelled ctx.
- Top-level README reflects the new service; CI auto-discovers services/*/go.mod so no workflow change needed.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-25 18:06:36 +00:00
CTO (Paperclip)
be3ac9cdb8 admin-api: Pocket-ID client_credentials TokenSource with OIDC discovery + cache (replaces StaticToken in prod)
All checks were successful
validate / JSON Schema (ajv) (push) Successful in 20s
validate / Go (vet + test) (push) Successful in 19s
validate / Rego (opa fmt + opa test) (push) Successful in 13s
* services/admin-api/internal/attest/pocket_id_token_source.go — attest.TokenSource impl: OIDC discovery via /.well-known/openid-configuration, HTTP Basic client_credentials grant, mutex-guarded in-memory token cache with configurable RefreshLeeway (default 30s = "verwirft bei exp-30s" from DEV-602).
* services/admin-api/internal/attest/pocket_id_token_source_test.go — 10 subtests against an httptest OIDC mock: happy path, cache hold, exp-30s refresh boundary, optional scope/audience, bad creds (401), missing config, discovery failures, non-positive expires_in, type-conformance.
* services/admin-api/cmd/admin-api/main.go — buildTokenSource(): -bearer flag wins (dev), else -oidc-issuer + POCKET_ID_CLIENT_ID + POCKET_ID_CLIENT_SECRET env → PocketIDTokenSource (prod); hard-fail at startup if issuer is set but credentials are missing. New flags -pocket-id-scope / -pocket-id-audience for the client_credentials request.
* services/admin-api/README.md — split inbound/outbound Pocket-ID auth, document the admin-api OIDC client registration (UI + API), new flags, env vars, prod bootstrap example.

36/36 go tests pass, go vet clean.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-25 18:04:00 +00:00
CTO Agent
d9fa1c55b9 services: add signing-service (real Ed25519 DSSE, in-memory keyring, AllowAll+Static validators as DEV; OpenBao/OPA/Pocket-ID as stubs). Interop with admin-api attest.HTTPSigner verified: 19 Go-Tests + end-to-end content_hash equality.
All checks were successful
validate / Rego (opa fmt + opa test) (push) Successful in 14s
validate / JSON Schema (ajv) (push) Successful in 19s
validate / Go (vet + test) (push) Successful in 18s
2026-10-25 18:02:44 +00:00
CTO Agent
ca62108f70 admin-api: wire Signer+TLog into Create/Retire handlers; on tlog outage row stays attestation-pending (26/26 tests pass, go vet clean)
All checks were successful
validate / Rego (opa fmt + opa test) (push) Successful in 13s
validate / JSON Schema (ajv) (push) Successful in 20s
validate / Go (vet + test) (push) Successful in 19s
2026-10-25 17:51:53 +00:00
CTO Agent
cacdf7e286 attest+tlog: ADR-0005 Signing/TLog API contract, HTTP clients + orchestrator in admin-api (go vet clean, 11 new tests pass)
Some checks failed
validate / Rego (opa fmt + opa test) (push) Successful in 13s
validate / JSON Schema (ajv) (push) Has been cancelled
validate / Go (vet + test) (push) Has been cancelled
2026-10-25 17:51:33 +00:00
CTO Agent
e3698de492 ci: fix Go job — golang:1.23-trixie does not exist on Docker Hub, use golang:1.23-alpine3.21 (git 2.47+, sha256-capable)
All checks were successful
validate / Rego (opa fmt + opa test) (push) Successful in 13s
validate / JSON Schema (ajv) (push) Successful in 20s
validate / Go (vet + test) (push) Successful in 21s
2026-10-25 17:30:18 +00:00
CTO Agent
eeaceb07ed ci+readme: add Go CI job (golang:1.23-trixie, vet + test for every services/**/go.mod); README reflects db/, admin-api, go-test commands
Some checks failed
validate / Rego (opa fmt + opa test) (push) Successful in 13s
validate / Go (vet + test) (push) Failing after 1s
validate / JSON Schema (ajv) (push) Successful in 20s
2026-10-25 17:28:32 +00:00
CTO Agent
65514559dc services: add admin-api Go service (per-diem rates CRUD, in-memory + Postgres stub store, HTML templates, 13/13 go tests pass, go vet clean)
Some checks failed
validate / Rego (opa fmt + opa test) (push) Successful in 12s
validate / JSON Schema (ajv) (push) Has been cancelled
2026-10-25 17:28:14 +00:00
CTO Agent
d56f918c69 db: add ADR-0004 (golang-migrate), db/ layout, 0001_per_diem_rates migration (up+down) with EXCLUDE constraint and immutability trigger
Some checks failed
validate / Rego (opa fmt + opa test) (push) Has been cancelled
validate / JSON Schema (ajv) (push) Has been cancelled
2026-10-25 17:28:00 +00:00
CTO Agent
3f466dfae8 services: add per-diem-calculator Go skeleton (deterministic compute core + 5 unit tests, HTTP wire stubbed); README reflects new layout
All checks were successful
validate / Rego (opa fmt + opa test) (push) Successful in 11s
validate / JSON Schema (ajv) (push) Successful in 18s
2026-10-25 17:07:55 +00:00
CTO Agent
c0472691c8 policies: add receipts + per_diem_rates data-source policies, per_diem transformation policy, extend expense.rego for v1.2.0 (54/54 tests pass)
Some checks failed
validate / Rego (opa fmt + opa test) (push) Successful in 12s
validate / JSON Schema (ajv) (push) Has been cancelled
2026-10-25 17:07:38 +00:00
CTO Agent
27c45b356a schemas: add ds/receipts v1.0.0, ds/per_diem v1.0.0, expense.canonical v1.2.0, sink.db.expense v1.2.0 (+ examples)
Some checks failed
validate / Rego (opa fmt + opa test) (push) Successful in 12s
validate / JSON Schema (ajv) (push) Has been cancelled
2026-10-25 17:07:20 +00:00
CTO Agent
95e50aff7d docs(architecture): add 4.7 receipts flow (ds:receipts, PII-store, hash binding) and 6.2 deterministic rules example (ds:per-diem-rates, compute-agent, no LLM on numeric path)
All checks were successful
validate / Rego (opa fmt + opa test) (push) Successful in 12s
validate / JSON Schema (ajv) (push) Successful in 17s
2026-10-25 16:50:34 +00:00
CTO Agent
2181225022 docs(architecture): add section 7.2a for per-line project allocations (expense.canonical v1.1.0)
All checks were successful
validate / Rego (opa fmt + opa test) (push) Successful in 13s
validate / JSON Schema (ajv) (push) Successful in 16s
2026-10-25 16:20:38 +00:00
CTO Agent
5d309e16c1 policies+ci: v1.1.0 allocation support — expense.rego enforces per-item sum invariant, 5 new tests (25/25 pass), CI validates examples per schema version
All checks were successful
validate / Rego (opa fmt + opa test) (push) Successful in 12s
validate / JSON Schema (ajv) (push) Successful in 16s
2026-10-25 16:19:37 +00:00
CTO Agent
81e5f334b5 schemas: add expense.canonical v1.1.0 and sink.db.expense v1.1.0 — per-line project allocations (percentage or absolute)
Some checks failed
validate / Rego (opa fmt + opa test) (push) Successful in 12s
validate / JSON Schema (ajv) (push) Has been cancelled
2026-10-25 16:19:21 +00:00
CTO Agent
36aa391e59 ci: POSIX-safe example-validation loop (trixie-slim sh is dash, no shopt)
All checks were successful
validate / Rego (opa fmt + opa test) (push) Successful in 13s
validate / JSON Schema (ajv) (push) Successful in 17s
2026-10-25 15:41:18 +00:00
CTO Agent
23594e1f9b ci: skip actions/checkout — do manual git clone (checkout does git init without --object-format=sha256)
Some checks failed
validate / Rego (opa fmt + opa test) (push) Successful in 12s
validate / JSON Schema (ajv) (push) Failing after 15s
2026-10-25 15:40:01 +00:00
CTO Agent
0a3ae36bf1 ci: run jobs in Debian trixie (git 2.47+) — sha256 git object format needs git >= 2.42
Some checks failed
validate / Rego (opa fmt + opa test) (push) Failing after 13s
validate / JSON Schema (ajv) (push) Failing after 46s
2026-10-25 15:36:59 +00:00
CTO Agent
1d4eccb6d3 ci: use fetch-depth 0 — Forgejo repo has sha256 object format, checkout cannot fetch by raw SHA
Some checks failed
validate / Rego (opa fmt + opa test) (push) Failing after 30s
validate / JSON Schema (ajv) (push) Failing after 35s
2026-10-25 15:33:02 +00:00
CTO Agent
81bc32e9c3 ci: make workflow robust — apt-get curl pre-step, local ajv install, OPA to user HOME, no setup-node reliance
Some checks failed
validate / Rego (opa fmt + opa test) (push) Failing after 24s
validate / JSON Schema (ajv) (push) Failing after 28s
2026-10-25 15:30:57 +00:00
CTO Agent
baa8b61021 docs(README): link policies/ and CI workflow; add local validation snippets
Some checks failed
validate / JSON Schema (ajv) (push) Failing after 29s
validate / Rego (opa fmt + opa test) (push) Failing after 26s
2026-10-25 15:28:50 +00:00
CTO Agent
961102ca6f ci: add Forgejo Actions workflow — JSON Schema (ajv) + Rego (opa fmt + opa test)
Some checks failed
validate / JSON Schema (ajv) (push) Has been cancelled
validate / Rego (opa fmt + opa test) (push) Has been cancelled
2026-10-25 15:28:19 +00:00
CTO Agent
e721073767 policies: add Rego skeletons for transformations, sink expense_db_app, and ds expense_db_app (20/20 opa test pass, rego.v1) 2026-10-25 15:28:12 +00:00
CTO Agent
71259de4c8 docs(README): link schemas/ and docs/adr/ in repo layout and entry points 2026-10-25 15:11:47 +00:00
CTO Agent
a9c853f881 docs(adr): add ADR template and ADR-0001..0003 (DB-App scope, session scope tokens, PII retention) 2026-10-25 15:11:23 +00:00
CTO Agent
30939b777a schemas: add expense.canonical, ds.expense.project_assignment, sink.db.expense (all v1.0.0) with README 2026-10-25 15:11:10 +00:00
CTO Agent
989140ee88 docs(architecture): clarify PII-Store is a role, not a product; add dedicated section 8.4 with technology choice 2026-10-25 14:57:00 +00:00
CTO Agent
fddce4d0fb docs(architecture): add parallel read path (Data Sources, Query Attestations, Session Scope Tokens); make own DB-App the first rollout 2026-10-25 14:41:18 +00:00
CTO Agent
7205fa2e98 docs(README): reflect sink generalization — ERP is one of many possible sinks 2026-10-25 14:12:11 +00:00
CTO Agent
9f89f9fb6b docs(architecture): generalize sink concept — SAP is one of many sinks, add sink adapter contract 2026-10-25 14:11:34 +00:00
CTO Agent
7de9cbc6ec docs: README points to V1.1 as active architecture, V1.0 archived 2026-10-25 11:02:33 +00:00
CTO Agent
1d5950d32c docs(architecture): add V1.1 — Pocket-ID only, PII separation, Trillian, Content Binding, Replay 2026-10-25 11:02:04 +00:00