ops-mason/plans/mas-tem-relay-smtp.md
tegwick 2f7237fe01 MASON-WP-0014: record CCR-2026-0051
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 278674@bnt-lap001
Assistant-Session: 64827407-e196-4ce3-b70c-76e7b537493f
2026-10-18 12:26:14 +02:00

14 KiB
Raw Blame History

id demand_source consumer_repo credential_type status approved_by approved_at created updated
mas-tem-relay-smtp mail-address-semantics/workplans/MAS-WP-0003-T03 mail-address-semantics openbao-kv-founder-provision reviewed null null 2026-10-18 2026-10-18

Construction plan: MAS live-test TEM SMTP relay credential

1. Demand

MAS-WP-0003-T03 runs a temporary live-test MX (mas/mas-mx) on railiance01. It sends service replies through Scaleway Transactional Email at [smtp.tem.scaleway.com]:587, because outbound port 25 is blocked on the node. The MX needs one SMTP credential: username (the Scaleway project ID, which is not secret) and password (a Scaleway API secret key). The founder types the value into the OpenBao UI. ops-mason builds an empty home and a read path into namespace mas, delivered as Secret mas-relay, which the MX mounts optionally at /etc/mas-relay. The lane is temporary: it is removed with the mas namespace when MAS-WP-0003 closes.

Requested by hub message 14bdd217 (2026-10-18). The request suggested kv/mas/tem-relay; it is placed at platform/suppliers/scaleway/mas-tem-relay instead (supplier convention). It also asked for a ClusterSecretStore openbao-mas and an ESO token. This plan keeps the shape but adapts it to the current conventions (§3).

2. Existing-structure survey

Generated by scripts/plan-survey.py at 2026-10-18T10:13:55Z against BAO_ADDR=http://127.0.0.1:18200, token display_name token-claude-MASON-WP-0014-T02 (policies: default, ops-mason-survey). Metadata only: no kv get and no */data/* read was issued; policy bodies were read with bao policy read for name-matched candidates only.

Demand: KV path platform/suppliers/scaleway/mas-tem-relay, proposed policy workload-kv-read-mas-tem-relay, AppRole -, Kubernetes role external-secrets-mas-tem-relay, consumer mail-address-semantics. Match terms: mail-address-semantics, external-secrets-mas-tem-relay, tem, smtp, mas, suppliers, scaleway, mas-tem-relay.

bao secrets list (10 mounts, 1 relevant)

platform/                kv         version=2

bao auth list (6 auth mounts, 2 relevant)

approle/                 approle
kubernetes/              kubernetes

bao policy list (94 policies, 5 relevant)

authelia-smtp-eso                                        matches: smtp
operator-kv-scaleway-bootstrap                           matches: scaleway
ops-mason-construct                                      matches: mas
ops-mason-survey                                         matches: mas
telemetry-smtp-eso                                       matches: smtp

bao list auth/approle/role (approle) (14 roles, 0 relevant)

(no relevant entries)

bao list auth/kubernetes/role (kubernetes) (35 roles, 2 relevant)

authelia-smtp-eso                                matches: smtp
    token_policies=['authelia-smtp-eso']
    token_ttl=900
    token_max_ttl=900
    token_num_uses=0
    bound_service_account_names=['authelia-smtp-eso']
    bound_service_account_namespaces=['sso']
    audience=openbao
telemetry-smtp-eso                               matches: smtp
    token_policies=['telemetry-smtp-eso']
    token_ttl=900
    token_max_ttl=900
    token_num_uses=0
    bound_service_account_names=['telemetry-smtp-eso']
    bound_service_account_namespaces=['telemetry']
    audience=openbao

bao policy read of name-matched candidates (target platform/data/suppliers/scaleway/mas-tem-relay)

authelia-smtp-eso: no stanza covers platform/data/suppliers/scaleway/mas-tem-relay
operator-kv-scaleway-bootstrap: no stanza covers platform/data/suppliers/scaleway/mas-tem-relay
ops-mason-construct: path "platform/data/suppliers/*" capabilities=['create']
ops-mason-construct: path "platform/metadata/suppliers/*" capabilities=['create', 'read', 'update', 'delete', 'list']
ops-mason-construct: path "platform/metadata/*" capabilities=['read', 'list']
ops-mason-construct: path "platform/data/*" capabilities=['deny']
ops-mason-survey: not permitted by ops-mason-survey
telemetry-smtp-eso: no stanza covers platform/data/suppliers/scaleway/mas-tem-relay

bao kv metadata get / bao kv list (metadata only)

platform/suppliers/scaleway/mas-tem-relay: exists, current_version=2, created=2026-10-18, updated=2026-10-18
    owner: railiance-platform
platform/suppliers/scaleway/: backup, bootstrap, mas-tem-relay

ops-warden routing catalog /home/worsch/ops-warden/registry/routing/catalog.yaml (48 entries, 10 relevant)

state-hub-forge-derivation-read  owner=ops-mason  status=active  matches: mas
    path_template: platform/workloads/state-hub/forge-derivation (field FORGE_READ_TOKEN)
    auth_method: Kubernetes auth (role state-hub-forge-derivation, ServiceAccount state-hub/state-hub) from the in-cluster state-hub workload
object-storage-sts  owner=net-kingdom  status=draft  matches: tem
rein-openweights-openrouter-approle  owner=ops-mason  status=active  matches: mas
    path_template: reins/rein-openweights/openrouter
    auth_method: AppRole login role=rein-openweights (role_id+secret_id via REIN_OPENWEIGHTS_APPROLE_DIR)
inbound-library-imap-approle  owner=ops-mason  status=active  matches: mas
    path_template: platform/workloads/inbound-library/imap
    auth_method: AppRole login role=inbound-library (role_id+secret_id in ~/.local/inbound-library/approle, config/library.json credential.approle_dir)
audit-core-senders  owner=ops-mason  status=active  matches: mas
    path_template: platform/workloads/audit-core/senders
    policy_ref: external-secrets-audit-core
    auth_method: OpenBao KV v2 custody; audit-core reads via ESO (policy external-secrets-audit-core, token auth). Operators and agents never read the registry; a sender gets its own exact-path projection.
email-connect-transactional  owner=railiance-platform  status=active  matches: smtp
    path_template: platform/workloads/email-connect/transactional
    policy_ref: flex-auth check secret.read:email-connect
    auth_method: caller's own OpenBao token (operator OIDC, or ESO child token openbao-email-connect-eso-token)
scaleway-bootstrap  owner=railiance-platform  status=draft  matches: mas, scaleway
    path_template: platform/workloads/railiance/scaleway/bootstrap
    auth_method: caller's own OpenBao token (founder / operator workstation)
openbao-structure-survey  owner=ops-mason  status=active  matches: mas
    auth_method: founder-minted scoped grant: ops-mason scripts/bao-session.sh grant <task> (policy ops-mason-survey, 45m, display_name claude-<task>)
platform-supplier-credentials  owner=railiance-platform  status=active  matches: suppliers
    path_template: platform/suppliers/<supplier>/<account>
    auth_method: railiance-platform delegated credential change (CCR); founder-attended provision from a mode-0600 file
fluid-telegram-operator  owner=ops-mason  status=draft  matches: mas
    path_template: platform/workloads/coulomb/fluid-telegram/hall-of-helix/{operator-app,operator-session,bot-token,redaction-salt}
    auth_method: bao login -method=oidc -path=netkingdom role=fluid-telegram-hall-of-helix-workload-kv (members of fluid-telegram-operators, aal2, 15m token)

Does an existing lane already satisfy this demand?

Decision (review 2026-10-18): no. Edited from the generated "partial".

  • The KV path exists because ops-mason created the placeholder (v1, 10:03Z), and the founder wrote the TEM value as v2 (10:11Z). That is the home this plan builds, not an existing lane.
  • No policy or role reads platform/data/suppliers/scaleway/mas-tem-relay. The only name-matched policy that touches the path is ops-mason-construct, which has create-only access and cannot read.
  • The two SMTP ESO lanes, authelia-smtp-eso and telemetry-smtp-eso, both read platform/data/workloads/railiance-telemetry/smtp, which is the IONOS mailbox platform@coulomb.social (CCR-2026-0035). That is a different supplier and belongs to production senders, so it is not reused.
  • Repo sources (checked earlier the same day): no catalog entry or CCR for mas or TEM. openbao-email-connect is a separate production sender.
  • Not detected by design: a policy with an unrelated name covering the path.

3. Proposed changes

SEC-DSM §37:

  • Q1 Can the operation use identity directly? No. TEM SMTP authenticates only with username/password.
  • Q2 Can the target accept federated identity? No. TEM SMTP has no federation.
  • Q3 Can the target issue an ephemeral credential? Not viably. Scaleway API keys can carry an expiry, but adding an issuer for a temporary test lane would be issuance for show (DSM-TRANS-003). Instead the founder sets the key's own Scaleway expiry to the end of MAS-WP-0003.
  • Q4 Can an existing static credential be rotated automatically? No automated rotation for a lane this short-lived.
  • Q5 Must a persistent static credential exist? Yes, for the life of MAS-WP-0003 only. This is §5 level "vaulted static secret".

Credential facts:

Field Value
Owner founder (Scaleway IAM)
Consumer mas/mas-mx
Authority Scaleway IAM API key on a dedicated application with only the Transactional Email permission set, on the TEM project
Delivery ESO → Secret mas/mas-relay
Copies OpenBao KV, plus one Kubernetes Secret (a delivery copy, not the authority)
Rotation Replace the KV version; ESO refreshes
Revocation Delete the API key in Scaleway, then tear down (rows 6–7 below)
Detection TEM sending logs and the OpenBao audit log for reads of the path
What would eliminate it Removing the MX, or an MTA that relays through email-connect's API instead of SMTP
# Action Object Reuse-vs-new rationale
1 create KV v2 platform/suppliers/scaleway/mas-tem-relay, fields username, password (placeholder xxx) No existing home (§2). Supplier account path platform/suppliers/<supplier>/<account> (founder decision 2026-10-08, ops-warden platform-supplier-credentials); credential-change.py accepts it
2 create policy workload-kv-read-mas-tem-relay: read on platform/data/suppliers/scaleway/mas-tem-relay, plus token lookup-self and revoke-self (CCR-2026-0051 policy file, used verbatim) Single-path ESO reader; no metadata read needed
3 create Kubernetes auth role external-secrets-mas-tem-relay: SA mas-tem-relay-eso, ns mas, audience openbao, ttl/max 15m, token_no_default_policy Current pattern (CCR-2026-0050), with no static ESO token to deliver or expire
4 create (consumer repo) ServiceAccount mas-tem-relay-eso, namespaced SecretStore openbao-mas-tem-relay, ExternalSecret mas-relay (keys username, password), all in ns mas Namespaced instead of a ClusterSecretStore, so the store is deleted with the namespace
5 reuse (railiance-platform) CCR-2026-0051 (railiance-platform@4398f89), with policy file openbao/policies/workload-kv-read-mas-tem-relay.hcl Platform's record of the lane. ops-mason applies rows 2–3 under ops-mason-construct after approval and records evidence against the CCR
6 tear-down (at MAS-WP-0003 close) auth role, policy, KV path (metadata delete) Cluster-scoped OpenBao objects do not go away with the namespace
7 tear-down (founder) Scaleway API key Revokes the authority itself

No catalog entry: the lane is temporary and has a single consumer.

4. Review notes

  • Naming: mas-tem-relay is used for the supplier account segment, policy, role, SA and store, so one grep finds every object to retire. Secret mas-relay keeps the name the consumer asked for.
  • TTL/scoping: 15m role TTL matches CCR-2026-0050. The policy covers one data path and no metadata. Binding is to exact SA and namespace claims.
  • Redundancy: does not reuse the email-connect sender key (§2).
  • Deviation from the request: it asked for a ClusterSecretStore openbao-mas and an ESO token. Both are replaced, by Kubernetes auth (DSM-K8S-001, projected identity before a broker token) and by a namespaced store (easier to retire). The consumer applies rows 4 itself.
  • Secret values: ops-mason neither reads nor writes them. The policy grants read only to the ESO role.
  • Naming vs existing SMTP lanes: authelia-smtp-eso and telemetry-smtp-eso use one name for role, policy and SA. This lane keeps the external-secrets-* / workload-kv-read-* shape, which is what ops-mason-construct may build (MASON-IN-0005).
  • Build path: after approval, ops-mason applies rows 2–3 itself under bao-session.sh build. Row 1 is done: the placeholder came first and the founder wrote the value on 2026-10-18.
  • CCR: CCR-2026-0051 (approvers: founder, railiance-platform-owner, mail-address-semantics-owner). The post-build check compares the written policy with its policy file.
  • Open: founder approval; bound_claims_confirmed after the SA exists.

5. Executive summary

One-line ask: let the temporary MAS test mail server read its TEM SMTP login from OpenBao, and nothing else, until MAS-WP-0003 ends.

Who gets access: the External Secrets reader in namespace mas, and only when it signs in as ServiceAccount mas-tem-relay-eso. No person, no agent, no other namespace.

To what: read on platform/suppliers/scaleway/mas-tem-relay (username, password), delivered as Secret mas/mas-relay to the mas-mx container at /etc/mas-relay.

For how long: 15-minute OpenBao sessions, renewed by ESO, until teardown at MAS-WP-0003 close. The Scaleway key's own expiry is the hard stop.

Blast radius if the credential leaks: someone can send mail as your TEM project, which risks reputation, quota and cost, until you revoke the key in the Scaleway console. If the key is TEM-only it gives no other Scaleway access.

Cost to reverse: low. Delete the role, the policy and the KV path, then revoke the key. Nothing else depends on it.

Decision: pending.