Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: opus Assistant-Process: 1170398@bnt-lap001 Assistant-Session: 622cd705-cb31-4322-ba08-dd42e44fa583
12 KiB
| id | demand_source | consumer_repo | credential_type | status | approved_by | approved_at | approved_spec_sha256 | created | updated | build_spec | |||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| inbound-library-imap-approle | inbound-library/workplans/ILIB-WP-0003-T01 | inbound-library | openbao-approle-kv | built | Bernd Worsch | 2026-10-05 | c62765c8a4505821e77b58c5e0daf0511dec39fb569a05e190ff51f5cdc3e2ba | 2026-10-05 | 2026-10-05 |
|
Construction plan: inbound-library IMAP AppRole
1. Demand
inbound-library collects material delivered by email for the rest of the
ecosystem. Its first use is the daily and weekly Grok and ChatGPT intelligence
briefs for intelligence-radar. A systemd user timer on the workstation reads the
dedicated mailbox library@coulomb.social (IONOS, imap.ionos.de:993)
read-only every 15 minutes. It must authenticate without an operator, and the
founder ruled out caching the password in a file. On every run it should log in
to OpenBao, read the IMAP entry once, and revoke its token. The consumer code is
already implemented and tested against a fake OpenBao
(inbound_library/vault.py, ILIB-WP-0003-T04).
The KV entry already exists. railiance-platform/scripts/inbound_library_imap_entry.py
created it password-free, and the founder added IMAP_PASSWORD as version 2
(railiance-platform/docs/inbound-library-imap-custody.md). This plan only
creates the read path to it.
2. Existing-structure survey
Generated by scripts/plan-survey.py at 2026-10-05T12:31:21Z against BAO_ADDR=http://127.0.0.1:18200, token display_name token-claude-MASON-WP-0008-T05 (policies: default, ops-mason-build). 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/workloads/inbound-library/imap, proposed policy workload-kv-read-inbound-library-imap, AppRole inbound-library, Kubernetes role -, consumer inbound-library. Match terms: inbound-library, imap.
bao secrets list (10 mounts, 1 relevant)
platform/ kv version=2
bao auth list (5 auth mounts, 2 relevant)
approle/ approle
kubernetes/ kubernetes
bao policy list (76 policies, 1 relevant)
workload-kv-read-binky-company-email-imap matches: imap
bao list auth/approle/role (approle) (13 roles, 0 relevant)
(no relevant entries)
bao list auth/kubernetes/role (kubernetes) (25 roles, 0 relevant)
(no relevant entries)
bao policy read of name-matched candidates (target platform/data/workloads/inbound-library/imap)
workload-kv-read-binky-company-email-imap: no stanza covers platform/data/workloads/inbound-library/imap
bao kv metadata get / bao kv list (metadata only)
platform/workloads/inbound-library/imap: exists, current_version=2, created=2026-10-05, updated=2026-10-05
platform/workloads/inbound-library/: imap
ops-warden routing catalog /home/worsch/ops-warden/registry/routing/catalog.yaml (40 entries, 2 relevant)
binky-company-email-imap owner=railiance-platform status=active matches: imap
path_template: tenants/binky/company-email/imap
auth_method: caller's own OpenBao token (OIDC netkingdom role binky-company-email-imap-workload-kv-read)
agent-harness-binky-mail-approle owner=railiance-platform status=active matches: imap
path_template: tenants/binky/company-email/imap
auth_method: AppRole login role=agent-harness-binky-mail (role_id+secret_id on worker host via EXECUTOR_APPROLE_DIR)
Does an existing lane already satisfy this demand?
Draft answer: partial. Review before committing; this is generated, not a decision.
- KV path
platform/workloads/inbound-library/imapalready exists (v2) - policy grants were checked only for name-matched candidates; a policy with an unrelated name that covers
platform/data/workloads/inbound-library/imapis not detected
Reviewed answer: no existing lane satisfies this demand. The survey's
"partial" refers only to the KV entry, which already exists and is not touched
by this plan. The two IMAP lanes it found (binky-company-email-imap,
agent-harness-binky-mail-approle) serve a different mailbox, on mount
tenants/, for a different tenant. Binding inbound-library to their policy
would grant it the Binky company mailbox, and adding this path to their policy
would grant the Binky consumers the library mailbox. Both would widen scope. No
policy name in the 76 listed names covers the path, and no AppRole or Kubernetes
role is named for the consumer. The survey only inspects policies whose names
match, so a policy with an unrelated name covering
platform/data/workloads/inbound-library/imap would be missed. Any such grant
would be a pre-existing over-grant, and this plan does not depend on it.
Live precedent settings, read with the same grant (bao read auth/approle/role/<name>): agent-harness-binky-mail and rein-openweights
both have token_ttl=900, token_max_ttl=1800, token_num_uses=8,
secret_id_ttl=0, secret_id_num_uses=0 and bind_secret_id=true, with no CIDR
binding.
3. Proposed changes
- create policy
workload-kv-read-inbound-library-imap:readonplatform/data/workloads/inbound-library/imapandplatform/metadata/workloads/inbound-library/imap, nothing else. It is new because, per section 2, no existing policy is scoped to this entry, and reusing a Binky policy would widen scope in both directions. - create AppRole
inbound-librarywithtoken_policies = [workload-kv-read-inbound-library-imap],token_ttl=15m,token_max_ttl=30m,token_num_uses=4,secret_id_ttl=0. It is new because no role exists for the consumer. Its shape follows the two workstation precedents. - create the delivery files
/home/worsch/.local/inbound-library/approle/role_idandsecret_id(private, exclusive), at the locationconfig/library.jsoncredential.approle_diralready points to. - unchanged: the KV entry itself, every other policy, role and mount, and the ops-warden catalog. A pointer-only entry is proposed after the build, in ILIB-WP-0003-T05.
4. Review notes (phase 2)
- Naming:
workload-kv-read-<consumer>-<purpose>matches theworkload-kv-read-binky-company-email-imapandworkload-kv-read-rein-openweights-openrouterconvention. The AppRole is named after the consumer, asrein-openweightsis. - TTL: 15m/30m matches both precedents. One run takes seconds.
token_num_uses=4instead of the precedents' 8: the consumer uses exactly two operations per run, the KV read andauth/token/revoke-self. 4 allows one retry and is tighter than the precedent. Below 2 would break revoke-self.secret_id_ttl=0(no expiry) matches the current build-phase precedent, and the builder requires it as an explicit, reviewable choice. Alternative: a bounded secret-id TTL with a rotation job. That is not proposed now because the consumer has no rotation path yet. It is recorded as the residual for a fleet-wide rotation decision, not one made for this lane alone.- CIDR binding: not used. The precedents do not use it, and the workstation
reaches OpenBao through the
openbao-ui-railiance01bridge forward (127.0.0.1:18200), so the source address OpenBao sees is the tunnel endpoint. A binding there would add little. - Redundancy and compaction: nothing to compact. The survey found no near
duplicate on
platform/. - Value handling: ops-mason never reads the IMAP value. Proof follows the
binky-company-email-imapCCR pattern (capabilities-safe): the lane token is checked forreadon the data path, and a default-policy token fordeny, usingsys/capabilities. The consumer's first live run proves the value works (ILIB-WP-0003-T06). - Spec digest:
build_spec_digest(build_spec)=c62765c8a4505821e77b58c5e0daf0511dec39fb569a05e190ff51f5cdc3e2ba. This is a candidate only. Record it asapproved_spec_sha256only on approval.
5. Executive summary (phase 3)
- One-line ask. Let the inbound-library timer on the workstation read the
password for the
library@coulomb.socialmailbox from OpenBao by itself, so no copy of it is kept on disk. - Who/what gets access. One new machine identity, the AppRole
inbound-library, whose login files live only on the workstation. Nothing else gains anything. - To what. Read-only access to exactly one entry: the library mailbox credential. No other entry, mailbox or tenant.
- For how long. Each login gives a token valid for 15 minutes (30 at most), usable for 4 operations, and the library revokes it after each run. The login files do not expire. That is the same build-phase choice as the existing workstation lanes, and their rotation is a separate, fleet-wide decision.
- Blast radius if the credential leaks. Whoever holds the two login files
can read the library mailbox password, and with it read, and through IMAP
change or delete, mail in
library@coulomb.social. That mailbox only receives automation briefs and similar inbound material. It is not a personal or company mailbox, and it gives no access to other OpenBao entries. - Cost to reverse. Fully isolated. Deleting one role and one policy, and removing the two login files, touches no other consumer. The mailbox password can be rotated independently at IONOS.
Decision: approve / reject / send back to phase 1 with what to change.
Decision 2026-10-05: approved by Bernd Worsch in a Claude Code session ("I approve"), for the exact build_spec above (digest c62765c8…e2ba).
6. Build result (phase 4)
Built 2026-10-05 with build_approle_kv_lane under the grant
token-claude-ILIB-WP-0003-T03 (ops-mason-build). The builder reloaded the plan
and matched approval and digest c62765c8…e2ba before any OpenBao command. It
added a metadata-only record to audit/build-log.jsonl.
- created policy
workload-kv-read-inbound-library-imap:readonplatform/data/workloads/inbound-library/imapandplatform/metadata/workloads/inbound-library/imap, and nothing else. Confirmed withbao policy read. - created AppRole
inbound-library:token_policies=[workload-kv-read-inbound-library-imap],token_ttl=900,token_max_ttl=1800,token_num_uses=4,secret_id_ttl=0,bind_secret_id=true. Confirmed withbao read. - delivered
role_idandsecret_idto/home/worsch/.local/inbound-library/approle/(directory 0700, files 0600). The values were not printed or read by the agent.
Capabilities-safe verification:
- Negative: a
default-only token (2 minutes, 2 uses, then revoked) getsdenyon the data path. - Positive: proven by the consumer, not by ops-mason, which reads no values.
The first systemd run of
inbound-library.service(2026-10-05 16:09 CEST) did the AppRole login, read the entry, revoked its token, and scannedlibrary@coulomb.socialread-only. It retained 2 messages. No secret appeared in the journal.
The ops-warden catalog entry is proposed under inbound-library ILIB-WP-0003-T05. No paste-once step remains: the value (version 2) was supplied by the founder before the build.