| Field | Value |
|---|---|
| Classification | PUBLIC |
| Date | 2026-06-03 |
| Version | 0.1.0-rc.1 |
| Status | Implemented & Verified (GKE deployment confirmed 2026-06-03) |
CAGE produces two fundamentally different categories of telemetry:
| Category | Content | Sensitivity | Retention |
|---|---|---|---|
| Application Metrics | Inference latency, token counts, model performance, traces | May contain PII | Operational |
| Compliance Audit Evidence | OSCAL findings, governance verdicts, ISO 42001 control scores, hash-chained context accumulator, AARM conformance reports | Evidentiary record | 7 years |
These categories must be physically isolated for three reasons:
If compliance audit evidence is stored in the same project that produces the telemetry being audited, a single compromised credential can both generate the data and tamper with the evidence. In litigation, opposing counsel asks: "Who controls the project where the compliance evidence is stored?" If the answer is "the same service account that runs the AI agent", the evidence is self-attested and its evidentiary weight collapses.
The governed financial advisor pod (src/governed_financial_advisor/) has LANGFUSE_PUBLIC_KEY / LANGFUSE_SECRET_KEY credentials that allow it to write traces and scores to the application metrics project. If a container breakout or dependency supply-chain attack compromises this pod, the attacker gains write access to application telemetry — but cannot read, write, or delete compliance audit evidence because those credentials (LANGFUSE_COMPLIANCE_*) are mounted only in the compliance bridge pod.
- ISO 42001 §A.7.5 — Records of AI management system performance must be maintained with controls to ensure integrity and availability.
- NIST SP 800-53 AU-9 — Audit records must be protected against unauthorized access, modification, and deletion.
- DORA Art. 12 — ICT audit trails must be maintained independently of the systems being audited.
Separate project credentials satisfy all three by ensuring no single service account has write access to both the operational telemetry and the audit evidence.
┌─────────────────────────────────────────────────────────────────────┐
│ GKE Cluster: governance-stack │
│ │
│ ┌───────────────────────────┐ ┌────────────────────────────────┐ │
│ │ governed-financial-advisor│ │ compliance-bridge │ │
│ │ (port 8000) │ │ (port 3001) │ │
│ │ │ │ │ │
│ │ Credentials: │ │ Credentials: │ │
│ │ LANGFUSE_PUBLIC_KEY │ │ LANGFUSE_PUBLIC_KEY (R) │ │
│ │ LANGFUSE_SECRET_KEY │ │ LANGFUSE_SECRET_KEY (R) │ │
│ │ │ │ LANGFUSE_COMPLIANCE_PUBLIC_KEY │ │
│ │ Writes to: │ │ LANGFUSE_COMPLIANCE_SECRET_KEY│ │
│ │ → Application Project │ │ │ │
│ │ │ │ Reads from: │ │
│ │ │ │ → Application Project (R) │ │
│ │ │ │ Writes to: │ │
│ │ │ │ → Compliance Project │ │
│ └───────────┬──────────────┘ └────────────┬───────────────────┘ │
│ │ │ │
└──────────────┼────────────────────────────────┼─────────────────────┘
│ │
▼ ▼
┌──────────────────────────┐ ┌──────────────────────────────────────┐
│ Langfuse Project: │ │ Langfuse Project: │
│ APPLICATION METRICS │ │ COMPLIANCE AUDIT │
│ │ │ │
│ Contents: │ │ Contents: │
│ • Inference traces │ │ • OSCAL finding scores │
│ • Token counts │ │ • ISO 42001 control verdicts │
│ • Model latency │ │ • Governance violation records │
│ • Agent routing spans │ │ • Remediation advisory traces │
│ • iso_42001_safety_rate │ │ • Eval dataset items (FAIL findings)│
│ scores │ │ • AARM conformance evidence │
│ │ │ │
│ Access: governed-advisor │ │ Access: compliance-bridge ONLY │
│ compliance-bridge│ │ │
│ (read only) │ │ │
└──────────────────────────┘ └──────────────────────────────────────┘
Key observation: The compliance bridge has credentials for both projects, but uses them asymmetrically:
- Application project — read-only access for
metrics.pyto query traces tagged withcontrol:<controlId>and aggregateiso_42001_outcomemetadata. - Compliance project — write access for
audit_workflow.pyto ingest OSCAL findings, create per-control compliance scores, and log LLM remediation advisories.
The governed financial advisor pod has no access to the compliance project.
Two distinct Kubernetes secrets are provisioned by Terraform (app_secrets/main.tf):
| Secret Name | Keys | Mounted By |
|---|---|---|
advisor-secrets |
LANGFUSE_PUBLIC_KEY, LANGFUSE_SECRET_KEY |
governed-financial-advisor, compliance-bridge |
langfuse-compliance-secrets |
LANGFUSE_COMPLIANCE_PUBLIC_KEY, LANGFUSE_COMPLIANCE_SECRET_KEY |
compliance-bridge only |
Source: app_secrets/main.tf L10-31 (advisor-secrets) and app_secrets/main.tf L45-58 (compliance-secrets).
The compliance bridge K8s deployment (compliance-bridge.yaml) mounts both secret objects:
# Main Langfuse project (application performance metrics)
- name: LANGFUSE_PUBLIC_KEY
valueFrom:
secretKeyRef:
name: langfuse-secrets # ← Application project
key: public-key
# ...
# Dedicated Langfuse compliance project (audit traces isolated from app metrics)
- name: LANGFUSE_COMPLIANCE_PUBLIC_KEY
valueFrom:
secretKeyRef:
name: langfuse-compliance-secrets # ← Compliance project
key: public-keySource: compliance-bridge.yaml L44-68.
Two separate Langfuse client factories enforce the isolation in application code:
Used by metrics.py to read traces for compliance metrics aggregation:
# src/compliance_bridge/metrics.py L55-61
def _make_app_langfuse():
from langfuse.api import LangfuseAPI
return LangfuseAPI(
username=os.environ.get("LANGFUSE_PUBLIC_KEY", ""),
password=os.environ.get("LANGFUSE_SECRET_KEY", ""),
base_url=os.environ.get("LANGFUSE_HOST", "https://cloud.langfuse.com"),
)Used by audit_workflow.py to write OSCAL findings and governance scores:
# src/compliance_bridge/audit_workflow.py L102-110
def _make_compliance_langfuse():
return _get_langfuse_class()(
public_key=os.environ.get("LANGFUSE_COMPLIANCE_PUBLIC_KEY", ""),
secret_key=os.environ.get("LANGFUSE_COMPLIANCE_SECRET_KEY", ""),
host=os.environ.get(
"LANGFUSE_COMPLIANCE_HOST",
os.environ.get("LANGFUSE_HOST", "https://cloud.langfuse.com"),
),
)The docstring in metrics.py makes the boundary explicit:
"Queries the application Langfuse project (LANGFUSE_PUBLIC_KEY / LANGFUSE_SECRET_KEY) for traces tagged control:. [...] The compliance project (LANGFUSE_COMPLIANCE_PUBLIC_KEY / LANGFUSE_COMPLIANCE_SECRET_KEY) is used only by audit_workflow.py."
The 6-step audit pipeline in audit_workflow.py uses both projects:
| Step | Action | Langfuse Project | Client Factory |
|---|---|---|---|
| 1 | Persist OSCAL YAML to GCS | Neither | — |
| 2 | Parse OSCAL → OscalFinding[] |
Neither | — |
| 2b | SHA-256 hash-chain accumulator | Neither | — |
| 3 | Ingest findings as scored traces | Compliance | _make_compliance_langfuse() |
| 3c | Populate eval dataset (FAIL) | Compliance | _make_compliance_langfuse() |
| 4 | Alert on critical failures | Neither | — |
| 5 | Fetch failing traces for context | Application (R) | _make_app_langfuse() |
| 5 | Log LLM advisory to compliance | Compliance | _make_compliance_langfuse() |
| 6 | AARM conformance report | Neither (GCS) | — |
Step 5 is the only cross-project operation: it reads failing traces from the application project to provide evidence context for the LLM remediation advisor, then writes the advisory back to the compliance project. This is an intentional one-way data flow — the compliance bridge acts as a read-only observer of application telemetry and a write-only producer of audit evidence.
| Threat | Vector | Mitigation |
|---|---|---|
| Audit Evidence Tampering | Compromised workload pod writes false compliance scores | Workload pod has no LANGFUSE_COMPLIANCE_* credentials |
| Evidence Deletion | Attacker with advisor-secrets access deletes compliance traces |
Application project credentials cannot access compliance project |
| Cross-Contamination | Application trace volume drowns compliance signals | Separate projects with independent storage, retention, and query scope |
| Credential Rotation Blast Radius | Rotating app Langfuse keys disrupts audit pipeline | Compliance credentials are a separate secret object with independent lifecycle |
| Gap | Description | Status |
|---|---|---|
| Shared Langfuse Host | Both projects may be hosted on the same Langfuse instance (LANGFUSE_HOST). A Langfuse-level compromise affects both projects. |
Accepted risk — mitigated by self-hosted Langfuse on GKE with dedicated namespace |
| Compliance Bridge Compromise | The compliance bridge pod has credentials for both projects. A full pod compromise gives read access to app telemetry AND write access to compliance evidence. | Mitigated by minimal attack surface (no public endpoint, no user input parsing, CPU/memory-limited), but remains the single point of credential aggregation |
| Same GCP Project | Both Langfuse projects currently run within the same GCP project. No GCP-level IAM boundary separates them. | Future: Move compliance Langfuse to a dedicated GCP project with separate IAM policy |
| Variable | Required By | Purpose |
|---|---|---|
LANGFUSE_PUBLIC_KEY |
Advisor, Compliance Bridge | Application metrics project — public key |
LANGFUSE_SECRET_KEY |
Advisor, Compliance Bridge | Application metrics project — secret key |
LANGFUSE_HOST |
All | Langfuse server URL (shared) |
LANGFUSE_COMPLIANCE_PUBLIC_KEY |
Compliance Bridge only | Compliance audit project — public key |
LANGFUSE_COMPLIANCE_SECRET_KEY |
Compliance Bridge only | Compliance audit project — secret key |
LANGFUSE_COMPLIANCE_HOST |
Compliance Bridge only | Optional: separate host for compliance project; falls back to LANGFUSE_HOST |
⚠️ POAM-018 (AU-9) — Open: IfLANGFUSE_COMPLIANCE_PUBLIC_KEY/LANGFUSE_COMPLIANCE_SECRET_KEYare not set, the compliance project client initializes with empty credentials. Langfuse SDK calls will fail silently (no exception raised — the SDK logs a warning and drops the trace). This means audit evidence collection fails without any visible error unless Langfuse SDK logs are monitored.Remediation (scheduled 2026-07-15): Add startup validation in
audit_workflow.pythat raisesRuntimeErrorif compliance credentials are empty in non-dev environments; add/healthcheck that reports compliance Langfuse connectivity status. Seedocs/POAM.mdPOAM-018.
This is documented as a known gap in NIST_RMF_CHUNK4:
"Compliance Langfuse project is a separate credential from the main project.
LANGFUSE_COMPLIANCE_PUBLIC_KEY/LANGFUSE_COMPLIANCE_SECRET_KEYmust be configured. If these env vars are absent, the compliance project fails silently — a critical evidence collection gap."
To verify the dual-project isolation is correctly configured:
# 1. Check that both secret objects exist in the cluster
kubectl get secret advisor-secrets -n governance-stack
kubectl get secret langfuse-compliance-secrets -n governance-stack
# 2. Verify the compliance bridge pod has both sets of credentials
kubectl exec -n governance-stack deploy/compliance-bridge -- env | grep LANGFUSE
# Expected output should include ALL of:
# LANGFUSE_PUBLIC_KEY=pk-lf-...
# LANGFUSE_SECRET_KEY=sk-lf-...
# LANGFUSE_COMPLIANCE_PUBLIC_KEY=pk-lf-... (DIFFERENT key)
# LANGFUSE_COMPLIANCE_SECRET_KEY=sk-lf-... (DIFFERENT key)
# 3. Verify the advisor pod does NOT have compliance credentials
kubectl exec -n governance-stack deploy/governed-financial-advisor -- env | grep COMPLIANCE
# Expected: no output (compliance credentials not mounted)
⚠️ POAM-019 (AU-9, SC-7) — Open:infra/targets/gcp-gke/main.tfcontains a fallback that silently collapses dual-project telemetry isolation whenlangfuse_compliance_public_key/langfuse_compliance_secret_keyTerraform variables are empty — it falls back to application project credentials. This silently defeats the evidentiary independence design.Impact: If
prod.tfvarsdoes not define compliance credentials (which it currently does not by default), production deployment has no telemetry isolation — all audit evidence flows to the same Langfuse project as application metrics.Remediation (scheduled 2026-07-15): Remove the Terraform fallback; make compliance credentials a required variable with no default; add compliance credentials to
prod.tfvarstemplate; add a Terraformvalidationblock requiring non-empty compliance keys whenenable_compliance_bridge = true. Seedocs/POAM.mdPOAM-019.
The current architecture uses two Langfuse projects within a single GCP project. The target architecture separates at the GCP project level:
| Component | Current | Target |
|---|---|---|
| Workload GCP Project | Single project (e.g., cage-prod) |
cage-prod — workloads + application Langfuse |
| Compliance GCP Project | Same project | cage-compliance — compliance Langfuse + GCS audit evidence + Cloud Audit Logs |
| IAM Boundary | Shared IAM policy | Separate IAM — cage-prod SA cannot access cage-compliance resources |
| VPC Peering | N/A (same project) | VPC peering or Private Service Connect for compliance bridge → compliance Langfuse |
| Cloud Audit Logs | Shared | Independent — compliance project logs are immutable to cage-prod administrators |
This would be a Terraform infrastructure change, not a code change. The Python client factories, K8s secret structure, and data flow already support this topology — only the Langfuse host URLs and IAM bindings need to change.
| Document | Relationship |
|---|---|
| 02-ARCHITECTURE.md §9.2 | Brief mention of dual-project setup |
| NIST_RMF_CHUNK4 | Documents the silent failure gap |
| EXTENSIBILITY_ARCHITECTURE.md §2.4 | References telemetry isolation as implementation requirement |
| infra/ENV_INTEGRATION.md | Credential mapping for both projects |
| compliance-bridge.yaml | K8s deployment with dual secret mounts |
| app_secrets/main.tf | Terraform provisioning of separate K8s secrets |
| docs/POAM.md POAM-018 | Silent credential failure — open remediation item |
| docs/POAM.md POAM-019 | Terraform fallback defeats isolation — open remediation item |