ProofedMesh/policies
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
..
data_sources policies: add receipts + per_diem_rates data-source policies, per_diem transformation policy, extend expense.rego for v1.2.0 (54/54 tests pass) 2026-10-25 17:07:38 +00:00
sinks signing-service: Pocket-ID JWKS + K8s TokenReview + OPA (DEV-609) 2026-10-25 19:42:40 +00:00
transformations policies: add receipts + per_diem_rates data-source policies, per_diem transformation policy, extend expense.rego for v1.2.0 (54/54 tests pass) 2026-10-25 17:07:38 +00:00
README.md policies: add receipts + per_diem_rates data-source policies, per_diem transformation policy, extend expense.rego for v1.2.0 (54/54 tests pass) 2026-10-25 17:07:38 +00:00

ProofedMesh Policies (OPA / Rego)

Rego-Policies für die drei in docs/architecture/v1.1.md Abschnitt 6 definierten Policy-Klassen. Alle Policies nutzen Rego v1 (import rego.v1).

Policy-Klassen

Klasse Package-Präfix Fragt der
Transformationen proofedmesh.transformations.<domain> Signing Service
Senken proofedmesh.sinks.<sink_id_slug> Verification Service / Adapter
Datenquellen proofedmesh.data_sources.<ds_id_slug> Session Scope Token Service

Default in jedem Package ist allow := false — explizites Zulassen ist erforderlich (deny-by-default).

Dateistruktur

policies/
  transformations/
    expense.rego          — v1.0.0/v1.1.0/v1.2.0 map_to_sink + normalize, with allocation-sum invariant
    expense_test.rego
    per_diem.rego         — expense.compute_per_diem (agent-per-diem-calculator → v1.2.0 output)
    per_diem_test.rego
  sinks/
    expense_db_app.rego
    expense_db_app_test.rego
  data_sources/
    expense_db_app.rego
    expense_db_app_test.rego
    receipts.rego         — ds:receipts write (agent-travel) + read-by-content_hash (agent-sink-*)
    receipts_test.rego
    per_diem_rates.rego   — ds:per-diem-rates read (agent-per-diem-calculator only, country+valid_on required)
    per_diem_rates_test.rego

Input-Verträge

Transformationen

{
  "agent_id": "agent-sink-mapping",
  "operation": "expense.map_to_sink",
  "input_schema":  { "name": "expense.canonical", "version": "1.0.0" },
  "output_schema": { "name": "sink.db.expense",   "version": "1.0.0" },
  "sink_target":   "sink:expense-db-app",
  "approval":      { "allowed_transformations": [...], "allowed_sinks": [...] },
  "input_payload":  { ... canonical fields ... },
  "output_payload": { ... sink fields ... }
}

Senken

{
  "agent_id": "agent-sink-posting",
  "sink_id":  "sink:expense-db-app",
  "action":   "expense.post",
  "payload":  { ... sink.db.expense ... },
  "approval": { "allowed_sinks": [...] }
}

Datenquellen (für Session Scope Token Service)

{
  "agent_id":   "agent-travel",
  "user_sub":   "user-12345",
  "session_id": "SES-...",
  "data_source": "ds:expense-db-app",
  "resource":   "project_assignments",
  "filters":    { "employee_id": "EMP-12345" },
  "operation":  "read"
}

Zusätzliche Daten (nicht input, sondern über data.* bereitgestellt): data.employee_mapping.by_sub (user_sub → employee_id), data.agent_registry.by_id (Agent-Metadaten), data.sinks.by_id, data.data_sources.by_id. Trust-API lädt diese bei jedem Policy-Call aus Postgres und übergibt sie via OPA Partial-Evaluation oder bei Bedarf als input.data.

Lokal testen

opa test policies/ -v
opa fmt --list --diff policies/      # formatting check

Beides läuft auch in CI (siehe .forgejo/workflows/validate.yml).

Policy-Versionierung

Jede Policy-Datei bekommt bei Breaking Changes eine neue Hauptversion. Historische Versionen bleiben parallel, solange Attestierungen mit policy_version darauf verweisen (Retention 1 Jahr). Rego-Bundles für Deployment werden aus dem jeweiligen Branch gebaut; der SHA-256 des Bundles landet in jeder Decision als policy_version (siehe ADR-0002, Abschnitt „Token-Format").