WARDEN-WP-0028: tenant secrets on mount tenants/ (first lane draft)
Adopt tenants/<tenant>/… custody (not platform/workloads). Document onboarding, add draft binky-company-email-imap catalog entry, and mark T01–T04/T06–T07 done. Founder Red provision remains T05.
This commit is contained in:
parent
b971403dad
commit
98a2339b81
7 changed files with 634 additions and 2 deletions
|
|
@ -390,6 +390,37 @@ entries:
|
|||
- "Confirm the new PAT works (fetch --field API_TOKEN into env, call /api/v1/user — never print it), then delete the old token in Forgejo."
|
||||
- "Verify capabilities-safe on the data path (`bao token capabilities`); notify consumers (package-prune, operator-bootstrap, npm-smoke, reuse-webhook) to re-fetch."
|
||||
|
||||
# --- tenant commercial secrets (mount tenants/; WARDEN-WP-0028) ---
|
||||
|
||||
- id: binky-company-email-imap
|
||||
title: Binky company email IMAP credentials
|
||||
need_keywords: [binky, company, email, imap, mailbox, binky-hedgehog, mail, company-email]
|
||||
owner_repo: railiance-platform
|
||||
subsystem: OpenBao + email-connect
|
||||
warden_executes: false
|
||||
wiki_ref: wiki/playbooks/binky-company-email-imap.md#worker-checklist
|
||||
canon_ref: railiance-platform/docs/workload-kv-access-lanes.md
|
||||
reviewed: "2026-07-17"
|
||||
status: draft
|
||||
risk: high
|
||||
# CCR-2026-0007: tenants/ mount + policy + OIDC role applied 2026-07-17.
|
||||
# Values: founder Red provision pending. Promote to active after provision +
|
||||
# capabilities-safe verify. Sibling non-secret host/port in email-connect config.
|
||||
auth_method: "caller's own OpenBao token (OIDC netkingdom role binky-company-email-imap-workload-kv-read)"
|
||||
path_template: "tenants/binky/company-email/imap"
|
||||
fetch_command: "bao kv get -field=IMAP_PASSWORD tenants/binky/company-email/imap"
|
||||
exec_capable: true
|
||||
lane: secret
|
||||
rotation:
|
||||
method: rotate
|
||||
owner: binky-control
|
||||
automatable: false
|
||||
steps:
|
||||
- "At the mail provider, revoke the old app password / mailbox password and mint a new one (do not paste it into chat or Git)."
|
||||
- "`bao kv put tenants/binky/company-email/imap IMAP_PASSWORD=@file` (and IMAP_USERNAME=@file if the login changed); shred the mode-0600 input file(s)."
|
||||
- "Re-run email-connect read-only scan with warden access --exec / env inject; confirm metadata-only evidence under binky-control/mailmeta/."
|
||||
- "Verify capabilities-safe on tenants/data/binky/company-email/imap; if EXPOSED taint was set, clear custom_metadata after rotation."
|
||||
|
||||
# --- draft: owner path not yet shipped; hidden from default lookup ---
|
||||
|
||||
- id: object-storage-sts
|
||||
|
|
|
|||
|
|
@ -102,12 +102,16 @@ run the owner's tool as the caller and preserve owner custody.
|
|||
|
||||
Promotion criteria: `wiki/playbooks/catalog-lane-promotion.md`.
|
||||
High-risk / agent boundary: `wiki/playbooks/agent-read-boundary.md`.
|
||||
EXPOSED taint: `wiki/playbooks/exposed-taint.md` · `warden taint <id>`.
|
||||
EXPOSED taint: `wiki/playbooks/exposed-taint.md` · `warden taint <id>`.
|
||||
**Tenant secrets:** mount `tenants/` — path `tenants/<tenant>/<workload>/<bundle>`
|
||||
(see `wiki/playbooks/tenant-secret-onboarding.md`, WARDEN-WP-0028). Not under
|
||||
`platform/workloads/`.
|
||||
|
||||
**Draft** (hidden from default lookup until owner path ships — `warden route list --all`):
|
||||
|
||||
| Catalog `id` | Routing focus | Playbook |
|
||||
| --- | --- | --- |
|
||||
| `binky-company-email-imap` | Tenant IMAP creds on `tenants/binky/…` (provision pending) | `wiki/playbooks/binky-company-email-imap.md` |
|
||||
| `object-storage-sts` | NK-WP-0007 STS vending path | `wiki/playbooks/object-storage-sts.md` |
|
||||
| `database-dynamic-credentials` | OpenBao database secrets engine | `wiki/playbooks/database-dynamic-credentials.md` |
|
||||
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ operator identity.
|
|||
| `railiance-backup-offsite-lane` | Nextcloud upload + `AGE_PRIVATE_KEY` recovery escrow |
|
||||
| `forgejo-admin-api-token` | Forgejo site-admin PAT |
|
||||
| `openrouter-llm-connect` | Provider key (spend + prompt-adjacent) |
|
||||
| `binky-company-email-imap` | Tenant mailbox IMAP password (`tenants/binky/…`) |
|
||||
|
||||
Keep this table in sync with `risk: high` rows in `registry/routing/catalog.yaml`
|
||||
and path denials in `agent-high-risk-boundary.hcl`.
|
||||
|
|
|
|||
103
wiki/playbooks/binky-company-email-imap.md
Normal file
103
wiki/playbooks/binky-company-email-imap.md
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
# Binky company email IMAP
|
||||
|
||||
Date: 2026-07-17
|
||||
Catalog: `binky-company-email-imap` (status `draft` until founder provisions)
|
||||
Owner: `railiance-platform` (CCR-2026-0007) · consumer need: `binky-control`
|
||||
Workplan: WARDEN-WP-0028
|
||||
|
||||
IMAP credentials for the company mailbox (founder address) so email-connect can
|
||||
run **read-only** scans for control-plane event intake.
|
||||
|
||||
---
|
||||
|
||||
## OpenBao pointers
|
||||
|
||||
| Field | Value |
|
||||
| --- | --- |
|
||||
| Mount | `tenants` |
|
||||
| Path | `tenants/binky/company-email/imap` |
|
||||
| Fields | `IMAP_USERNAME`, `IMAP_PASSWORD` |
|
||||
| Policy | `workload-kv-read-binky-company-email-imap` |
|
||||
| OIDC role | `binky-company-email-imap-workload-kv-read` (`groups=net-kingdom-admins`) |
|
||||
| Risk | `high` |
|
||||
|
||||
Non-secret connection facts (IMAP host/port) live in email-connect / binky-control
|
||||
config as host + env **names**, not passwords.
|
||||
|
||||
---
|
||||
|
||||
## Worker checklist
|
||||
|
||||
1. Login as caller:
|
||||
|
||||
```bash
|
||||
bao login -method=oidc -path=netkingdom role=binky-company-email-imap-workload-kv-read
|
||||
```
|
||||
|
||||
2. Fetch via sanctioned transport (never paste into chat):
|
||||
|
||||
```bash
|
||||
warden access binky-company-email-imap --all --no-policy --out /tmp/imap.user
|
||||
# primary field is IMAP_USERNAME; for password use --field after template support
|
||||
# or:
|
||||
warden access binky-company-email-imap --all --no-policy --exec -- \
|
||||
env IMAP_USERNAME=… # prefer secrets-engine / dual-field exec when wired
|
||||
```
|
||||
|
||||
Until catalog is `active` and resolvable, use bao as caller with files:
|
||||
|
||||
```bash
|
||||
bao kv get -field=IMAP_USERNAME tenants/binky/company-email/imap > /tmp/u
|
||||
bao kv get -field=IMAP_PASSWORD tenants/binky/company-email/imap > /tmp/p
|
||||
chmod 600 /tmp/u /tmp/p
|
||||
```
|
||||
|
||||
3. Run email-connect read-only scan (config uses env names only).
|
||||
|
||||
4. Store **metadata-only** evidence under `binky-control/mailmeta/`.
|
||||
|
||||
Agents (`WARDEN_AGENT_ID` set): raw value stream refused (exit 7). Use `--out` /
|
||||
`--exec` / `--wrap` / `--fingerprint`.
|
||||
|
||||
---
|
||||
|
||||
## Verify (capabilities-safe)
|
||||
|
||||
```bash
|
||||
LANE=$(bao token create -policy=workload-kv-read-binky-company-email-imap -ttl=2m -field=token)
|
||||
bao token capabilities "$LANE" tenants/data/binky/company-email/imap # read
|
||||
bao token revoke "$LANE"
|
||||
|
||||
DEFAULT=$(bao token create -policy=default -ttl=2m -field=token) # deny of create is also pass
|
||||
bao token capabilities "$DEFAULT" tenants/data/binky/company-email/imap # deny
|
||||
bao token revoke "$DEFAULT"
|
||||
```
|
||||
|
||||
Never use `bao kv get` for deny tests.
|
||||
|
||||
---
|
||||
|
||||
## Founder provision (Red lane)
|
||||
|
||||
```bash
|
||||
# values only in mode-0600 files, never argv/chat
|
||||
bao kv put tenants/binky/company-email/imap \
|
||||
IMAP_USERNAME=@/path/to/user.file \
|
||||
IMAP_PASSWORD=@/path/to/pass.file
|
||||
shred -u /path/to/user.file /path/to/pass.file
|
||||
```
|
||||
|
||||
Then re-verify capabilities, promote catalog to `active`, set CCR
|
||||
`access_frontdoor.resolvable: true` / `readiness: ready`.
|
||||
|
||||
## Rotation
|
||||
|
||||
```bash
|
||||
warden rotate-guide binky-company-email-imap
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- `binky-control/integrations/company-email-openbao.md`
|
||||
- `wiki/playbooks/tenant-secret-onboarding.md`
|
||||
- CCR-2026-0007 in railiance-platform
|
||||
|
|
@ -87,10 +87,11 @@ out on the OpenBao-delivered value, positive + negative verification audit-logge
|
|||
|
||||
---
|
||||
|
||||
## Draft lanes (2026-07-16)
|
||||
## Draft lanes (2026-07-17)
|
||||
|
||||
| Catalog `id` | Blocker |
|
||||
| --- | --- |
|
||||
| `binky-company-email-imap` | Founder Red provision of IMAP values (CCR-2026-0007 applied; mount/policy/role live) |
|
||||
| `object-storage-sts` | NK-WP-0007 vending path not production-exercised |
|
||||
| `database-dynamic-credentials` | OpenBao database engine role paths TBD per workload |
|
||||
|
||||
|
|
@ -98,6 +99,9 @@ out on the OpenBao-delivered value, positive + negative verification audit-logge
|
|||
capabilities-safe re-verify (WP-0026 T07); primary field `NC_WEBDAV_TOKEN`;
|
||||
`risk: high` + EXPOSED taint on version 2 (operator may rotate optionally).
|
||||
|
||||
**Tenant path (WP-0028):** new client secrets use mount `tenants/`, not
|
||||
`platform/workloads/`. See `wiki/playbooks/tenant-secret-onboarding.md`.
|
||||
|
||||
Re-run promotion when the owning repo closes the blocker; do not promote on
|
||||
playbook prose alone.
|
||||
|
||||
|
|
|
|||
81
wiki/playbooks/tenant-secret-onboarding.md
Normal file
81
wiki/playbooks/tenant-secret-onboarding.md
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
# Tenant secret onboarding
|
||||
|
||||
Date: 2026-07-17
|
||||
Workplan: WARDEN-WP-0028
|
||||
|
||||
How to add a **client/tenant** commercial secret to NetKingdom OpenBao so
|
||||
ops-warden can route it and consumers can use it without pasting values into
|
||||
Git, State Hub, or chat.
|
||||
|
||||
---
|
||||
|
||||
## Path convention
|
||||
|
||||
```text
|
||||
mount: tenants # dedicated KV v2 mount (not platform/)
|
||||
path: tenants/<tenant>/<workload>/<bundle>
|
||||
```
|
||||
|
||||
| Segment | Meaning | Example |
|
||||
| --- | --- | --- |
|
||||
| `tenant` | Stable client slug | `binky` |
|
||||
| `workload` | Capability / system | `company-email` |
|
||||
| `bundle` | One purpose / one CCR | `imap` |
|
||||
|
||||
**Do not** put new client secrets under `platform/workloads/…` (fleet/platform
|
||||
services) or invent `secret/prod/…` as the production home.
|
||||
|
||||
First worked lane: `tenants/binky/company-email/imap` (CCR-2026-0007).
|
||||
|
||||
---
|
||||
|
||||
## Ownership
|
||||
|
||||
| Step | Owner |
|
||||
| --- | --- |
|
||||
| Business need, non-secret host facts, consumer config | Tenant control repo (e.g. `binky-control`) |
|
||||
| CCR, policy HCL, OIDC role, mount hygiene | `railiance-platform` |
|
||||
| Catalog front door + playbook + rotation guide | `ops-warden` |
|
||||
| Value provision (Red) | Human founder/operator |
|
||||
| Optional exec wrapper | `secrets-engine` (same path only) |
|
||||
|
||||
---
|
||||
|
||||
## Checklist
|
||||
|
||||
1. **Slug + fields** — pick `tenant`/`workload`/`bundle` and field names (no values).
|
||||
2. **CCR** — `railiance-platform/credential-change-requests/CCR-YYYY-NNNN-….yaml`
|
||||
- `openbao.mount: tenants`
|
||||
- `openbao.kv_path: tenants/<tenant>/<workload>/<bundle>`
|
||||
- `policy_name` starts with `workload-kv-read-`
|
||||
- OIDC role ends with `-workload-kv-read`
|
||||
- `risk` high for mailbox/admin/recovery-class secrets
|
||||
3. **Policy file** — exact `tenants/data/…` + `tenants/metadata/…` read only.
|
||||
4. **Applier dry-run** — `scripts/credential-change.py applier-dry-run <CCR>`
|
||||
5. **Approve + apply metadata** — policy + OIDC role; **no** secret write in apply.
|
||||
6. **ops-warden catalog** — draft entry with `risk: high`, rotation block, concrete
|
||||
`fetch_command` for primary field; playbook under `wiki/playbooks/`.
|
||||
7. **Founder provision** — `bao kv put tenants/… FIELD=@file` (mode 0600 file) or
|
||||
secrets-engine provision; never chat/Git.
|
||||
8. **Verify capabilities-safe** — `bao token capabilities` allow/deny; never
|
||||
`kv get` for deny tests (WP-0026).
|
||||
9. **Promote** catalog `draft` → `active` when resolvable; update CCR readiness.
|
||||
10. **Agent boundary** — add exact data `deny` + metadata `read` to
|
||||
`agent-high-risk-boundary` for high-risk lanes.
|
||||
|
||||
---
|
||||
|
||||
## Worker fetch (after active)
|
||||
|
||||
```bash
|
||||
bao login -method=oidc -path=netkingdom role=<lane>-workload-kv-read
|
||||
warden access <catalog-id> --no-policy --out /tmp/secret.file # mode 0600
|
||||
# agents: never raw --fetch stream; WARDEN_AGENT_ID + risk=high → exit 7
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- `wiki/playbooks/binky-company-email-imap.md`
|
||||
- `wiki/playbooks/catalog-lane-promotion.md`
|
||||
- `wiki/playbooks/agent-read-boundary.md`
|
||||
- `railiance-platform/docs/credential-change-approval.md`
|
||||
408
workplans/WARDEN-WP-0028-tenant-secret-custody.md
Normal file
408
workplans/WARDEN-WP-0028-tenant-secret-custody.md
Normal file
|
|
@ -0,0 +1,408 @@
|
|||
---
|
||||
id: WARDEN-WP-0028
|
||||
type: workplan
|
||||
title: "Tenant secret custody — NetKingdom pattern for client/tenant secrets"
|
||||
domain: infotech
|
||||
repo: ops-warden
|
||||
status: active
|
||||
owner: grok
|
||||
topic_slug: custodian
|
||||
planning_priority: high
|
||||
planning_order: 28
|
||||
created: "2026-07-16"
|
||||
updated: "2026-07-17"
|
||||
---
|
||||
|
||||
# WARDEN-WP-0028 — Tenant secret custody (NetKingdom pattern)
|
||||
|
||||
## Origin
|
||||
|
||||
Founder establishing **binky-control** as the control-plane repo for a Coulomb
|
||||
client/tenant (Binky-Hedgehog GmbH). Immediate need: store **tenant-specific**
|
||||
secrets in OpenBao (e.g. company mailbox usernames/passwords for IMAP scan) and
|
||||
consume them safely via email-connect / agents **without** putting values in
|
||||
git, State Hub, chat, or workplans.
|
||||
|
||||
BINKY-WP-0003-T01 prepared a draft plan
|
||||
(`binky-control/integrations/company-email-openbao.md`) that targeted
|
||||
`secret/prod/binky/...` via secrets-engine. That draft is directionally right on
|
||||
*workflow*, but it does not match a production-ready custody layout on
|
||||
`bao.coulomb.social`. This workplan resolves the NetKingdom layout and makes
|
||||
**tenant** secrets first-class — **separate from** platform workload lanes
|
||||
(`platform/workloads/...`).
|
||||
|
||||
## Goal
|
||||
|
||||
Define and land a **repeatable, multi-tenant-safe** pattern for:
|
||||
|
||||
1. Where tenant secrets live in OpenBao (path + mount convention)
|
||||
2. Who owns policy, CCR, catalog, and front door
|
||||
3. How agents and workloads obtain values (sanctioned transports only)
|
||||
4. First concrete lane: **binky company email IMAP** (unblocks real-world event
|
||||
intake for binky-control)
|
||||
|
||||
## Architectural facts (as of 2026-07-16)
|
||||
|
||||
### Planes (ADR-0006)
|
||||
|
||||
| Plane | Role | Example |
|
||||
| --- | --- | --- |
|
||||
| Bootstrap | First trust / recovery | unseal, platform-root |
|
||||
| Platform control | Shared identity, secrets, authz | OpenBao, key-cape, flex-auth, railiance-platform |
|
||||
| Tenant | Scoped client/workload authority | coulomb products, **binky**, future clients |
|
||||
|
||||
**Binky is a tenant**, not platform-root. Tenant secrets must not require
|
||||
platform-admin for day-to-day read, and tenant operators must not gain
|
||||
platform-root grants.
|
||||
|
||||
### OpenBao mounts (live + planned)
|
||||
|
||||
| Mount | Type | Role |
|
||||
| --- | --- | --- |
|
||||
| **`platform/`** | KV v2 | **Platform control-plane** secrets: `platform/workloads/...` (fleet CCRs, ESO) and `platform/operators/...` — *not* client/tenant commercial secrets |
|
||||
| **`secret/`** | KV v2 | secrets-engine stage / pilot paths (`secret/coulomb/...`) — not the tenant home |
|
||||
| **`tenants/`** | KV v2 (**new**) | **Client/tenant commercial secrets** — explicit multi-tenant namespace |
|
||||
|
||||
Live under `platform/workloads/` today: `activity-core/`, `coulomb/`, `forgejo/`,
|
||||
`issue-core/`, `railiance/`, `reuse/`. Those stay where they are; we do **not**
|
||||
migrate them into `tenants/` in this workplan.
|
||||
|
||||
### Dual front-door systems (both real; don't invent a third)
|
||||
|
||||
| Layer | Owns | Entry |
|
||||
| --- | --- | --- |
|
||||
| **OpenBao** | Custody, ACL, audit | `bao` CLI / API |
|
||||
| **railiance-platform CCR** | Approved metadata apply (policy + OIDC role + evidence) | `credential-change-requests/CCR-*.yaml` |
|
||||
| **ops-warden catalog** | Routing + `warden access` proxy (no custody) | `registry/routing/catalog.yaml` |
|
||||
| **secrets-engine catalog** | Workflow: decision → plan → provision → exec delivery | `secrets-engine/catalog/*.yaml` |
|
||||
|
||||
**Binding rule (ops-warden):** never vend secret values; only route/proxy as the
|
||||
caller. **Binding rule (WP-0026):** capabilities-safe verify; agents on
|
||||
`risk: high` use `--out` / `--exec` / `--wrap` only.
|
||||
|
||||
### Why not bury tenants under `platform/workloads/`
|
||||
|
||||
Earlier draft of this workplan used `platform/workloads/<tenant>/...`. Founder
|
||||
prefer a **shallower, explicit** namespace. That is sound:
|
||||
|
||||
- **No conflict with Vault/OpenBao best practice.** Separate mounts (or
|
||||
top-level prefixes) for different security *domains* are common and good.
|
||||
`platform/workloads` is an *internal fleet convention* for platform services,
|
||||
not an industry mandate for every secret.
|
||||
- **Clearer mental model:** platform plane vs tenant plane (ADR-0006) maps to
|
||||
mount/prefix names operators can see at a glance.
|
||||
- **Isolation:** ACLs, list boundaries, and future audit queries stay simpler
|
||||
when tenant material is not mixed under the same path tree as forgejo/ESO
|
||||
lanes.
|
||||
|
||||
### Decision (2026-07-17) — use dedicated mount `tenants/`
|
||||
|
||||
```text
|
||||
mount: tenants # KV v2, enable once (platform-admin)
|
||||
logical path: <tenant_slug>/<workload>/<bundle>
|
||||
CLI: tenants/<tenant_slug>/<workload>/<bundle>
|
||||
API data path: tenants/data/<tenant_slug>/<workload>/<bundle>
|
||||
```
|
||||
|
||||
**Rejected alternatives (kept for archaeology only):**
|
||||
|
||||
| Option | Why not |
|
||||
| --- | --- |
|
||||
| `secret/prod/binky/...` | `secret/prod/` does not exist; stage model not ready as sole production path |
|
||||
| `platform/workloads/binky/...` | Too deep; conflates tenant clients with platform workloads |
|
||||
| `platform/tenants/binky/...` | Acceptable fallback if enabling a new mount is blocked; prefer full `tenants/` mount |
|
||||
|
||||
### Tooling debt to clear (not a reason to keep the deep path)
|
||||
|
||||
`railiance-platform/scripts/credential-change.py` currently hard-fails unless:
|
||||
|
||||
- `openbao.mount == "platform"`
|
||||
- `openbao.kv_path` starts with `platform/workloads/`
|
||||
|
||||
That guard must be **extended** to allow `mount: tenants` and
|
||||
`kv_path` under `tenants/` (T03). It is an implementation constraint, not a
|
||||
security reason to force the deep path.
|
||||
|
||||
## Recommended path convention (tenant secrets)
|
||||
|
||||
```text
|
||||
tenants/<tenant_slug>/<workload>/<bundle>
|
||||
```
|
||||
|
||||
| Segment | Rules | Binky email example |
|
||||
| --- | --- | --- |
|
||||
| `tenant_slug` | Stable kebab slug; company/client id | `binky` |
|
||||
| `workload` | Capability or system using the secret | `company-email` |
|
||||
| `bundle` | Single purpose unit (one CCR / one policy) | `imap` |
|
||||
|
||||
Full CLI path:
|
||||
|
||||
```text
|
||||
tenants/binky/company-email/imap
|
||||
```
|
||||
|
||||
Suggested fields (names only — never values in git):
|
||||
|
||||
| Field | Purpose |
|
||||
| --- | --- |
|
||||
| `IMAP_USERNAME` | mailbox login (often the address) |
|
||||
| `IMAP_PASSWORD` | app password / mailbox password |
|
||||
| `IMAP_HOST` | optional if not fixed in consumer config |
|
||||
| `IMAP_PORT` | optional (default 993 in config) |
|
||||
|
||||
Non-secret connection facts (provider host if stable) may live in
|
||||
`binky-control` config / email-connect yaml as **env names + host**, not
|
||||
passwords.
|
||||
|
||||
### Future multi-mailbox / multi-tenant
|
||||
|
||||
```text
|
||||
tenants/<tenant>/mailbox/<mailbox_id>
|
||||
# e.g. tenants/binky/mailbox/founder-primary
|
||||
# tenants/acme/mailbox/billing
|
||||
```
|
||||
|
||||
One CCR + one least-privilege policy **per bundle** (or per mailbox). Do not
|
||||
grant `tenants/binky/*` list/read to agents — exact-path policies only.
|
||||
|
||||
### Risk class
|
||||
|
||||
Mailbox credentials and similar client secrets are **`risk: high`** (WP-0026
|
||||
T04): agent identities get metadata/capabilities only; raw stream refused when
|
||||
`WARDEN_AGENT_ID` is set.
|
||||
|
||||
## Ownership split
|
||||
|
||||
| Concern | Owner repo | Artifact |
|
||||
| --- | --- | --- |
|
||||
| Tenant business need, IMAP host facts, scan config, queues | **binky-control** | `integrations/*`, mailmeta (metadata only), activity defs |
|
||||
| OpenBao path, policy HCL, OIDC role, CCR lifecycle | **railiance-platform** | `CCR-YYYY-NNNN-…yaml`, `openbao/policies/…` |
|
||||
| Routing front door + rotation guidance | **ops-warden** | catalog entry, playbook, `warden access` / `rotate-guide` |
|
||||
| Exec-time delivery workflow (optional same path) | **secrets-engine** | catalog entry pointing at `tenants/...` (same path; no second copy) |
|
||||
| Identity groups for tenant operators | **net-kingdom / key-cape** | group e.g. `tenant-binky-operators` (near-term may use `net-kingdom-admins` for founder-only) |
|
||||
| Mailbox scan consumer | **email-connect** | config with `username_env` / `password_env` only |
|
||||
|
||||
## Auth model (near-term vs target)
|
||||
|
||||
**Near-term (founder dogfood):** OIDC `netkingdom` role bound to
|
||||
`groups=net-kingdom-admins` (same pattern as backup/forgejo lanes). Acceptable
|
||||
while binky is founder-operated only.
|
||||
|
||||
**Target (real multi-tenant):**
|
||||
|
||||
- IAM group `tenant-binky-operators` (and later per-tenant groups)
|
||||
- OpenBao OIDC role `binky-company-email-imap-workload-kv-read` bound only to
|
||||
that group
|
||||
- flex-auth check `secret.read:binky-company-email` if pre-approval required
|
||||
- Agents **never** hold the workload-kv-read policy; use operator wrap / exec
|
||||
injection / AppRole for email-connect scanner only
|
||||
|
||||
## First worked lane (deliverable sketch)
|
||||
|
||||
| Item | Value |
|
||||
| --- | --- |
|
||||
| Catalog id | `binky-company-email-imap` |
|
||||
| CCR | `CCR-2026-0007` (next free) — title: Binky company email IMAP |
|
||||
| Mount | `tenants` |
|
||||
| KV path | `tenants/binky/company-email/imap` |
|
||||
| Policy | `workload-kv-read-binky-company-email-imap` |
|
||||
| OIDC role | `binky-company-email-imap-workload-kv-read` |
|
||||
| Consumer | email-connect `scan-mailbox` via env inject |
|
||||
| Front door | `warden access binky-company-email-imap --out FILE` or `--exec -- …` |
|
||||
| Provision | **Red lane, founder once** — value via `bao kv put … @file` or secrets-engine provision; never chat |
|
||||
|
||||
## Out of scope
|
||||
|
||||
- Mass rotation / lockdown machinery (WARDEN-WP-0027 Strand B)
|
||||
- Sending mail / SMTP from company address (separate lane if needed)
|
||||
- Migrating all `secret/` mount lanes onto `platform/` (separate secrets-engine
|
||||
/ railiance work)
|
||||
- Full key-cape tenant group productization (may be a net-kingdom follow-on)
|
||||
|
||||
## Tasks
|
||||
|
||||
### T01 — Canon note: tenant secret path + ownership
|
||||
|
||||
```task
|
||||
id: WARDEN-WP-0028-T01
|
||||
status: done
|
||||
priority: high
|
||||
```
|
||||
|
||||
Done 2026-07-17: `wiki/playbooks/tenant-secret-onboarding.md` + CredentialRouting
|
||||
tenant paragraph; catalog-lane-promotion draft table updated.
|
||||
|
||||
Land a short ops-warden wiki page (and one paragraph in
|
||||
`wiki/CredentialRouting.md`) that freezes:
|
||||
|
||||
- mount **`tenants`** + path `tenants/<tenant>/<workload>/<bundle>`
|
||||
- ownership table (binky-control / railiance-platform / ops-warden / secrets-engine)
|
||||
- link to WP-0026 hygiene (capabilities verify, high-risk, taint)
|
||||
- **do not** put new client/tenant commercial secrets under
|
||||
`platform/workloads/` or invent `secret/prod/...` as the production home
|
||||
|
||||
**Done when:** wiki + CredentialRouting pointer exist; binky integration doc
|
||||
updated to match (or superseded with link).
|
||||
|
||||
### T02 — Align binky-control integration plan to production path
|
||||
|
||||
```task
|
||||
id: WARDEN-WP-0028-T02
|
||||
status: done
|
||||
priority: high
|
||||
```
|
||||
|
||||
Done 2026-07-17: `binky-control/integrations/company-email-openbao.md` rewritten
|
||||
to `tenants/binky/company-email/imap` + checklist (host confirm + Red provision
|
||||
still open).
|
||||
|
||||
**Cross-repo: binky-control.** Rewrite
|
||||
`integrations/company-email-openbao.md` to use
|
||||
`tenants/binky/company-email/imap`, CCR + ops-warden front door as primary,
|
||||
secrets-engine as optional exec wrapper on the same path. Confirm non-secret
|
||||
IMAP host/port with founder (Blue). Keep Red-lane value provision as the last
|
||||
step.
|
||||
|
||||
**Done when:** binky doc matches this workplan; checklist items re-opened for
|
||||
implementation (T01 in BINKY was "prepared only").
|
||||
|
||||
### T03 — Enable `tenants` mount + extend CCR tooling + policy/role
|
||||
|
||||
```task
|
||||
id: WARDEN-WP-0028-T03
|
||||
status: done
|
||||
priority: high
|
||||
```
|
||||
|
||||
Done 2026-07-17: `tenants/` KV v2 mount live; CCR applier allowlist extended;
|
||||
CCR-2026-0007 + policy + OIDC role applied; lane-policy `read` / default
|
||||
`deny` / agent boundary on tenants path; `docs/workload-kv-access-lanes.md`
|
||||
tenant section.
|
||||
|
||||
**Cross-repo: railiance-platform (+ OpenBao admin once).**
|
||||
|
||||
1. **Enable KV v2 mount** `tenants/` on `bao.coulomb.social` (platform-admin /
|
||||
attended; record non-secret evidence). Versioning + max versions policy
|
||||
aligned with `platform` if practical.
|
||||
2. **Extend** `scripts/credential-change.py` allowlist: `mount: tenants` and
|
||||
`kv_path` under `tenants/` (keep existing `platform/workloads/` rules
|
||||
intact for fleet lanes). Update dry-run docs and tests.
|
||||
3. Author `CCR-2026-0007` (or next id) for the binky IMAP path; policy HCL on
|
||||
exact data/metadata paths under `tenants/…`; OIDC role (founder group
|
||||
near-term). Dry-run; founder approves; apply metadata only.
|
||||
4. Document fields without values; add a **tenant lanes** section to
|
||||
`docs/workload-kv-access-lanes.md` (or sibling doc).
|
||||
|
||||
**Done when:** mount exists; CCR tool accepts tenant paths; CCR approved +
|
||||
policy/role applied; capabilities-safe positive/negative evidence (no value
|
||||
reads for verify).
|
||||
|
||||
### T04 — ops-warden catalog + playbook + rotation
|
||||
|
||||
```task
|
||||
id: WARDEN-WP-0028-T04
|
||||
status: done
|
||||
priority: high
|
||||
```
|
||||
|
||||
Done 2026-07-17: catalog draft `binky-company-email-imap` (`risk: high`,
|
||||
rotation, path `tenants/binky/...`); playbooks
|
||||
`binky-company-email-imap.md` + onboarding. Promote to active after T05.
|
||||
|
||||
Add `binky-company-email-imap` to `registry/routing/catalog.yaml`:
|
||||
`status: draft` until verify, then promote; `risk: high`; `exec_capable: true`;
|
||||
concrete `fetch_command` for primary field; `rotation:` block (re-establish or
|
||||
rotate mailbox app-password). Playbook under `wiki/playbooks/`. Scorecard
|
||||
rotation coverage satisfied on promote.
|
||||
|
||||
**Done when:** `warden route show binky-company-email-imap --json` shows
|
||||
expected pointers; after T03+T05, `resolvable: true`.
|
||||
|
||||
### T05 — Founder provision (Red) + first scan evidence
|
||||
|
||||
```task
|
||||
id: WARDEN-WP-0028-T05
|
||||
status: wait
|
||||
priority: high
|
||||
```
|
||||
|
||||
**Human-only value path.** Founder provisions IMAP username/password into
|
||||
OpenBao via approved tool (`bao kv put … @file` or secrets-engine provision).
|
||||
Agents never see the value. Run email-connect read-only scan with
|
||||
`warden access … --exec` or secrets-engine exec; store **metadata-only**
|
||||
evidence under `binky-control/mailmeta/`.
|
||||
|
||||
**Done when:** one successful read-only scan evidence note exists; lane
|
||||
promoted active/resolvable; CCR verification evidence complete.
|
||||
|
||||
### T06 — Generalize "tenant secret onboarding" playbook
|
||||
|
||||
```task
|
||||
id: WARDEN-WP-0028-T06
|
||||
status: done
|
||||
priority: medium
|
||||
```
|
||||
|
||||
Done 2026-07-17 with T01: `wiki/playbooks/tenant-secret-onboarding.md` +
|
||||
CredentialRouting link.
|
||||
|
||||
From the binky lane, write a reusable playbook:
|
||||
`wiki/playbooks/tenant-secret-onboarding.md` — steps for any new tenant:
|
||||
slug, CCR template fields, policy naming, catalog entry, agent boundary,
|
||||
rotation block, promotion checklist. Intended for the next Coulomb client
|
||||
without redesign.
|
||||
|
||||
**Done when:** playbook exists and is linked from CredentialRouting + first
|
||||
session notes for tenant repos.
|
||||
|
||||
### T07 — secrets-engine alignment decision (record only)
|
||||
|
||||
```task
|
||||
id: WARDEN-WP-0028-T07
|
||||
status: done
|
||||
priority: low
|
||||
```
|
||||
|
||||
Done 2026-07-17: **Decision** — tenant production lanes use `mount: tenants`
|
||||
and the CCR path only; secrets-engine may wrap exec delivery against that same
|
||||
path later but must not store a second copy under `secret/`. Until stage roles
|
||||
gain an approved `tenants/` grant, production path is CCR + ops-warden +
|
||||
caller `bao`.
|
||||
|
||||
Record a short decision: secrets-engine catalog entries for **tenant**
|
||||
production lanes **must** use `mount: tenants` and the same path as the CCR
|
||||
(no second copy under `secret/`). Stage roles may need a later extension to
|
||||
touch `tenants/` under an approved plan — until then, ops-warden + CCR +
|
||||
`bao` as caller are the production path.
|
||||
|
||||
**Done when:** decision note in this workplan or `POST /decisions/` + wiki
|
||||
pointer; no dual-value storage for the IMAP password.
|
||||
|
||||
## Suggested implementation order
|
||||
|
||||
1. T01 canon (unblocks everyone)
|
||||
2. T02 binky doc alignment
|
||||
3. T03 mount + CCR tooling + CCR apply (metadata)
|
||||
4. T04 catalog draft
|
||||
5. T05 founder provision + verify + promote
|
||||
6. T06 generalize playbook
|
||||
7. T07 secrets-engine alignment (can parallel after T01)
|
||||
|
||||
## Acceptance
|
||||
|
||||
- [ ] Documented path convention `tenants/<tenant>/…` used for ≥1 live lane (`binky`)
|
||||
- [ ] CCR applier accepts `tenants/` without weakening `platform/workloads/` rules
|
||||
- [ ] No secret values in any git/State Hub artifact
|
||||
- [ ] Lane is capabilities-verified and agent high-risk safe
|
||||
- [ ] email-connect can scan with env injection without printing password
|
||||
- [ ] Next tenant can copy the onboarding playbook without redesign
|
||||
|
||||
## References
|
||||
|
||||
- ADR-0006 recursive multi-tenant identity (`net-kingdom/docs/adr/…`)
|
||||
- `net-kingdom/docs/secrets-engine-security-infrastructure-boundary.md`
|
||||
- `railiance-platform/docs/workload-kv-access-lanes.md`
|
||||
- `railiance-platform/docs/credential-change-approval.md`
|
||||
- `binky-control/integrations/company-email-openbao.md` (draft to align)
|
||||
- WARDEN-WP-0026 disclosure hygiene (capabilities, high-risk, taint, rotation)
|
||||
- WARDEN-WP-0027 Strand B (out of scope here)
|
||||
Loading…
Add table
Add a link
Reference in a new issue