Ekho-Labs / infrastructure-tf-kubernetes  ·  Pull Request #92  ·  task15/matrix-metadata-only → main

Metadata-only Matrix credential staging

One OnePasswordItem reference is added to the Prometheus Stack module so a Kubernetes Secret can be materialized later. No credential value enters Terraform, no Matrix receiver is built, and nothing in this change asserts that the credentials work.

Open · not merged Hold — no merge, no apply Metadata only Semantic gate = Task 16 3 files · +222 / −0
1
Resource created
0
Deletes
0
Replace paths
5
Key names mapped
0
Matrix receivers
Base
14ebd76300348d087dd1e0d05165d9c43253edc5
Head (frozen)
052418a050dc67440987d15a2d7f3317f7db16f4
Tree (head = merge preview)
df8fd457eec41d0120a49fbaf9e7759e642e3a4a
Merge preview commit
67cac7080f4c4466c5767c64b29b5c41ebf83563

What this change is

Task 15 stages a credential reference. It writes a pointer to a 1Password vault item into the cluster and stops there. Everything that would prove the credential is usable — and everything that would actually deliver an alert — belongs to later, separately gated tasks.

What the PR does
  • Adds exactly one Terraform resource: a OnePasswordItem custom resource in namespace kube-prometheus-stack.
  • Points it at a single vault item path. The manifest carries a path and nothing else.
  • Adds a validator that pins that manifest to an exact literal shape and rejects three concrete mutations.
  • Adds a narrowly scoped exemption so the existing no-delivery guard tolerates the new resource's own name.
What the PR does not establish
  • It does not show the credentials are valid. Nothing authenticates against Matrix anywhere in this change.
  • It does not show Matrix delivery is active. No Matrix receiver, route, or webhook exists, and every alert still routes to the built-in null sink.
  • It does not verify the vault item actually holds the five expected fields.
  • It was not applied. No merge, no terraform apply, no Secret read, no workflow dispatch.

The staged object

The entire resource added to tf/modules/prometheus-stack/main.tf. The validator asserts the manifest is byte-for-byte this and has exactly the four keys apiVersion, kind, metadata, spec — with spec containing only itemPath.

tf/modules/prometheus-stack/main.tf  ·  lines 9–21+14
resource "kubernetes_manifest" "matrix_alertmanager_receiver" {
  manifest = {
    apiVersion = "onepassword.com/v1"
    kind       = "OnePasswordItem"
    metadata = {
      name      = "matrix-alertmanager-receiver"
      namespace = "kube-prometheus-stack"
    }
    spec = {
      itemPath = "vaults/kubernetes-vault/items/alertmanager_matrix_receiver"
    }
  }
}
Terraform address

module.prometheus_stack
.kubernetes_manifest
.matrix_alertmanager_receiver

Vault item path

vaults/kubernetes-vault
/items/alertmanager_matrix_receiver

Absent by assertion

No fields block, no value, no depends_on, no lifecycle. The validator requires the resource body to hold the single key manifest.

The five-key map

Five 1Password field labels are intended to materialize as five identically named keys on the resulting Secret.

1Password field labelKubernetes Secret key
1MATRIX_USER_IDMATRIX_USER_ID
2MATRIX_ACCESS_TOKENMATRIX_ACCESS_TOKEN
3MATRIX_ALERT_ROOMMATRIX_ALERT_ROOM
4ALERTMANAGER_BASIC_USERNAMEALERTMANAGER_BASIC_USERNAME
5ALERTMANAGER_BASIC_PASSWORDALERTMANAGER_BASIC_PASSWORD

Change map — staging, materialization, and the gate

Solid lanes are created or enforced by this PR. Dashed lanes are runtime behaviour or future work that this PR deliberately does not perform. Hover a lane to isolate it.

Terraform — this pull requestIn scope

Declares the pointer and nothing else. The manifest carries a path, not a value, so there is no credential for Terraform to render or persist; the reviewed plan artifact is redacted to structure only.

main.tf
kubernetes_manifest resource, metadata only
task15 validator
Pins the manifest to an exact literal; 3 negative cases
speculative plan
1 create · 0 delete · 0 replace
apply would create the custom resource — not performed in this PR
Kubernetes runtime — OnePassword Connect operatorOut of scope · runtime

Behaviour of the already-installed operator once the custom resource exists. This is where values first appear, entirely outside Terraform's view.

OnePasswordItem CR
matrix-alertmanager-receiver in kube-prometheus-stack
operator resolves itemPath
Reads the vault item through 1Password Connect
Secret materialized
Keys derived from the item's field labels
Trust boundary — staging ends here

Everything above is a reference: a path, a name, a namespace. Nothing above proves the referenced item exists, holds the five expected fields, or contains working credentials. No semantic claim crosses this line.

Task 16 — the authoritative semantic gateNot in this PR

The sole place where the credentials are actually judged. It binds its verdict to the specific Secret it inspected, by UID and resourceVersion, so a later rotation cannot silently inherit a stale pass.

bind Secret UID + resourceVersion
Verdict is pinned to one exact Secret revision
whoami
Identity check against Matrix
join
Room membership check
send
End-to-end delivery check
only a passing Task 16 gate authorises receiver work
Task 17 — receiver workloadNot built · actively blocked

Zero Matrix delivery resources exist today. Alertmanager still declares a single built-in null receiver and routes every alert to it, which is the standing no-delivery safeguard. The Task 9 contract fails the build if core tries to add real delivery, and that guard is unchanged outside one path-scoped exemption.

Matrix receiver
Blocked: webhook keys are forbidden
route
Blocked: no matching route may exist
receiver Deployment / Service
Blocked: sensitive resource types

Plan outcome

The reviewed artifact is a redacted plan manifest — structure without values. Its schema is asserted, not assumed.

Observed — the only changed address
# module.prometheus_stack
#   .kubernetes_manifest
#   .matrix_alertmanager_receiver
actions       = ["create"]
delete_count  = 0
replace_paths = []
  • Exactly one non-no-op resource change is permitted; more than one fails the contract.
  • The validator accepts only ["create"] or ["update"]. The observed plan is create.
  • Any delete action or any replacement path fails closed — one of the three negative test cases proves it.
Redaction — how values stay out

The manifest may contain exactly three top-level keys, and each resource change exactly nine. The absent keys are the point:

# allowed per change
address, module_address, mode, type, name,
actions, replace_paths,
before_sensitive, after_sensitive

# absent by assertion — this is the redaction
before, after

Because before and after are rejected outright, the reviewed artifact structurally cannot carry a value, rather than merely happening not to.

Trusted plan evidence — bound to the frozen head
Workflow run
32109226912 · attempt 1 · success
Artifact
9314286884
terraform-speculative-plan-052418a0…
tfplan sha256
772a78a5cbdec7dedc81aed2fb648982185d3e34ee54abb119f1765515a374db
plan manifest sha256
5221b470126d4fafab540ff7973534e2db5ec4b2576f7983efbd860dfe4f61d5
Remote run
run-tx4LXcN2EtjtqG4N
Artifact expires
2026-08-19T06:59:59Z

File by file

Three files, all inside tf/modules/prometheus-stack. Nothing else in the repository is touched.

tf/modules/prometheus-stack/main.tf+14−0Modified

The staged resource itself, added above the existing Grafana password resource. It is the only Matrix-related resource in a 444-line module whose other twenty resources are Grafana, Prometheus, Pushgateway, ingresses and dashboard ConfigMaps.

Why here: the Secret must land in the same namespace as the Alertmanager that will eventually consume it, so the reference belongs to the Prometheus Stack module rather than a separate one.

tf/modules/prometheus-stack/tests/task15_metadata_validator.py+203−0Added

A standalone contract validator. It parses main.tf with python-hcl2, requires the staging resource to be the sole one of its name, and compares the manifest against an expected literal — so any drift in path, name, namespace, or shape fails.

It then runs three negative cases, each a deep copy mutated and required to be rejected: a wrong itemPath, an added depends_on, and a plan whose action is delete. Testing that the guard refuses bad input is what gives the positive assertion its weight.

Note on invocation: the plan-manifest half only runs when a plan path is passed as an argument. Invoked bare, it validates source and mutation cases only.

tf/modules/prometheus-stack/tests/task9_contract/policy.py+5−0Modified

The existing no-delivery guard scans every walked location for the tokens matrix and /alerts/, plus the five Task 15 field names and the item-name token alertmanager_matrix_receiver. The new resource necessarily trips it: its Terraform name contains matrix and its itemPath contains alertmanager_matrix_receiver.

The fix is a path-scoped skip, not a token removal — and it suppresses only forbidden-string scanning. The sensitive-resource-type and manifest-kind checks still run over the new resource, which passes them on its own merits because OnePasswordItem is not a sensitive kind. Webhook-key detection and the alternate-config-source checks are likewise untouched, though it is worth being precise that those read the Helm values and the rendered Alertmanager config rather than the Terraform resource, so they never covered this resource to begin with.

Weak links and reviewer notes

No blocking defect was found in the change as scoped. These are the seams a reviewer should hold in mind — each one is a consequence of Task 15 deliberately deferring meaning to Task 16.

MediumThe five-key assertion validates a constant, not the configuration+
Whatvalidate_source builds source_fields from the left column of FIELD_MAPPING and secret_keys from the right column, then asserts they are equal and number five. Both columns come from the same hardcoded literal of five identical pairs, so the assertion is true by construction and cannot fail.
ConsequenceThe five-key mapping is documented by constant rather than verified. Nothing here checks that the vault item carries these labels, nor that the materialized Secret exposes these keys.
Blast radiusContained. A wrong or missing field surfaces only at Task 16, or later at delivery time — not at plan or test time.
JudgementAcceptable while Task 16 remains the authoritative gate, which the change's own hold notes state. It would become a real gap if anything downstream ever treated a passing Task 15 validator as evidence about the Secret's contents.
LowThe guard exemption is a substring match on the location path+
WhatThe skip triggers when the constant .kubernetes_manifest.matrix_alertmanager_receiver appears anywhere in a walked location, so every nested location under that resource is exempt from forbidden-token scanning.
Why boundedThe Task 15 validator independently pins that same resource to an exact literal manifest with exactly four keys. A token that the Task 9 guard now ignores would still have to survive an equality check against the expected manifest.
Coupling riskThe two checks are load-bearing together. Relaxing the Task 15 equality assertion silently widens the Task 9 exemption, and the connection is not obvious from either file alone.
SuggestionA comment in policy.py pointing at the pinning assertion would keep the pair from drifting apart.
LowThe no-Secret rule guards the Terraform surface, not the cluster surface+
Whatresource_failures blocks core from declaring a kind: Secret manifest, and OnePasswordItem is deliberately absent from the sensitive-kind set — so the new resource passes that check on its own merits, without needing the exemption.
NuanceA Secret is still created in-cluster, by the operator, as the intended outcome. The guard's promise is that Terraform does not author a Secret, not that no Secret comes into existence.
JudgementCorrect as designed and consistent with the metadata-only framing; worth stating plainly so the guard is not read as a stronger claim than it makes.
InfoThe trusted plan artifact expires 2026-08-19T06:59:59Z+
WhatThe reviewed plan binding names artifact 9314286884 from run 32109226912, with a fixed expiry.
ConsequenceIf the hold outlasts the expiry, the digests in the binding can no longer be re-verified against a retrievable artifact and a fresh speculative plan is required before apply.
InfoTrivy was deferred locally; CI static checks carry it+
WhatThe local validation record notes no trivy executable was installed, so that scan was left to the Terraform Static Checks job.
StatusTerraform Static Checks passed on the frozen head, so coverage exists — it simply came from CI rather than the local run.
InfoMerging introduces no content change relative to the head+
WhatHead commit 052418a0 and merge-preview commit 67cac708 resolve to the same tree, df8fd457.
ConsequenceThe reviewed tree and the tree that would land are identical, so the plan taken at the head remains representative of the merge result for as long as the base does not move.

Status

Hold — review pending, no merge and no apply REVIEW_PENDING_NO_MERGE_OR_APPLY

Every automated check that ran on the frozen head passed — Apply and Main Plan were skipped by design, not failed — and the change is tightly scoped to what Task 15 authorises. The hold is not a symptom of a failure; it is the design. Task 16 remains the sole gate that may assert anything about the credentials themselves, and no step in this pull request attempts that assertion.

PASSSecurity Check
PASSTerraform Static Checks
PASSTerraform Plan
SKIPTerraform Apply — by design
SKIPTerraform Main Plan — by design
PASSterraform test — 8 passed, 0 failed
PASStask15 metadata validator
PASStask9 contract + amtool
PASSsecret scan — no values or placeholders