docs: preserve reviewed NetKingdom session artifacts
This commit is contained in:
parent
daeba9eeab
commit
24ae04fa80
50 changed files with 8375 additions and 0 deletions
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"root_review_candidates": [
|
||||
"glas-harness-driver-reference.patch",
|
||||
"sand-boxer-current-driver-reference.patch"
|
||||
],
|
||||
"superseded_broad_sand_proposal": "sand-boxer-driver-reference.patch is retained but not recommended; it also changed historical prose references. Use the current-only patch instead.",
|
||||
"preserve_historical_sand_prose": true,
|
||||
"canonical_driver_edits_performed": false,
|
||||
"frozen_receipts_unchanged": true
|
||||
}
|
||||
|
|
@ -0,0 +1,450 @@
|
|||
---
|
||||
id: GLAS-WP-0012
|
||||
type: workplan
|
||||
title: "Prove the first local rein profile end to end"
|
||||
domain: infotech
|
||||
repo: glas-harness
|
||||
status: blocked
|
||||
flavor: implementation
|
||||
owner: codex
|
||||
topic_slug: first-local-profile-production-proof
|
||||
created: "2026-09-05"
|
||||
updated: "2026-09-27"
|
||||
state_hub_workstream_id: "170bf1ae-337f-5553-8d1e-03b07100e08f"
|
||||
---
|
||||
|
||||
# Prove the first local rein profile end to end
|
||||
|
||||
## Context and scope
|
||||
|
||||
Follow up the Claude-route portion of live residual `GLAS-IN-0002`, originating
|
||||
in finished `GLAS-WP-0005`. Boundary hardening is committed at `63a7f9f` and
|
||||
proved with a deterministic non-secret dispatch. That proof did not execute
|
||||
the real rein CLI or make a model call.
|
||||
|
||||
Start from `harness.agent-dev-local@1.0.0` (rein-aharness,
|
||||
`claude-sonnet-4-6`, `claude-code-cli`, `green-commit-only`). Review a new
|
||||
versioned candidate after the owner runtime contract is available. Keep the
|
||||
existing blocked selection as the rollback pin. The open-weight profile and
|
||||
remote profile do not acquire readiness from this workplan.
|
||||
|
||||
Glas owns profile selection, its adapter, and acceptance evidence. Sand-boxer
|
||||
owns runtime provisioning, execution environment, isolation, egress enforcement,
|
||||
and teardown. The selected rein and credential owner own authentication and
|
||||
credential acquisition. Use existing owner work `SAND-WP-0014-T05`; its current
|
||||
OpenRouter dependency must be distinguished from this first Claude-route proof.
|
||||
|
||||
## Establish the exact local baseline and acceptance requirements
|
||||
|
||||
```task
|
||||
id: GLAS-WP-0012-T01
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "711aeb7e-4664-5078-8c65-3fe6f46f7999"
|
||||
```
|
||||
|
||||
Completed 2026-09-05. Reviewed Glas `63a7f9f`, sand-boxer `b6655d8`, and
|
||||
rein-aharness `1429db5`. Added a reproducible non-secret owner-execution runtime
|
||||
inventory and the [acceptance contract](../docs/local-profile-acceptance.md).
|
||||
|
||||
The baseline can discover Python, Git, and a `claude` command. It cannot
|
||||
discover `rein-aharness`, its Python module, or `llm_connect`. Command discovery
|
||||
alone does not prove that Claude can start or authenticate. The namespace has
|
||||
only loopback; declared egress and credential route references are empty.
|
||||
Owner execution and workspace teardown pass. The baseline is therefore
|
||||
insufficient for a real rein run.
|
||||
|
||||
Reviewed the plan against current implementation. Local preparation is done;
|
||||
remaining execution waits on the owner inputs in T02.
|
||||
|
||||
## Resolve and verify the owner runtime, credential, and egress contract
|
||||
|
||||
```task
|
||||
id: GLAS-WP-0012-T02
|
||||
status: wait
|
||||
depends_on: [SAND-WP-0014-T05]
|
||||
blocking_reason: "Dependency: sand-boxer SAND-WP-0014-T05 with rein-aharness and its credential owner."
|
||||
priority: high
|
||||
state_hub_task_id: "4209f564-a9ee-57f9-9522-3708988801ef"
|
||||
```
|
||||
|
||||
Dependency: sand-boxer `SAND-WP-0014-T05`, with rein-aharness and its credential
|
||||
owner. Required deliverables are listed in `docs/local-profile-acceptance.md`:
|
||||
|
||||
- Exact runtime artifact/revision, in-sandbox entrypoints and dependencies,
|
||||
sanitized environment, private writable state, and deployment/host identity.
|
||||
- A Claude-compatible catalog credential route and owner implementation that
|
||||
delivers it to the selected runtime. An environment label containing route
|
||||
references is not credential delivery.
|
||||
- An owner-defined sandbox profile with explicit enforced provider egress,
|
||||
DNS/TLS requirements, and negative egress tests. Editing an egress list alone
|
||||
does not provide connectivity in the current loopback-only implementation.
|
||||
- Non-secret startup, identity, source-absence, and cleanup evidence through
|
||||
the same manager/service path the gateway will use.
|
||||
|
||||
Done when these deliverables exist at reviewable revisions and their owner
|
||||
proofs pass. No rein installation in the source checkout, host home mount,
|
||||
credential injection from the interactive shell, or unrestricted network
|
||||
substitution is an acceptance path.
|
||||
|
||||
2026-09-05 owner progress: SAND-WP-0015 implemented digest-pinned standalone
|
||||
Python runtime mounting and private namespace state. Real rein CLI startup,
|
||||
read-only runtime, source absence, clean worktree, state persistence across
|
||||
exec calls, and teardown passed in sandbox d4de9531; sand-boxer lint and 132
|
||||
tests pass. This completes the candidate runtime mechanism, not production
|
||||
selection. Authentication lane, credential delivery/revocation, provider egress,
|
||||
pinning the Claude executable, and deployed profile review remain open. The
|
||||
credential catalog has no matching Anthropic/Claude workload lane; the user selected a dedicated Anthropic workload API key. See
|
||||
[the concrete custody proposal](../docs/anthropic-workload-key.md) and owner
|
||||
CCR-2026-0016 (in flight). Organization/key metadata is supplied and the user saved the key in OpenBao.
|
||||
A metadata-only owner check confirmed live version 2 on 2026-09-05, without
|
||||
reading the value. Custody handoff is complete. Verified machine binding,
|
||||
protected runtime delivery, workspace scope/budget and provider egress remain
|
||||
outstanding; provider authentication has not been tested.
|
||||
See docs/local-profile-acceptance.md.
|
||||
|
||||
## Review and validate the versioned Glas proof candidate
|
||||
|
||||
```task
|
||||
id: GLAS-WP-0012-T03
|
||||
status: wait
|
||||
depends_on: [GLAS-WP-0012-T02, SECRETS-WP-0009-T03]
|
||||
priority: high
|
||||
blocking_reason: "Await the rebuilt runtime artifact carrying profile 1.1.1 started through its owner on railiance01 (SECRETS-WP-0009-T03 step B2)."
|
||||
state_hub_task_id: "c660808a-1724-555c-bc59-7318870d3591"
|
||||
```
|
||||
|
||||
Depends on T02. Prepare `harness.agent-dev-local@1.1.0` only once the real
|
||||
sandbox profile and credential route are known. Keep the rein/model route and
|
||||
tool profile explicit; choose bounded proof limits no greater than the current
|
||||
900 seconds / 60,000 tokens. Record runtime and sandbox revisions and the
|
||||
validated host scope in evidence. Account for dependency versions that are not
|
||||
represented directly in the Glas profile schema.
|
||||
|
||||
Use `unverified` only after known blockers are resolved, to label the bounded
|
||||
real proof. Validate schema, capabilities, route propagation, timeout behavior,
|
||||
and refusal of the old blocked version. Version coexistence must make an
|
||||
unversioned selection ambiguous rather than silently changing consumers.
|
||||
|
||||
Done when the exact candidate resolves, the real runtime starts through its
|
||||
owner, the focused tests and full suite pass, and rollback to the blocked
|
||||
`harness.agent-dev-local@1.0.0` pin is documented.
|
||||
|
||||
2026-09-21 candidate: `profiles/harness.agent-dev-local-1.1.0.yaml`, readiness
|
||||
`unverified` (evidence_ref GLAS-WP-0012-T03), `profile.bwrap-local`, no
|
||||
credential route refs, claude-sonnet-4-6, 900 s / 60,000 tokens, max_turns 30,
|
||||
max_budget_usd 5.0. The provider key stays with rein-aharness MessagesOwner
|
||||
outside bwrap (metered-once); the owner bootstrap's sandbox, model and
|
||||
credential-route checks accept it. max_budget_usd matches the operator-accepted
|
||||
spend envelope hfact-glas-anthropic-2026-09 (Informed Decision
|
||||
infd-20260921-b01, approval ba5ce2d8). Unpinned selection is now ambiguous and
|
||||
1.0.0 stays blocked; rollback is pinning 1.0.0. 134 tests pass. Remaining for
|
||||
done: the rebuilt runtime artifact carrying this profile starts through its
|
||||
owner on railiance01 (SECRETS-WP-0009-T03 step B2).
|
||||
|
||||
2026-09-23 the candidate is now `harness.agent-dev-local@1.1.1` (`claude-sonnet-5`,
|
||||
otherwise identical). 1.1.0 is blocked, and rollback is still pinning 1.0.0.
|
||||
Reviewed 2026-09-24: the catalog resolves 1.1.1 as unverified, and 134 tests pass.
|
||||
The done condition is unchanged. It needs runtime `2cfc7b5f` rebuilt with 1.1.1
|
||||
and started through its owner.
|
||||
|
||||
## Prove one real bounded rein task inside the sandbox
|
||||
|
||||
```task
|
||||
id: GLAS-WP-0012-T04
|
||||
status: wait
|
||||
depends_on: [GLAS-WP-0012-T03]
|
||||
blocking_reason: "Depends on T03."
|
||||
priority: high
|
||||
state_hub_task_id: "3e791432-8f37-54bd-a44a-55eeff11ac19"
|
||||
```
|
||||
|
||||
Depends on T03. Run the actual `ReinAharness` adapter and installed
|
||||
`rein-aharness` CLI with Claude through the candidate profile on a disposable
|
||||
Git fixture. Do not substitute deterministic dispatch or change the source
|
||||
checkout to satisfy the task. Verify the acceptance assertions in the contract,
|
||||
including exactly one intended commit, a clean final tree, unchanged/inaccessible
|
||||
host source, exact identity, bounded duration, task cleanup, and destruction.
|
||||
|
||||
Collect and validate the sandbox artifact before gateway teardown removes the
|
||||
workspace. Implement the smallest necessary proof observer in the acceptance
|
||||
fixture; direct evidence must distinguish actual content validation from a
|
||||
reported commit SHA. Preserve compact value-free evidence only.
|
||||
|
||||
Done when the bounded real run and relevant failure/cleanup checks pass with
|
||||
the exact selected profile/runtime pins and credential route identified.
|
||||
|
||||
## Publish evidence and mark only the proven profile ready
|
||||
|
||||
```task
|
||||
id: GLAS-WP-0012-T05
|
||||
status: wait
|
||||
depends_on: [GLAS-WP-0012-T04]
|
||||
blocking_reason: "Depends on T04."
|
||||
priority: high
|
||||
state_hub_task_id: "f7635cf6-e044-52d4-8242-0512e0ccbd7b"
|
||||
```
|
||||
|
||||
Depends on T04. Update only the proven candidate to `ready` with evidence that
|
||||
identifies the host/runtime scope, credential route, egress policy, revisions,
|
||||
limits, and rollback. Run catalog validation and regression checks. Do not
|
||||
enable production schedules as a side effect of this proof.
|
||||
|
||||
Update `GLAS-IN-0002` with the Claude acceptance result. Keep its open-weight
|
||||
portion live, or split that remaining work into a separately registered record
|
||||
before closing the intake. Synchronize work records and log progress. Finish
|
||||
this workplan only when the first profile is proven and residuals remain live.
|
||||
|
||||
## Prepare the real-rein acceptance runner
|
||||
|
||||
```task
|
||||
id: GLAS-WP-0012-T06
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "d0c26709-549f-540a-8250-1fd1b2e4960a"
|
||||
```
|
||||
|
||||
Independent preparation for T04 while T02 remains blocked. Add a disposable
|
||||
fixture that delegates actual dispatch to the catalog-built ReinAharness,
|
||||
validates the commit and artifact through owner execution before teardown,
|
||||
checks the host source and cleanup, and emits bounded evidence without model
|
||||
output or credential values. Refuse blocked or unpinned profiles before
|
||||
creating a sandbox. Test negative artifact cases and the refusal path. This
|
||||
runner does not itself satisfy T04 or change readiness.
|
||||
|
||||
Completed 2026-09-05. `scripts/prove-local-profile.py` delegates real dispatch
|
||||
and observes content/commit acceptance before teardown. It verifies exactly one
|
||||
new commit, expected paths and committed/worktree content, a clean tree,
|
||||
source absence, exact child identity, private task mode/removal, and host-source
|
||||
integrity plus workspace destruction. It emits bounded evidence and has no
|
||||
readiness override. `tests/test_local_profile_proof.py`: 20 focused tests pass;
|
||||
full suite: 101 passed; catalog validation passes. A real CLI invocation against
|
||||
`harness.agent-dev-local@1.0.0` returned `OperationallyBlockedProfileError`
|
||||
without creating a sandbox. T02–T05 remain waiting on the owner dependencies;
|
||||
no real model run or readiness promotion is claimed.
|
||||
|
||||
## Acceptance
|
||||
|
||||
One version-pinned local profile runs its actual rein/model task entirely in
|
||||
the declared sandbox, produces validated local output, and tears down cleanly.
|
||||
Its readiness describes the proven runtime scope. Other profile readiness and
|
||||
consumer schedules remain independently governed.
|
||||
|
||||
## 2026-09-05 egress owner return
|
||||
|
||||
Owner-controlled HTTPS egress is implemented and live-tested through both the
|
||||
extension and a persisted manager: provider TLS/HTTP response, undeclared-host
|
||||
and direct-IP denial, isolated namespace and proxy/workspace cleanup passed.
|
||||
See [egress evidence](../docs/evidence/GLAS-WP-0012-egress-2026-09-05.md).
|
||||
T02 remains waiting on machine auth, protected credential delivery, the pinned
|
||||
Claude executable and the combined production acceptance. Profiles remain blocked.
|
||||
|
||||
## 2026-09-05 credential owner return
|
||||
|
||||
Sand-boxer credential transport is implemented and proved with synthetic
|
||||
provider sandbox 880f749e. Owner config binds exact profile/project/actor and
|
||||
run id; unknown routes refuse, shell API-key fallback is stripped, only the
|
||||
workload child receives the value, and output redaction precedes truncation.
|
||||
Tests: sand-boxer171, secrets-engine226, Glas101 pass. Native data-only read
|
||||
policy/AppRole proposal and activation now live in SECRETS-WP-0009. Its
|
||||
production exec refuses before OpenBao because durable access-engine decision
|
||||
records and scoped service authority remain unavailable. T02 remains waiting
|
||||
on that activation, pinned Claude startup and real provider/task acceptance.
|
||||
|
||||
## 2026-09-06 credential owner update
|
||||
|
||||
Reviewed secrets-engine message `3bd23bc9-e863-4f1f-8c76-415812ec0656`.
|
||||
Owner reports the local authorization join implemented at `627810b`. Remaining
|
||||
native activation depends on approval-engine serving the durable
|
||||
ActionAuthorization claim endpoint and access-engine serving Check, followed
|
||||
by configuration, per-lane approval and positive/negative verification.
|
||||
State Hub decisions are not a substitute for the durable authorization object.
|
||||
SECRETS-WP-0009-T03 and this plan's T02 remain waiting; runtime pinning and
|
||||
combined real proof remain required. No readiness change or real-key read.
|
||||
|
||||
## 2026-09-06 authorization contract and policy publication
|
||||
|
||||
Reviewed owner commits `7b4b9e3` and `083bee7` and FLEX-WP-0021. Gate-house
|
||||
GH-DEC-2026-005 resolved the contract: secrets-engine now validates the
|
||||
approval-claim and decision envelope separately and removed the incorrect
|
||||
State Hub authority requirement. Flex-auth published
|
||||
`secrets-engine.catalog-lane.lifecycle` v1; T01/T02 are done.
|
||||
|
||||
Remaining owner gates are real decision/digest verification (FLEX-WP-0021-T03),
|
||||
consumer service deployment and handoff (T04/T05), approval-engine deployment,
|
||||
production service identity and verified lane activation. Publication is not
|
||||
deployment. T02 stays waiting, and runtime pinning and combined acceptance
|
||||
remain required. No new owner inbox message or activation evidence was found
|
||||
in this review.
|
||||
|
||||
## 2026-09-06 digest verification follow-up
|
||||
|
||||
Reviewed secrets-engine `6e9c152` and `925d028`: owner corrected correlation
|
||||
field handling in the flex-auth digest and refreshed fixtures. Its tests also
|
||||
record an unresolved approval/native action-vocabulary and embedded-claim
|
||||
digest mismatch for dual-control requests. This is owner integration evidence,
|
||||
not successful live activation; SECRETS-WP-0009-T03 remains wait. No new
|
||||
activation receipt was found in the Glas inbox. T02 remains waiting.
|
||||
|
||||
## 2026-09-06 authorization-chain owner return
|
||||
|
||||
Reviewed secrets-engine `f62d3fe` / `64aeec9`. Owner reports claim → Check →
|
||||
consume → backend integration against a throwaway OpenBao, including denial,
|
||||
unreachable service, invalid claim, missing digest, consume conflict and action
|
||||
mismatch refusal before backend access. Authorization transport is stubbed;
|
||||
wire shapes use published flex-auth fixtures. Do not describe this as a live
|
||||
production service or Anthropic delivery proof.
|
||||
|
||||
Owner now identifies deployment/configuration as the remaining live gate:
|
||||
consumer PDP service and approval-engine rollout, production service
|
||||
credentials, deployed policy pin and per-lane authorization id.
|
||||
SECRETS-WP-0009-T03 remains wait. Earlier digest-defect notes are historical;
|
||||
use this latest owner return for the integration status. Native lane positive/
|
||||
negative access checks, pinned Claude startup, provider scope/budget inputs and
|
||||
combined Glas acceptance remain outstanding. T02–T05 stay wait.
|
||||
|
||||
Review found no pending local changes or new inbox messages. Corrected stale
|
||||
README wording that still called owner execution missing; the implemented
|
||||
mechanism and its separate proofs do not change profile readiness.
|
||||
|
||||
## 2026-09-06 production deployment action
|
||||
|
||||
User authorized production deployment. Dedicated flex-auth-secrets-engine Helm
|
||||
release is available 1/1 at revision 1; five policy fixtures and allowed/denied
|
||||
network probes passed. CI image is digest-pinned; existing consumers unchanged.
|
||||
Caller authentication remains warn pending identity adoption. See
|
||||
[deployment evidence](../history/2026-09-06-174000-production-deployment.md).
|
||||
Approval-engine remains undeployed: live KeyCape clients and audit sender are
|
||||
absent, the tenant contract needs alignment, and a release digest is required.
|
||||
Corrected its stale issuer/JWKS deployment inputs. No production credential
|
||||
lane or real Claude run was activated; T02 remains wait.
|
||||
|
||||
## Production dependency coordination
|
||||
|
||||
GLAS-WP-0015 now tracks exact owner handoffs and return receipts from Glas.
|
||||
The requests in docs/production-dependency-handoffs.md reuse existing owner
|
||||
work: tenant agreement and client custody, KeyCape rollout, audit source
|
||||
onboarding, approval release, native lane activation and pinned combined
|
||||
runtime. Image/runtime preparation can proceed in parallel. The runner's
|
||||
project glas-local-proof must be reconciled with the owner route example's
|
||||
project glas-harness before the exact delivery binding is selected. Owner
|
||||
handoffs were sent after explicit authorization; GLAS-WP-0015 records all seven
|
||||
receipt IDs and tracks replies. This plan's T02 remains wait.
|
||||
|
||||
## Verified owner follow-up — 2026-09-06
|
||||
|
||||
Glas reviewed six owner acknowledgements in GLAS-WP-0015. Project binding is
|
||||
resolved to agt/glas-local-proof at sand-boxer 23d0c2b; the combined profile
|
||||
source is available, but Claude is not pinned. The deployed policy was found
|
||||
to lack tenant denial at v1; the owner correction v2 is now deployed at Helm
|
||||
revision 2 and six live Check fixtures pass, including wrong_tenant denial.
|
||||
Tenant choice, workstation caller access, audit load-bearing schema/admission,
|
||||
identity/custody and native activation remain gates. T02 remains wait.
|
||||
|
||||
|
||||
Tenant decision update (2026-09-06): operator approved `tenant:platform` for the
|
||||
approval dependency chain as the platform management/services tenant. See
|
||||
`docs/platform-tenant-decision.md`. The choice blocker is resolved; exact owner
|
||||
configuration alignment and verification remain part of T02 before readiness.
|
||||
|
||||
|
||||
Later deployment progress (2026-09-06): KeyCape/approval source tenant alignment
|
||||
verified, approval release published and pinned, audit T03 complete, dedicated
|
||||
PDP revision 3 enforces adopted short-lived caller identity. See GLAS-WP-0015
|
||||
and its evidence receipts. T02 remains wait until remaining owner admission and
|
||||
runtime gates are verified; no real model run or readiness promotion.
|
||||
|
||||
|
||||
## Pinned Claude startup progress — 2026-09-06
|
||||
|
||||
Advanced sand-boxer SAND-WP-0015-T05 independently of custody: runtime builder
|
||||
now copies only an explicitly digest-pinned native Claude executable. Selected
|
||||
installed Claude 2.1.263, binary digest
|
||||
26d020351e8112f4006790f3cfce43b4c9df0c1bb1d0e542364d64151b81d5ba;
|
||||
complete candidate runtime digest
|
||||
5cf9a16c5d77a16bdb2cb5b3df06ea655356bc2d44741791e3fedfee20d7e922.
|
||||
Sandbox f8821ec2 passed actual Claude --version, rein CLI/import, read-only
|
||||
runtime, private HOME/state persistence, source absence, clean tree and teardown
|
||||
with owner proxy environment present. Lint and all 175 sand-boxer tests pass.
|
||||
Evidence: docs/evidence/GLAS-WP-0015-claude-startup-2026-09-06.json.
|
||||
|
||||
This resolves candidate binary pin/startup, not protected runtime deployment,
|
||||
Claude provider-request compatibility, credential delivery or a real model run.
|
||||
The candidate is in /tmp; no production profile readiness changed. Existing
|
||||
credential and acceptance tasks remain open. No new owner replies at session start.
|
||||
|
||||
|
||||
## Protected local artifact return — 2026-09-08
|
||||
|
||||
SAND-WP-0015-T06 is done. The previously pinned combined artifact is installed
|
||||
unchanged in owner UID 1000's mode-0700 runtime store on bnt-lap001:
|
||||
`/home/worsch/.local/share/sandboxer/runtimes/5cf9a16c5d77a16bdb2cb5b3df06ea655356bc2d44741791e3fedfee20d7e922`.
|
||||
Complete artifact digest remains
|
||||
`5cf9a16c5d77a16bdb2cb5b3df06ea655356bc2d44741791e3fedfee20d7e922`.
|
||||
This is the existing owner-controlled local artifact contract, not root-owned
|
||||
system storage or Railiance placement. Do not rebuild this accepted candidate
|
||||
merely to move it out of /tmp.
|
||||
|
||||
The installed-path sandbox `51b59587` proves actual Claude 2.1.263 and rein
|
||||
startup, read-only runtime, source absence, clean Git, private state persistence,
|
||||
loopback-only interfaces, and workspace/proxy removal. No credential references
|
||||
or model request were used. Sand-boxer `make check`: lint clean, 188 tests pass.
|
||||
Source return: 3e49a98a0e2c4a64539a5ccd674be8cd67ac9845; receipt in
|
||||
`sand-boxer/docs/evidence/SAND-WP-0015-protected-local-install-2026-09-08.json`.
|
||||
|
||||
T02 remains wait for trusted owner execution configuration, native credentials
|
||||
and real-provider evidence. T03-T06 retain candidate review, real-model run,
|
||||
readiness and recovery obligations. No profile readiness or schedule changes.
|
||||
|
||||
2026-09-23 candidate superseded before use: operator chose `claude-sonnet-5`.
|
||||
`harness.agent-dev-local@1.1.1` is identical to 1.1.0 except the model; 1.1.0 is
|
||||
now `blocked`. Runtime `2cfc7b5f` bundles the earlier 1.1.0 copy (profile sha
|
||||
`e84b9d0f…`) and must be rebuilt with this revision and placed again
|
||||
(SECRETS-WP-0009-T03). Claude CLI 2.1.266 in that runtime lists
|
||||
`claude-sonnet-5` in its model registry (static check). 134 tests pass.
|
||||
|
||||
## 2026-09-27 loose-ends review
|
||||
|
||||
Reviewed for locally finishable work. No new owner reply advances T02 beyond
|
||||
the 2026-09-23 secrets-engine status already recorded in GLAS-WP-0015-T03.
|
||||
T03's remaining done condition (runtime `2cfc7b5f` rebuilt with 1.1.1 and
|
||||
started through its owner on railiance01, SECRETS-WP-0009-T03 step B2) and
|
||||
T04/T05 all require that external owner delivery. Nothing in this repo can
|
||||
advance them today; `status: blocked` stands unchanged. Re-review when the
|
||||
runtime rebuild/placement or owner authentication lands.
|
||||
|
||||
|
||||
## EMERGENT-WP-0002 proof requirements — 2026-10-03
|
||||
|
||||
Scoped review and local observed-model correction:
|
||||
`docs/evidence/2026-10-03-emergent-session-readiness-review.md`.
|
||||
T04 retains actual native-provider startup and continuity acceptance, including
|
||||
reload after real resume/compaction, provider/repository changes and exact
|
||||
available model/session metadata. The current lifecycle exposes no continuity
|
||||
event API. Existing owner-delivery blockers and all task/readiness states remain;
|
||||
local deterministic tests are not governed provider or isolation acceptance.
|
||||
|
||||
|
||||
## 2026-10-16 NetKingdom isolated integration preparation
|
||||
|
||||
Prepared, without owner deployment or readiness promotion, a Glas change that
|
||||
separates credential-free housekeeping commands from explicit rein workload
|
||||
execution. Both concrete adapters use the workload path; SSH selections with
|
||||
credential routes refuse because that transport has no owner delivery contract.
|
||||
Full regression: 141 passed. Real disposable bwrap/Glas transport proof passes
|
||||
synthetic host-file denial, inherited credential environment and subprocess
|
||||
denial, workload-only delivery, redaction, subsequent-command absence and
|
||||
teardown. No provider/model call or real credential read occurred.
|
||||
|
||||
Latest owner dependency sources supersede the stale missing-lane account:
|
||||
September 27 native lanes were activated and revoked; corrected immutable
|
||||
owner runtime is `818b1b7c…` (October 15 startup proof), not yet installed or
|
||||
admitted on Railiance. HARNESS-WP-0008-T07 retains memo lifecycle acceptance,
|
||||
installation, rehearsal, renewal/pin updates, native approvals and bounded
|
||||
successful model/tool/commit acceptance. Any subsequent build containing this
|
||||
patch has a different source/artifact pin requiring owner admission.
|
||||
|
||||
This is a reviewable patch in Custodian's NetKingdom change bundle; no canonical
|
||||
source synchronization or workplan task completion is implied. CUST-WP-0073
|
||||
continues to require admission of the actual interactive assignment, including
|
||||
its existing host credential and external tool paths, not a disposable fixture.
|
||||
|
|
@ -0,0 +1,375 @@
|
|||
---
|
||||
id: SAND-WP-0015
|
||||
type: workplan
|
||||
title: "Provide a pinned bwrap rein runtime and private state"
|
||||
domain: infotech
|
||||
repo: sand-boxer
|
||||
status: blocked
|
||||
flavor: implementation
|
||||
owner: codex
|
||||
topic_slug: bwrap-runtime-and-private-state
|
||||
created: "2026-09-05"
|
||||
updated: "2026-10-04"
|
||||
state_hub_workstream_id: "d3f12387-fd23-58f0-b979-9c811507614d"
|
||||
---
|
||||
|
||||
# Provide a pinned bwrap rein runtime and private state
|
||||
|
||||
Implement the runtime prerequisites returned by Glas in `GLAS-WP-0012` and
|
||||
`docs/local-profile-acceptance.md`. Continue owner work `SAND-WP-0014-T05` and
|
||||
live residual `GLAS-IN-0002`. Runtime startup is a separate acceptance gate
|
||||
from credential delivery, enforced provider egress, and a real model task.
|
||||
|
||||
## Mount an owner-selected, digest-pinned Python runtime
|
||||
|
||||
```task
|
||||
id: SAND-WP-0015-T01
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "93e4aad8-ba9b-5850-93d4-b5bf78ac8978"
|
||||
```
|
||||
|
||||
Add exact artifact verification to trusted extension configuration and mount
|
||||
the standalone runtime read-only at a fixed namespace path. Reject altered
|
||||
content/modes, unexpected files, escaping symlinks, special files, and source
|
||||
or workspace overlap. A caller cannot choose runtime paths through exec input.
|
||||
Provide a non-editable rein-aharness/llm-connect bundle builder with recorded
|
||||
source revisions and resolved dependency versions.
|
||||
|
||||
Completed 2026-09-05. `extensions/runtime.py` verifies complete artifact
|
||||
contents/modes before a read-only mount; the builder installed committed
|
||||
rein-aharness `1429db5` and llm-connect `0056094` without editable source paths.
|
||||
Candidate digest and source/package evidence are in `docs/bwrap-runtime.md`.
|
||||
|
||||
## Keep writable runtime state outside the repository
|
||||
|
||||
```task
|
||||
id: SAND-WP-0015-T02
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "c8a689f8-bdf2-57a2-8521-a1c0ca44e750"
|
||||
```
|
||||
|
||||
Create private mode-0700 HOME/config/cache/state/tmp directories within the
|
||||
namespace, preserve them across exec requests, and remove them with teardown.
|
||||
Keep the command environment sanitized and disable Python user-site/bytecode
|
||||
writes. Fail closed on profile network/setup-credential declarations that the
|
||||
current bwrap implementation cannot honor.
|
||||
|
||||
Completed 2026-09-05. Mode-0700 namespace HOME and XDG/TMP directories sit
|
||||
outside the copied Git tree. Explicit runtime PATH selection remains owner
|
||||
controlled. Unsupported egress/default-allow and setup credentials now refuse
|
||||
before workspace creation; regression tests cover these boundaries.
|
||||
|
||||
## Verify the real rein runtime and owner regression paths
|
||||
|
||||
```task
|
||||
id: SAND-WP-0015-T03
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "1a3002f6-c6a8-59d0-9d38-942028419de5"
|
||||
```
|
||||
|
||||
Run `make check`, the existing authenticated cross-request owner smoke, and a
|
||||
real runtime startup smoke. Verify the actual rein CLI and adapter imports,
|
||||
read-only runtime, private HOME, clean worktree, absent source, loopback-only
|
||||
network, and teardown. Keep the production Glas profile blocked.
|
||||
|
||||
Completed 2026-09-05. `make check`: lint clean, 132 tests passed. Authenticated
|
||||
owner API smoke `223db65b` returned HTTP 200 and proved exact consumer identity,
|
||||
stdin delivery, absent source, loopback-only network and complete teardown.
|
||||
Pinned runtime smoke `d4de9531` ran the actual rein CLI and imported its Claude
|
||||
adapter, proved read-only runtime/private HOME/clean worktree, retained private
|
||||
state across a second exec, and removed the workspace. No model call or
|
||||
credential acquisition occurred; T04 remains waiting.
|
||||
|
||||
## Resolve Claude credentials, enforced egress, and production acceptance
|
||||
|
||||
```task
|
||||
id: SAND-WP-0015-T04
|
||||
status: wait
|
||||
blocked_on: message-from:rein-aharness
|
||||
blocking_reason: "Tracked as HARNESS-WP-0008-T07 (owner, attended): accept spend memo infd-20261014-b01 (up to three attempts within EUR 10, observed-usage settlement), rebuild/install ee3d0a53 on railiance01, rehearse, renew, then a successful attempt."
|
||||
priority: high
|
||||
state_hub_task_id: "58817ef6-76d9-5e34-908f-c024e6c99f93"
|
||||
```
|
||||
|
||||
The 2026-09-05 routing lookup found no concrete Anthropic/Claude workload lane.
|
||||
The generic OpenBao template is not a delegable lane; the OpenRouter AppRole
|
||||
belongs to another rein. Credential owner railiance-platform/OpenBao plus
|
||||
rein-aharness must establish a concrete Claude-compatible route and delivery/
|
||||
revocation contract before values can be requested or supplied to the runtime.
|
||||
|
||||
Sand-boxer must then implement the matching enforced provider egress contract
|
||||
(including DNS/TLS and denied-destination proof) and pin/deploy the actual
|
||||
Claude executable. No unrestricted-network or interactive-login substitute is
|
||||
permitted. Review the deployed runtime/profile with Glas, run its real-rein
|
||||
acceptance fixture, and update only the proven profile's readiness. No
|
||||
production schedule is enabled by the runtime-startup smoke.
|
||||
|
||||
This task and SAND-WP-0014-T05 remain open until those gates pass. The detailed
|
||||
return contract and runtime proof are in `docs/bwrap-runtime.md`; the live
|
||||
cross-repo residual remains `GLAS-IN-0002`.
|
||||
|
||||
## 2026-09-05 transport implementation evidence
|
||||
|
||||
T04 remains waiting on owner machine authentication, protected credential
|
||||
delivery, pinned Claude startup and the real model proof. CCR-2026-0016 custody
|
||||
is confirmed by a metadata-only check of live version 2; no value was read.
|
||||
HTTPS egress is now implemented as an exact owner/profile allowlisted CONNECT
|
||||
proxy while retaining an isolated network namespace. Non-secret live smoke
|
||||
sandbox e290e788 verified provider TLS response, undeclared destination and
|
||||
direct-IP denial, and proxy/workspace teardown. See docs/bwrap-egress.md.
|
||||
|
||||
## 2026-09-05 credential transport owner return
|
||||
|
||||
Owner-bound exec-env transport is implemented and proved with a synthetic
|
||||
provider in real sandbox 0e5fb35a. Exact route/profile/project/actor/run binding,
|
||||
child-only key injection, pre-truncation redaction, next-exec absence and teardown
|
||||
pass. No real key read or production profile activation. See docs/bwrap-credentials.md.
|
||||
SECRETS-WP-0009 now owns native AppRole adoption for CCR-2026-0016, including
|
||||
a data-only consumer read policy. Its production exec refuses before OpenBao
|
||||
until SECRETS-WP-0007-T04 and SECRETS-WP-0008-T02/T06 deliver canonical
|
||||
authorization/consume and service authority. T04 remains waiting on that live
|
||||
owner path, the pinned Claude executable and real model acceptance.
|
||||
|
||||
## 2026-09-06 combined-runtime candidate returned to Glas
|
||||
|
||||
Answered the GLAS-WP-0015 handoff. Added candidate profile
|
||||
`profile.claude-agent-dev-proof` version 1.1.0 (`ext.bwrap`, localhost-only,
|
||||
`default: deny` with declared `api.anthropic.com:443`) for GLAS-WP-0012 review.
|
||||
The committed profile grants no egress by itself: `ext.bwrap` refuses it unless
|
||||
the owner extension config allowlists the destination, and a regression test now
|
||||
asserts that fail-closed default. `make check`: lint clean, 172 tests passed.
|
||||
|
||||
Resolved the differing project examples in favour of the acceptance runner's
|
||||
`actor: agt` / `project: glas-local-proof`, and bound the documented credential
|
||||
route to that single project rather than broadening it. The full return contract
|
||||
— profile id/revision, host scope, consumer tuple, runtime digest and mount
|
||||
paths, declared egress, and the value-free denial/cleanup receipts — is in
|
||||
`docs/bwrap-runtime.md`.
|
||||
|
||||
T04 stays `wait`: the concrete Claude credential lane and delivery/revocation
|
||||
contract, owner machine authentication, the pinned Claude executable with its
|
||||
own proxy startup proof, and the real-model acceptance are still operator and
|
||||
upstream-owner gated. Provider workspace scope and spend limit remain operator
|
||||
inputs. `SAND-WP-0014-T05` remains open on the same gates.
|
||||
|
||||
|
||||
## Package and prove the pinned Claude executable independently of custody
|
||||
|
||||
```task
|
||||
id: SAND-WP-0015-T05
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "f0163cda-b581-56a0-a0dd-9c8a436f5bbe"
|
||||
```
|
||||
|
||||
Split the independent executable packaging/startup portion from T04 so credential
|
||||
custody does not hide completed local runtime work. Implement explicit binary
|
||||
pinning, reject mismatches/host wrappers, and prove actual startup with the
|
||||
broker proxy environment and complete cleanup. T04 retains protected deployment,
|
||||
credential adoption and real-provider acceptance.
|
||||
|
||||
|
||||
## Pinned Claude candidate and isolated startup — 2026-09-06
|
||||
|
||||
The builder now accepts `--claude-binary`, `--claude-sha256` and
|
||||
`--claude-version` together. It requires a regular native ELF file, verifies the
|
||||
exact copied bytes, refuses symlinks/wrappers/mismatches and existing destination
|
||||
files, and copies only the executable as bin/claude mode 0755. The expected
|
||||
version is metadata until the sandbox smoke checks the actual output. No
|
||||
interactive HOME, provider key or login state is copied.
|
||||
|
||||
Candidate provenance: the explicitly selected installed native release
|
||||
`/home/worsch/.local/share/claude/versions/2.1.263`, SHA-256
|
||||
`26d020351e8112f4006790f3cfce43b4c9df0c1bb1d0e542364d64151b81d5ba`.
|
||||
This is a digest pin of the selected local artifact, not an upstream signature
|
||||
verification claim. Complete runtime candidate:
|
||||
`/tmp/sandboxer-claude-runtime-20260906`, SHA-256
|
||||
`5cf9a16c5d77a16bdb2cb5b3df06ea655356bc2d44741791e3fedfee20d7e922`.
|
||||
Mount remains `/opt/sandboxer/runtime`; Claude is `/opt/sandboxer/runtime/bin/claude`.
|
||||
Rein sources remain 1429db5 / llm-connect 0056094; resolved dependency versions
|
||||
are recorded in the evidence. Rebuilding may resolve different dependencies;
|
||||
the complete digest pins this artifact, not future builds.
|
||||
|
||||
Sandbox f8821ec2 ran real `claude --version` -> `2.1.263 (Claude Code)` plus rein
|
||||
CLI/import checks, with owner allowlisted provider egress and HTTPS_PROXY set
|
||||
by the broker. Runtime read-only, private 0700 HOME/state persistence, clean Git,
|
||||
absent source and loopback-only interfaces passed; workspace and proxy removed.
|
||||
`make check`: lint passed, 175 tests passed.
|
||||
|
||||
Evidence: docs/evidence/SAND-WP-0015-claude-startup-2026-09-06.json.
|
||||
Reproduce with the builder options above and `scripts/smoke-bwrap-runtime.py
|
||||
--runtime-path <candidate> --runtime-sha256 <digest> --claude-version 2.1.263`.
|
||||
|
||||
Limits: `--version` proves startup with the proxy environment, not that Claude
|
||||
has sent a provider request through it. No credential acquisition or model run.
|
||||
The /tmp candidate is not production placement; protected artifact installation,
|
||||
owner configuration, credential adoption and real-model proof remain T04 gates.
|
||||
|
||||
|
||||
## Install the pinned runtime in the local owner's protected store
|
||||
|
||||
```task
|
||||
id: SAND-WP-0015-T06
|
||||
status: done
|
||||
priority: high
|
||||
assignee: the-custodian
|
||||
state_hub_task_id: "f85f54c9-1081-5d0c-95ad-aca20ede8d66"
|
||||
```
|
||||
|
||||
HFACT-WP-0001-T04 consumes this independent installation return. Reuse the
|
||||
already verified combined candidate with digest
|
||||
`5cf9a16c5d77a16bdb2cb5b3df06ea655356bc2d44741791e3fedfee20d7e922`;
|
||||
do not rebuild resolved dependencies. Publish it under the local owner's
|
||||
private artifact store, verify exact copied content/modes and refusal of unsafe
|
||||
or corrupted destinations, and prove startup/read-only access/teardown through
|
||||
the installed path. The owner remains trusted; this is not root-owned storage.
|
||||
T04 retains real credential adoption, owner configuration, model acceptance
|
||||
and production placement. No credential route or profile is activated here.
|
||||
|
||||
`scripts/install-bwrap-runtime.py` installs below the owner's home into a
|
||||
mode-0700 store, verifies before and after copying, preserves artifact modes
|
||||
and internal symlinks, serializes publishers and never overwrites a published
|
||||
artifact. Existing modified artifacts are refused rather than repaired silently.
|
||||
Group/other-writable ancestors, unsafe permission bits and store aliases refuse.
|
||||
Unit tests cover integrity, idempotence, copy-time corruption and access modes.
|
||||
|
||||
|
||||
T06 completed 2026-09-08 on bnt-lap001, local owner UID 1000. Installed the
|
||||
unchanged 358-entry / 245176062-byte candidate at
|
||||
`/home/worsch/.local/share/sandboxer/runtimes/5cf9a16c5d77a16bdb2cb5b3df06ea655356bc2d44741791e3fedfee20d7e922`.
|
||||
The private store is mode 0700; the installing owner remains trusted. A root-owned
|
||||
system installation was not claimed or required for this local owner contract.
|
||||
The installed-path sandbox smoke `51b59587` passed real Claude 2.1.263 and rein
|
||||
startup, read-only mount, private state persistence, clean worktree, absent source,
|
||||
loopback-only interfaces and complete workspace/proxy teardown. No model call
|
||||
or credential acquisition occurred. `make check`: lint clean, 188 tests passed.
|
||||
Two existing tests require the checkout directory name `sand-boxer`; the final
|
||||
full check used that canonical basename. Evidence:
|
||||
`docs/evidence/SAND-WP-0015-protected-local-install-2026-09-08.json`.
|
||||
T04 retains owner execution configuration, native credential/egress and real-model
|
||||
acceptance; Railiance installation needs its own target-specific return.
|
||||
|
||||
## 2026-09-09 factory metered route source return
|
||||
|
||||
Implemented the trusted, ephemeral Messages route described in
|
||||
[docs/bwrap-messages-route.md](../docs/bwrap-messages-route.md). Actual local
|
||||
bwrap owner transport proves provider key/ledger separation, direct-route denial,
|
||||
revocation and teardown; rein also proves metered request plus commit import and
|
||||
close replay. `make check`: lint clean, 199 passed. T04 remains waiting for the
|
||||
admitted provider-to-owner bootstrap, updated protected runtime/profile, Railiance
|
||||
placement, live compatibility and G0. Existing child-provider-key/direct-CONNECT
|
||||
proofs do not admit this different credential holder or metered profile. No CCR,
|
||||
secret read, deployment or paid request was performed.
|
||||
|
||||
## 2026-09-09 standalone owner packaging and runtime selection
|
||||
|
||||
Added frozen-lock owner build mode for the matched rein/llm/Glas/sandboxer set,
|
||||
with non-editable installation and recorded lock/source/package pins. Sand-boxer
|
||||
wheels now carry their profile and extension definitions, fixing a bwrap owner
|
||||
failure that source-checkout tests could hide. The trusted Messages binding can
|
||||
select the digest-pinned runtime without adding an API or profile override.
|
||||
`make check`: lint clean, 205 passed. Existing standalone workload builds remain
|
||||
supported. See docs/bwrap-runtime.md and rein's docs/owner-bootstrap.md.
|
||||
|
||||
The project's `evidence/2026-09-09-owner-bootstrap.json` records the actual candidate
|
||||
build and isolated installed-interpreter/CLI proof. T04 remains wait for accepted
|
||||
credential-to-owner delivery, current protected installation and Railiance placement,
|
||||
provider compatibility and G0. Source packaging does not reopen completed T01-T03,
|
||||
T05-T06, broaden the old CCRs or activate the previously installed 2.1.263 artifact.
|
||||
|
||||
The first standalone candidate exposed a stale cached local wheel despite current
|
||||
Git metadata. Owner builds now refresh all local distributions and compare every
|
||||
installed package source/definition file with its tracked source; stale, missing or
|
||||
extra files refuse the build. Four content-conformance regressions cover that gap.
|
||||
|
||||
|
||||
## 2026-09-27 installed launcher defect and guarded correction
|
||||
|
||||
REINAH-WP-0003's single admitted attempt completed native provider/companion
|
||||
apply, verify and exec, including scoped-token revocation. The claimed job
|
||||
`6efa9436-6a91-47c0-94e5-b31b5a0e7e3a` failed before any provider request
|
||||
reservation. The sandbox was destroyed and the source repository is unchanged.
|
||||
The parent EUR 10 liability remains held for explicit reconciliation; no retry
|
||||
or replacement authority is inferred.
|
||||
|
||||
Installed artifact `b6e4e8a4` contains uv-generated three-line shell launchers
|
||||
that still reference a vanished temporary build interpreter. The builder had
|
||||
only rewritten direct Python shebangs. Fixed both generated forms and added
|
||||
actual relocation/execution tests for direct, long and space-containing build
|
||||
paths, plus refusal of unknown governed launchers. Builder tests: 12 passed;
|
||||
focused lint passes. Rein's real installed bwrap probe reproduces both broken
|
||||
entrypoints at exit 127 with zero provider forwards; its new bootstrap checks
|
||||
refuse the malformed artifact before claiming.
|
||||
|
||||
T04 remains `wait` and this plan stays `blocked`: replacement artifact build/
|
||||
admission and a separately authorized successful model/tool/commit proof remain.
|
||||
The existing SAND-WP-0015 / REINAH-WP-0003 / SECRETS-WP-0009 records retain the
|
||||
work. No new task or workplan. Detailed non-secret receipts are in rein-aharness
|
||||
`docs/evidence/2026-09-27-metered-*.json`.
|
||||
|
||||
|
||||
## 2026-10-04 offline owner startup proof preparation
|
||||
|
||||
The existing no-credential smoke command now supports
|
||||
`--require-owner-entrypoints`: the admitted digest-pinned runtime must start both
|
||||
`rein-aharness --help` and `glas-harness --help`, and imported rein, llm-connect,
|
||||
Glas and sandboxer modules must originate inside the mounted artifact. Broken or
|
||||
missing launchers, failed help, timeout and external package origins cannot be
|
||||
recorded as startup success. The default standalone mode remains supported.
|
||||
Launcher diagnostics are not copied into the receipt. Focused tests exercise
|
||||
real subprocesses, including the vanished-build-interpreter failure.
|
||||
|
||||
This is preparation for a corrected immutable candidate's independent native
|
||||
startup proof; it is not that candidate's receipt or owner acceptance. T04 stays
|
||||
`wait`, the plan stays `blocked`, and the Sept27 artifact/liability evidence
|
||||
remains intact. Replacement artifact admission and pins, explicit retained
|
||||
EUR 10 reconciliation, and a separately authorized successful model/tool/commit
|
||||
attempt remain with the existing owners. No credentials, provider request,
|
||||
queue claim, policy activation or retry authorization is supplied here.
|
||||
|
||||
2026-10-04 current blocker reconciliation: September 27 native provider/companion
|
||||
apply/verify/exec and successful revocation supersede the original missing
|
||||
platform route. The remaining owner return is rein-aharness corrected artifact/
|
||||
profile admission, retained EUR10 reconciliation and fresh retry authority.
|
||||
T04 stays wait; no renewed trial is authorized. Full owner check: lint clean,
|
||||
219 tests passed (one existing Starlette deprecation warning).
|
||||
|
||||
## 2026-10-13 owner returns: reconciliation and corrected build
|
||||
|
||||
rein-aharness `d897a83` records owner reconciliation of the Sept 27 held
|
||||
liability: run `6efa9436` failed at launcher exit 127 before any provider
|
||||
reservation; liability stays at the full hold, exhausting that one-attempt
|
||||
envelope, and the owner authorized a new retry envelope on 2026-10-13.
|
||||
rein-aharness `9a4469d` records corrected runtime `ee3d0a53…` (replaces
|
||||
`b6e4e8a4…`; only rein moved, adding pre-claim launcher rejection), built with
|
||||
the sand-boxer builder fix; both launchers exit 0 inside bwrap. Receipts:
|
||||
rein-aharness `docs/evidence/2026-10-13-*.json`.
|
||||
|
||||
Remaining before T04 closes: install `ee3d0a53` on railiance01, pin its digest
|
||||
in owner config, pass `--require-owner-entrypoints` offline startup proof, then
|
||||
run the single authorized attempt to a successful model/tool/commit receipt.
|
||||
T04 stays `wait`; the plan stays `blocked`.
|
||||
|
||||
2026-10-14 check: rein-aharness pushed `9a4469d` (corrected build record) and
|
||||
opened REINAH-WP-0008 (simpler metered retry, active). No railiance01 install/
|
||||
admission of `ee3d0a53` or successful acceptance receipt yet; request sent on
|
||||
hub message `82faf25c`. T04 stays `wait`; plan stays `blocked`.
|
||||
|
||||
2026-10-14 rein-aharness reply (thread `82faf25c`): step 1 done (`9a4469d` on
|
||||
origin/main). Steps 2-4 are REINAH-WP-0008-T07. Budget is now up to three
|
||||
attempts within EUR 10 under memo `infd-20261014-b01` (observed-usage
|
||||
settlement); order: memo acceptance in the attended informed-decision sitting,
|
||||
rebuild and install `ee3d0a53` on railiance01, `metered-once --rehearse`,
|
||||
`owner renew`, first attempt. Railiance01 still pins `b6e4e8a4`. T04 stays `wait`.
|
||||
|
||||
2026-10-15 owner-approved rebuild: the pinned inputs of the `ee3d0a53` receipt
|
||||
(same four commits, uv.lock `8775904b…`, Claude 2.1.266 `19842705…`) rebuilt
|
||||
to `818b1b7c8292…f359`. The digest differs only because the builder embeds
|
||||
absolute source paths (`direct_url.json`, `build-info.json`). Owner-entrypoint
|
||||
startup smoke passed in sandbox `28df7a1d`; no credentials or model calls.
|
||||
Sent to rein-aharness (message `8a7962f7`, thread `82faf25c`) to record as the
|
||||
then-current corrected runtime for REINAH-WP-0008-T07 and to load the unsigned
|
||||
memo sitting. Nothing installed on railiance01. T04 stays `wait`.
|
||||
|
|
@ -0,0 +1,375 @@
|
|||
---
|
||||
id: SAND-WP-0015
|
||||
type: workplan
|
||||
title: "Provide a pinned bwrap rein runtime and private state"
|
||||
domain: infotech
|
||||
repo: sand-boxer
|
||||
status: blocked
|
||||
flavor: implementation
|
||||
owner: codex
|
||||
topic_slug: bwrap-runtime-and-private-state
|
||||
created: "2026-09-05"
|
||||
updated: "2026-10-04"
|
||||
state_hub_workstream_id: "d3f12387-fd23-58f0-b979-9c811507614d"
|
||||
---
|
||||
|
||||
# Provide a pinned bwrap rein runtime and private state
|
||||
|
||||
Implement the runtime prerequisites returned by Glas in `GLAS-WP-0012` and
|
||||
`docs/local-profile-acceptance.md`. Continue owner work `SAND-WP-0014-T05` and
|
||||
live residual `GLAS-IN-0002`. Runtime startup is a separate acceptance gate
|
||||
from credential delivery, enforced provider egress, and a real model task.
|
||||
|
||||
## Mount an owner-selected, digest-pinned Python runtime
|
||||
|
||||
```task
|
||||
id: SAND-WP-0015-T01
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "93e4aad8-ba9b-5850-93d4-b5bf78ac8978"
|
||||
```
|
||||
|
||||
Add exact artifact verification to trusted extension configuration and mount
|
||||
the standalone runtime read-only at a fixed namespace path. Reject altered
|
||||
content/modes, unexpected files, escaping symlinks, special files, and source
|
||||
or workspace overlap. A caller cannot choose runtime paths through exec input.
|
||||
Provide a non-editable rein-aharness/llm-connect bundle builder with recorded
|
||||
source revisions and resolved dependency versions.
|
||||
|
||||
Completed 2026-09-05. `extensions/runtime.py` verifies complete artifact
|
||||
contents/modes before a read-only mount; the builder installed committed
|
||||
rein-aharness `1429db5` and llm-connect `0056094` without editable source paths.
|
||||
Candidate digest and source/package evidence are in `docs/bwrap-runtime.md`.
|
||||
|
||||
## Keep writable runtime state outside the repository
|
||||
|
||||
```task
|
||||
id: SAND-WP-0015-T02
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "c8a689f8-bdf2-57a2-8521-a1c0ca44e750"
|
||||
```
|
||||
|
||||
Create private mode-0700 HOME/config/cache/state/tmp directories within the
|
||||
namespace, preserve them across exec requests, and remove them with teardown.
|
||||
Keep the command environment sanitized and disable Python user-site/bytecode
|
||||
writes. Fail closed on profile network/setup-credential declarations that the
|
||||
current bwrap implementation cannot honor.
|
||||
|
||||
Completed 2026-09-05. Mode-0700 namespace HOME and XDG/TMP directories sit
|
||||
outside the copied Git tree. Explicit runtime PATH selection remains owner
|
||||
controlled. Unsupported egress/default-allow and setup credentials now refuse
|
||||
before workspace creation; regression tests cover these boundaries.
|
||||
|
||||
## Verify the real rein runtime and owner regression paths
|
||||
|
||||
```task
|
||||
id: SAND-WP-0015-T03
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "1a3002f6-c6a8-59d0-9d38-942028419de5"
|
||||
```
|
||||
|
||||
Run `make check`, the existing authenticated cross-request owner smoke, and a
|
||||
real runtime startup smoke. Verify the actual rein CLI and adapter imports,
|
||||
read-only runtime, private HOME, clean worktree, absent source, loopback-only
|
||||
network, and teardown. Keep the production Glas profile blocked.
|
||||
|
||||
Completed 2026-09-05. `make check`: lint clean, 132 tests passed. Authenticated
|
||||
owner API smoke `223db65b` returned HTTP 200 and proved exact consumer identity,
|
||||
stdin delivery, absent source, loopback-only network and complete teardown.
|
||||
Pinned runtime smoke `d4de9531` ran the actual rein CLI and imported its Claude
|
||||
adapter, proved read-only runtime/private HOME/clean worktree, retained private
|
||||
state across a second exec, and removed the workspace. No model call or
|
||||
credential acquisition occurred; T04 remains waiting.
|
||||
|
||||
## Resolve Claude credentials, enforced egress, and production acceptance
|
||||
|
||||
```task
|
||||
id: SAND-WP-0015-T04
|
||||
status: wait
|
||||
blocked_on: message-from:rein-aharness
|
||||
blocking_reason: "Tracked as HARNESS-WP-0008-T07 (owner, attended): accept spend memo infd-20261014-b01 (up to three attempts within EUR 10, observed-usage settlement), rebuild/install ee3d0a53 on railiance01, rehearse, renew, then a successful attempt."
|
||||
priority: high
|
||||
state_hub_task_id: "58817ef6-76d9-5e34-908f-c024e6c99f93"
|
||||
```
|
||||
|
||||
The 2026-09-05 routing lookup found no concrete Anthropic/Claude workload lane.
|
||||
The generic OpenBao template is not a delegable lane; the OpenRouter AppRole
|
||||
belongs to another rein. Credential owner railiance-platform/OpenBao plus
|
||||
rein-aharness must establish a concrete Claude-compatible route and delivery/
|
||||
revocation contract before values can be requested or supplied to the runtime.
|
||||
|
||||
Sand-boxer must then implement the matching enforced provider egress contract
|
||||
(including DNS/TLS and denied-destination proof) and pin/deploy the actual
|
||||
Claude executable. No unrestricted-network or interactive-login substitute is
|
||||
permitted. Review the deployed runtime/profile with Glas, run its real-rein
|
||||
acceptance fixture, and update only the proven profile's readiness. No
|
||||
production schedule is enabled by the runtime-startup smoke.
|
||||
|
||||
This task and SAND-WP-0014-T05 remain open until those gates pass. The detailed
|
||||
return contract and runtime proof are in `docs/bwrap-runtime.md`; the live
|
||||
cross-repo residual remains `GLAS-IN-0002`.
|
||||
|
||||
## 2026-09-05 transport implementation evidence
|
||||
|
||||
T04 remains waiting on owner machine authentication, protected credential
|
||||
delivery, pinned Claude startup and the real model proof. CCR-2026-0016 custody
|
||||
is confirmed by a metadata-only check of live version 2; no value was read.
|
||||
HTTPS egress is now implemented as an exact owner/profile allowlisted CONNECT
|
||||
proxy while retaining an isolated network namespace. Non-secret live smoke
|
||||
sandbox e290e788 verified provider TLS response, undeclared destination and
|
||||
direct-IP denial, and proxy/workspace teardown. See docs/bwrap-egress.md.
|
||||
|
||||
## 2026-09-05 credential transport owner return
|
||||
|
||||
Owner-bound exec-env transport is implemented and proved with a synthetic
|
||||
provider in real sandbox 0e5fb35a. Exact route/profile/project/actor/run binding,
|
||||
child-only key injection, pre-truncation redaction, next-exec absence and teardown
|
||||
pass. No real key read or production profile activation. See docs/bwrap-credentials.md.
|
||||
SECRETS-WP-0009 now owns native AppRole adoption for CCR-2026-0016, including
|
||||
a data-only consumer read policy. Its production exec refuses before OpenBao
|
||||
until SECRETS-WP-0007-T04 and SECRETS-WP-0008-T02/T06 deliver canonical
|
||||
authorization/consume and service authority. T04 remains waiting on that live
|
||||
owner path, the pinned Claude executable and real model acceptance.
|
||||
|
||||
## 2026-09-06 combined-runtime candidate returned to Glas
|
||||
|
||||
Answered the GLAS-WP-0015 handoff. Added candidate profile
|
||||
`profile.claude-agent-dev-proof` version 1.1.0 (`ext.bwrap`, localhost-only,
|
||||
`default: deny` with declared `api.anthropic.com:443`) for GLAS-WP-0012 review.
|
||||
The committed profile grants no egress by itself: `ext.bwrap` refuses it unless
|
||||
the owner extension config allowlists the destination, and a regression test now
|
||||
asserts that fail-closed default. `make check`: lint clean, 172 tests passed.
|
||||
|
||||
Resolved the differing project examples in favour of the acceptance runner's
|
||||
`actor: agt` / `project: glas-local-proof`, and bound the documented credential
|
||||
route to that single project rather than broadening it. The full return contract
|
||||
— profile id/revision, host scope, consumer tuple, runtime digest and mount
|
||||
paths, declared egress, and the value-free denial/cleanup receipts — is in
|
||||
`docs/bwrap-runtime.md`.
|
||||
|
||||
T04 stays `wait`: the concrete Claude credential lane and delivery/revocation
|
||||
contract, owner machine authentication, the pinned Claude executable with its
|
||||
own proxy startup proof, and the real-model acceptance are still operator and
|
||||
upstream-owner gated. Provider workspace scope and spend limit remain operator
|
||||
inputs. `SAND-WP-0014-T05` remains open on the same gates.
|
||||
|
||||
|
||||
## Package and prove the pinned Claude executable independently of custody
|
||||
|
||||
```task
|
||||
id: SAND-WP-0015-T05
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "f0163cda-b581-56a0-a0dd-9c8a436f5bbe"
|
||||
```
|
||||
|
||||
Split the independent executable packaging/startup portion from T04 so credential
|
||||
custody does not hide completed local runtime work. Implement explicit binary
|
||||
pinning, reject mismatches/host wrappers, and prove actual startup with the
|
||||
broker proxy environment and complete cleanup. T04 retains protected deployment,
|
||||
credential adoption and real-provider acceptance.
|
||||
|
||||
|
||||
## Pinned Claude candidate and isolated startup — 2026-09-06
|
||||
|
||||
The builder now accepts `--claude-binary`, `--claude-sha256` and
|
||||
`--claude-version` together. It requires a regular native ELF file, verifies the
|
||||
exact copied bytes, refuses symlinks/wrappers/mismatches and existing destination
|
||||
files, and copies only the executable as bin/claude mode 0755. The expected
|
||||
version is metadata until the sandbox smoke checks the actual output. No
|
||||
interactive HOME, provider key or login state is copied.
|
||||
|
||||
Candidate provenance: the explicitly selected installed native release
|
||||
`/home/worsch/.local/share/claude/versions/2.1.263`, SHA-256
|
||||
`26d020351e8112f4006790f3cfce43b4c9df0c1bb1d0e542364d64151b81d5ba`.
|
||||
This is a digest pin of the selected local artifact, not an upstream signature
|
||||
verification claim. Complete runtime candidate:
|
||||
`/tmp/sandboxer-claude-runtime-20260906`, SHA-256
|
||||
`5cf9a16c5d77a16bdb2cb5b3df06ea655356bc2d44741791e3fedfee20d7e922`.
|
||||
Mount remains `/opt/sandboxer/runtime`; Claude is `/opt/sandboxer/runtime/bin/claude`.
|
||||
Rein sources remain 1429db5 / llm-connect 0056094; resolved dependency versions
|
||||
are recorded in the evidence. Rebuilding may resolve different dependencies;
|
||||
the complete digest pins this artifact, not future builds.
|
||||
|
||||
Sandbox f8821ec2 ran real `claude --version` -> `2.1.263 (Claude Code)` plus rein
|
||||
CLI/import checks, with owner allowlisted provider egress and HTTPS_PROXY set
|
||||
by the broker. Runtime read-only, private 0700 HOME/state persistence, clean Git,
|
||||
absent source and loopback-only interfaces passed; workspace and proxy removed.
|
||||
`make check`: lint passed, 175 tests passed.
|
||||
|
||||
Evidence: docs/evidence/SAND-WP-0015-claude-startup-2026-09-06.json.
|
||||
Reproduce with the builder options above and `scripts/smoke-bwrap-runtime.py
|
||||
--runtime-path <candidate> --runtime-sha256 <digest> --claude-version 2.1.263`.
|
||||
|
||||
Limits: `--version` proves startup with the proxy environment, not that Claude
|
||||
has sent a provider request through it. No credential acquisition or model run.
|
||||
The /tmp candidate is not production placement; protected artifact installation,
|
||||
owner configuration, credential adoption and real-model proof remain T04 gates.
|
||||
|
||||
|
||||
## Install the pinned runtime in the local owner's protected store
|
||||
|
||||
```task
|
||||
id: SAND-WP-0015-T06
|
||||
status: done
|
||||
priority: high
|
||||
assignee: the-custodian
|
||||
state_hub_task_id: "f85f54c9-1081-5d0c-95ad-aca20ede8d66"
|
||||
```
|
||||
|
||||
HFACT-WP-0001-T04 consumes this independent installation return. Reuse the
|
||||
already verified combined candidate with digest
|
||||
`5cf9a16c5d77a16bdb2cb5b3df06ea655356bc2d44741791e3fedfee20d7e922`;
|
||||
do not rebuild resolved dependencies. Publish it under the local owner's
|
||||
private artifact store, verify exact copied content/modes and refusal of unsafe
|
||||
or corrupted destinations, and prove startup/read-only access/teardown through
|
||||
the installed path. The owner remains trusted; this is not root-owned storage.
|
||||
T04 retains real credential adoption, owner configuration, model acceptance
|
||||
and production placement. No credential route or profile is activated here.
|
||||
|
||||
`scripts/install-bwrap-runtime.py` installs below the owner's home into a
|
||||
mode-0700 store, verifies before and after copying, preserves artifact modes
|
||||
and internal symlinks, serializes publishers and never overwrites a published
|
||||
artifact. Existing modified artifacts are refused rather than repaired silently.
|
||||
Group/other-writable ancestors, unsafe permission bits and store aliases refuse.
|
||||
Unit tests cover integrity, idempotence, copy-time corruption and access modes.
|
||||
|
||||
|
||||
T06 completed 2026-09-08 on bnt-lap001, local owner UID 1000. Installed the
|
||||
unchanged 358-entry / 245176062-byte candidate at
|
||||
`/home/worsch/.local/share/sandboxer/runtimes/5cf9a16c5d77a16bdb2cb5b3df06ea655356bc2d44741791e3fedfee20d7e922`.
|
||||
The private store is mode 0700; the installing owner remains trusted. A root-owned
|
||||
system installation was not claimed or required for this local owner contract.
|
||||
The installed-path sandbox smoke `51b59587` passed real Claude 2.1.263 and rein
|
||||
startup, read-only mount, private state persistence, clean worktree, absent source,
|
||||
loopback-only interfaces and complete workspace/proxy teardown. No model call
|
||||
or credential acquisition occurred. `make check`: lint clean, 188 tests passed.
|
||||
Two existing tests require the checkout directory name `sand-boxer`; the final
|
||||
full check used that canonical basename. Evidence:
|
||||
`docs/evidence/SAND-WP-0015-protected-local-install-2026-09-08.json`.
|
||||
T04 retains owner execution configuration, native credential/egress and real-model
|
||||
acceptance; Railiance installation needs its own target-specific return.
|
||||
|
||||
## 2026-09-09 factory metered route source return
|
||||
|
||||
Implemented the trusted, ephemeral Messages route described in
|
||||
[docs/bwrap-messages-route.md](../docs/bwrap-messages-route.md). Actual local
|
||||
bwrap owner transport proves provider key/ledger separation, direct-route denial,
|
||||
revocation and teardown; rein also proves metered request plus commit import and
|
||||
close replay. `make check`: lint clean, 199 passed. T04 remains waiting for the
|
||||
admitted provider-to-owner bootstrap, updated protected runtime/profile, Railiance
|
||||
placement, live compatibility and G0. Existing child-provider-key/direct-CONNECT
|
||||
proofs do not admit this different credential holder or metered profile. No CCR,
|
||||
secret read, deployment or paid request was performed.
|
||||
|
||||
## 2026-09-09 standalone owner packaging and runtime selection
|
||||
|
||||
Added frozen-lock owner build mode for the matched rein/llm/Glas/sandboxer set,
|
||||
with non-editable installation and recorded lock/source/package pins. Sand-boxer
|
||||
wheels now carry their profile and extension definitions, fixing a bwrap owner
|
||||
failure that source-checkout tests could hide. The trusted Messages binding can
|
||||
select the digest-pinned runtime without adding an API or profile override.
|
||||
`make check`: lint clean, 205 passed. Existing standalone workload builds remain
|
||||
supported. See docs/bwrap-runtime.md and rein's docs/owner-bootstrap.md.
|
||||
|
||||
The project's `evidence/2026-09-09-owner-bootstrap.json` records the actual candidate
|
||||
build and isolated installed-interpreter/CLI proof. T04 remains wait for accepted
|
||||
credential-to-owner delivery, current protected installation and Railiance placement,
|
||||
provider compatibility and G0. Source packaging does not reopen completed T01-T03,
|
||||
T05-T06, broaden the old CCRs or activate the previously installed 2.1.263 artifact.
|
||||
|
||||
The first standalone candidate exposed a stale cached local wheel despite current
|
||||
Git metadata. Owner builds now refresh all local distributions and compare every
|
||||
installed package source/definition file with its tracked source; stale, missing or
|
||||
extra files refuse the build. Four content-conformance regressions cover that gap.
|
||||
|
||||
|
||||
## 2026-09-27 installed launcher defect and guarded correction
|
||||
|
||||
REINAH-WP-0003's single admitted attempt completed native provider/companion
|
||||
apply, verify and exec, including scoped-token revocation. The claimed job
|
||||
`6efa9436-6a91-47c0-94e5-b31b5a0e7e3a` failed before any provider request
|
||||
reservation. The sandbox was destroyed and the source repository is unchanged.
|
||||
The parent EUR 10 liability remains held for explicit reconciliation; no retry
|
||||
or replacement authority is inferred.
|
||||
|
||||
Installed artifact `b6e4e8a4` contains uv-generated three-line shell launchers
|
||||
that still reference a vanished temporary build interpreter. The builder had
|
||||
only rewritten direct Python shebangs. Fixed both generated forms and added
|
||||
actual relocation/execution tests for direct, long and space-containing build
|
||||
paths, plus refusal of unknown governed launchers. Builder tests: 12 passed;
|
||||
focused lint passes. Rein's real installed bwrap probe reproduces both broken
|
||||
entrypoints at exit 127 with zero provider forwards; its new bootstrap checks
|
||||
refuse the malformed artifact before claiming.
|
||||
|
||||
T04 remains `wait` and this plan stays `blocked`: replacement artifact build/
|
||||
admission and a separately authorized successful model/tool/commit proof remain.
|
||||
The existing SAND-WP-0015 / REINAH-WP-0003 / SECRETS-WP-0009 records retain the
|
||||
work. No new task or workplan. Detailed non-secret receipts are in rein-aharness
|
||||
`docs/evidence/2026-09-27-metered-*.json`.
|
||||
|
||||
|
||||
## 2026-10-04 offline owner startup proof preparation
|
||||
|
||||
The existing no-credential smoke command now supports
|
||||
`--require-owner-entrypoints`: the admitted digest-pinned runtime must start both
|
||||
`rein-aharness --help` and `glas-harness --help`, and imported rein, llm-connect,
|
||||
Glas and sandboxer modules must originate inside the mounted artifact. Broken or
|
||||
missing launchers, failed help, timeout and external package origins cannot be
|
||||
recorded as startup success. The default standalone mode remains supported.
|
||||
Launcher diagnostics are not copied into the receipt. Focused tests exercise
|
||||
real subprocesses, including the vanished-build-interpreter failure.
|
||||
|
||||
This is preparation for a corrected immutable candidate's independent native
|
||||
startup proof; it is not that candidate's receipt or owner acceptance. T04 stays
|
||||
`wait`, the plan stays `blocked`, and the Sept27 artifact/liability evidence
|
||||
remains intact. Replacement artifact admission and pins, explicit retained
|
||||
EUR 10 reconciliation, and a separately authorized successful model/tool/commit
|
||||
attempt remain with the existing owners. No credentials, provider request,
|
||||
queue claim, policy activation or retry authorization is supplied here.
|
||||
|
||||
2026-10-04 current blocker reconciliation: September 27 native provider/companion
|
||||
apply/verify/exec and successful revocation supersede the original missing
|
||||
platform route. The remaining owner return is rein-aharness corrected artifact/
|
||||
profile admission, retained EUR10 reconciliation and fresh retry authority.
|
||||
T04 stays wait; no renewed trial is authorized. Full owner check: lint clean,
|
||||
219 tests passed (one existing Starlette deprecation warning).
|
||||
|
||||
## 2026-10-13 owner returns: reconciliation and corrected build
|
||||
|
||||
rein-aharness `d897a83` records owner reconciliation of the Sept 27 held
|
||||
liability: run `6efa9436` failed at launcher exit 127 before any provider
|
||||
reservation; liability stays at the full hold, exhausting that one-attempt
|
||||
envelope, and the owner authorized a new retry envelope on 2026-10-13.
|
||||
rein-aharness `9a4469d` records corrected runtime `ee3d0a53…` (replaces
|
||||
`b6e4e8a4…`; only rein moved, adding pre-claim launcher rejection), built with
|
||||
the sand-boxer builder fix; both launchers exit 0 inside bwrap. Receipts:
|
||||
rein-aharness `docs/evidence/2026-10-13-*.json`.
|
||||
|
||||
Remaining before T04 closes: install `ee3d0a53` on railiance01, pin its digest
|
||||
in owner config, pass `--require-owner-entrypoints` offline startup proof, then
|
||||
run the single authorized attempt to a successful model/tool/commit receipt.
|
||||
T04 stays `wait`; the plan stays `blocked`.
|
||||
|
||||
2026-10-14 check: rein-aharness pushed `9a4469d` (corrected build record) and
|
||||
opened HARNESS-WP-0008 (simpler metered retry, active). No railiance01 install/
|
||||
admission of `ee3d0a53` or successful acceptance receipt yet; request sent on
|
||||
hub message `82faf25c`. T04 stays `wait`; plan stays `blocked`.
|
||||
|
||||
2026-10-14 rein-aharness reply (thread `82faf25c`): step 1 done (`9a4469d` on
|
||||
origin/main). Steps 2-4 are HARNESS-WP-0008-T07. Budget is now up to three
|
||||
attempts within EUR 10 under memo `infd-20261014-b01` (observed-usage
|
||||
settlement); order: memo acceptance in the attended informed-decision sitting,
|
||||
rebuild and install `ee3d0a53` on railiance01, `metered-once --rehearse`,
|
||||
`owner renew`, first attempt. Railiance01 still pins `b6e4e8a4`. T04 stays `wait`.
|
||||
|
||||
2026-10-15 owner-approved rebuild: the pinned inputs of the `ee3d0a53` receipt
|
||||
(same four commits, uv.lock `8775904b…`, Claude 2.1.266 `19842705…`) rebuilt
|
||||
to `818b1b7c8292…f359`. The digest differs only because the builder embeds
|
||||
absolute source paths (`direct_url.json`, `build-info.json`). Owner-entrypoint
|
||||
startup smoke passed in sandbox `28df7a1d`; no credentials or model calls.
|
||||
Sent to rein-aharness (message `8a7962f7`, thread `82faf25c`) to record as the
|
||||
then-current corrected runtime for HARNESS-WP-0008-T07 and to load the unsigned
|
||||
memo sitting. Nothing installed on railiance01. T04 stays `wait`.
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
--- a/workplans/GLAS-WP-0012-first-local-profile-production-proof.md
|
||||
+++ b/workplans/GLAS-WP-0012-first-local-profile-production-proof.md
|
||||
@@ -439,7 +439,7 @@
|
||||
Latest owner dependency sources supersede the stale missing-lane account:
|
||||
September 27 native lanes were activated and revoked; corrected immutable
|
||||
owner runtime is `818b1b7c…` (October 15 startup proof), not yet installed or
|
||||
-admitted on Railiance. REINAH-WP-0008-T07 retains memo lifecycle acceptance,
|
||||
+admitted on Railiance. HARNESS-WP-0008-T07 retains memo lifecycle acceptance,
|
||||
installation, rehearsal, renewal/pin updates, native approvals and bounded
|
||||
successful model/tool/commit acceptance. Any subsequent build containing this
|
||||
patch has a different source/artifact pin requiring owner admission.
|
||||
|
|
@ -0,0 +1 @@
|
|||
sha256:881ab0f5ba3bb43f5404c8f4cd23f9628e014992ba88fc13f541f0fa8b7b1eaa
|
||||
|
|
@ -0,0 +1,262 @@
|
|||
# rein-aharness — Agent Instructions
|
||||
|
||||
## Repo Identity
|
||||
|
||||
**Purpose:** Claude-Code-CLI-driven rein: governed, unattended/scheduled agent runtime
|
||||
|
||||
**Domain:** infotech
|
||||
**Repo slug:** rein-aharness
|
||||
**Topic ID:** `cee7bedf-2b48-46ef-8601-006474f2ad7a`
|
||||
**Workplan prefix:** `REINAH-WP-`
|
||||
|
||||
`REIN-A-0001` through `REIN-A-0004` are immutable historical identifiers from
|
||||
before the fleet `PREFIX-WP-NNNN` convention. `HARNESS-WP-0001` through
|
||||
`HARNESS-WP-0003` and `HARNESS-WP-ADHOC-2026-09-04` are older ids from the
|
||||
agent-harness name. Preserve all of these in references and archives, but use
|
||||
the hub-registered `REINAH-WP-` prefix for every new workplan and task.
|
||||
|
||||
---
|
||||
|
||||
## Developer Workflow
|
||||
|
||||
```bash
|
||||
# Install (editable, including the governed profile-execution siblings)
|
||||
python3 -m venv .venv && source .venv/bin/activate
|
||||
pip install -e . -e ../llm-connect -e ../sand-boxer -e ../glas-harness pytest
|
||||
|
||||
# Test
|
||||
PYTHONPATH=".:$HOME/llm-connect" python3 -m pytest tests/ -q
|
||||
|
||||
# Run — bounded agentic session against a real target repo (real Claude Code
|
||||
# CLI call, real commit if successful)
|
||||
rein-aharness run --task-file examples/task-hello-sandbox.json --no-hub --no-metrics
|
||||
|
||||
# Build the Railiance deploy image
|
||||
make image
|
||||
```
|
||||
|
||||
## State Hub Integration
|
||||
|
||||
The Custodian State Hub tracks work across all domains. Interact via HTTP REST —
|
||||
there is no MCP server for Codex agents.
|
||||
|
||||
| Context | URL |
|
||||
|---------|-----|
|
||||
| Local workstation | `http://127.0.0.1:8000` |
|
||||
| Remote (railiance01, in-cluster) | `http://10.43.68.154:8000` |
|
||||
| Optional local edge relay | http://127.0.0.1:18080 |
|
||||
|
||||
When an operator has enabled the edge relay, set API_BASE to the relay URL.
|
||||
Queueable writes return an explicit queued receipt if the central hub is
|
||||
unreachable. Treat that as pending local evidence, then ask the operator to run
|
||||
statehub outbox status/replay after connectivity returns.
|
||||
|
||||
### Orient at session start
|
||||
|
||||
```bash
|
||||
# Offline brief — works without hub connection
|
||||
cat .custodian-brief.md
|
||||
|
||||
# Active workplans for this domain
|
||||
curl -s "http://127.0.0.1:8000/workplans/?topic_id=cee7bedf-2b48-46ef-8601-006474f2ad7a&status=active" \
|
||||
| python3 -m json.tool
|
||||
|
||||
# Check inbox
|
||||
curl -s "http://127.0.0.1:8000/messages/?to_agent=rein-aharness&unread_only=true" \
|
||||
| python3 -m json.tool
|
||||
```
|
||||
|
||||
Mark a message read:
|
||||
```bash
|
||||
curl -s -X PATCH "http://127.0.0.1:8000/messages/<id>/read" \
|
||||
-H "Content-Type: application/json" -d '{}'
|
||||
```
|
||||
|
||||
### Log progress (required at session close)
|
||||
|
||||
```bash
|
||||
curl -s -X POST http://127.0.0.1:8000/progress/ \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"summary": "what was done",
|
||||
"event_type": "note",
|
||||
"author": "codex",
|
||||
"workplan_id": "<uuid>",
|
||||
"task_id": "<uuid>"
|
||||
}'
|
||||
```
|
||||
|
||||
Omit `workplan_id` / `task_id` when not applicable.
|
||||
|
||||
### Update task status
|
||||
|
||||
```bash
|
||||
curl -s -X PATCH "http://127.0.0.1:8000/tasks/<task_id>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"status": "progress"}'
|
||||
# values: wait | todo | progress | done | cancel
|
||||
```
|
||||
|
||||
### Flag a task for human review
|
||||
|
||||
```bash
|
||||
curl -s -X PATCH "http://127.0.0.1:8000/tasks/<task_id>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"needs_human": true, "intervention_note": "reason"}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Session Protocol
|
||||
|
||||
**Start:**
|
||||
1. `cat .custodian-brief.md` — domain goal and open workplans (offline-safe)
|
||||
2. Check inbox: `GET /messages/?to_agent=rein-aharness&unread_only=true`; mark read
|
||||
3. Scan workplans: `ls workplans/` — note `status: ready`, `active`, or `blocked` files and open tasks
|
||||
4. Check human-needed tasks: `GET /tasks/?needs_human=true`
|
||||
|
||||
**During work:**
|
||||
- Update task statuses in workplan files as tasks progress
|
||||
- Record significant decisions via `POST /decisions/`
|
||||
|
||||
**Close:**
|
||||
1. Update workplan file task statuses to reflect progress
|
||||
2. If finishing a workplan: hand off **residuals** as live work records first
|
||||
(intake with `origin: residual` + `origin_ref: <WP-id>`, or a next workplan /
|
||||
decision / engagement). Do not park leftovers only in prose or `SCOPE.md`.
|
||||
Canon: `the-custodian/canon/standards/work-record-types_v0.1.md` § Residuals.
|
||||
3. Log: `POST /progress/` with a summary of what changed (name handoff ids)
|
||||
4. After workplan file changes, run:
|
||||
```bash
|
||||
statehub fix-consistency
|
||||
```
|
||||
Coding agents should run this directly; ask the operator only if the CLI or
|
||||
State Hub API is unavailable. This syncs task status from files into the hub DB.
|
||||
|
||||
---
|
||||
|
||||
## Credential and access routing
|
||||
|
||||
**Audience:** Codex, Claude Code, Grok, and custodian agents that call **llm-connect**
|
||||
for inference. Run this check **before** requesting secrets, API keys, SSH access,
|
||||
login tokens, or database passwords — in any repo, not only `ops-warden`.
|
||||
|
||||
ops-warden **issues SSH certificates only** (`warden sign`, `cert_command`). Every
|
||||
other credential need belongs to another subsystem. **Do not** message
|
||||
`ops-warden` on State Hub expecting a secret value; the reply is a pointer, not a key.
|
||||
|
||||
### Lookup (do this first)
|
||||
|
||||
```bash
|
||||
warden route find "<describe your need>" --json
|
||||
warden route show <catalog-id> --json
|
||||
```
|
||||
|
||||
Requires the `warden` CLI from `~/ops-warden` (`uv tool install .` or `uv run warden`).
|
||||
|
||||
| Agent runtime | How to orient |
|
||||
| --- | --- |
|
||||
| **Codex / Grok** (shell, HTTP State Hub) | `warden route` commands above; inbox `to_agent=rein-aharness` is for coordination, not secret vending |
|
||||
| **Claude Code** (MCP when available) | `get_domain_summary("custodian")` for workplans; **still** use `warden route` for credential ownership |
|
||||
| **llm-connect** (inference service) | Never put secret retrieval in prompts; route custody to OpenBao/operator paths surfaced by `warden route` |
|
||||
|
||||
### Quick routing table
|
||||
|
||||
| I need… | Owner | ops-warden executes? |
|
||||
| --- | --- | --- |
|
||||
| SSH cert (`adm`/`agt`/`atm`) | ops-warden | **Yes** — `warden sign` |
|
||||
| API key, DB password, provider token | OpenBao (`railiance-platform`) | No — route only |
|
||||
| Login / OIDC / MFA | key-cape / Keycloak | No — route only |
|
||||
| Authorization decision | flex-auth | No — route only |
|
||||
| activity-core → issue-core emission | activity-core + issue-core | No — `warden route show activity-core-issue-sink` |
|
||||
| SSH tunnel | ops-bridge (+ `cert_command` from warden) | No — route only |
|
||||
|
||||
### Anti-patterns (do not do these)
|
||||
|
||||
- `POST /messages/` to `ops-warden` asking for `ISSUE_CORE_API_KEY`, `OPENROUTER_API_KEY`, etc.
|
||||
- Inventing `warden secret`, `warden login`, `warden bao`, `warden tunnel` — they do not exist
|
||||
- Pasting secrets into Git, State Hub, workplans, logs, or chat
|
||||
|
||||
### Other capabilities (reuse-surface)
|
||||
|
||||
Non-credential capabilities are usually discovered through **reuse-surface** federation
|
||||
(`reuse-surface` registry / `capability.*` indexes). Credential routing is inlined in
|
||||
every repo's agent instructions because it is high-frequency, high-risk, and easy to
|
||||
get wrong.
|
||||
|
||||
**Canon:** `~/ops-warden/wiki/CredentialRouting.md` · catalog `~/ops-warden/registry/routing/catalog.yaml`
|
||||
|
||||
<!-- REPO-AGENTS-EXTENSIONS -->
|
||||
<!-- Append repo-specific agent instructions below this marker.
|
||||
The state-hub template sync preserves content after this line. -->
|
||||
|
||||
---
|
||||
|
||||
## Workplan Convention (ADR-001)
|
||||
|
||||
Work items originate as files in this repo — not in the hub. The hub is a
|
||||
read/cache/index layer that rebuilds from files.
|
||||
|
||||
**File location:** `workplans/REINAH-WP-NNNN-<slug>.md`
|
||||
|
||||
**Archived location:** finished workplans may move to
|
||||
`workplans/archived/YYMMDD-REINAH-WP-NNNN-<slug>.md`. The `YYMMDD` prefix is
|
||||
the completion/archive date; the frontmatter `id` does not change.
|
||||
|
||||
**Ad Hoc Tasks:** small opportunistic fixes discovered during a session use
|
||||
`workplans/ADHOC-YYYY-MM-DD.md` with task ids `ADHOC-YYYY-MM-DD-T01`, etc. Use
|
||||
this only for low-risk work completed directly; create a normal workplan for
|
||||
anything needing analysis, design, approval, dependencies, or multiple phases.
|
||||
|
||||
**Frontmatter:**
|
||||
|
||||
```yaml
|
||||
---
|
||||
id: REINAH-WP-NNNN
|
||||
type: workplan
|
||||
title: "..."
|
||||
domain: infotech
|
||||
repo: rein-aharness
|
||||
status: proposed | ready | active | blocked | backlog | finished | archived
|
||||
owner: codex
|
||||
topic_slug: ...
|
||||
created: "YYYY-MM-DD"
|
||||
updated: "YYYY-MM-DD"
|
||||
state_hub_workstream_id: "<uuid>" # fix-consistency — do not edit (legacy field name; workplan UUID)
|
||||
---
|
||||
```
|
||||
|
||||
Use `proposed` for a new draft, `ready` after review against current repo
|
||||
state, and `finished` after implementation. `stalled` and `needs_review` are
|
||||
derived health labels, not frontmatter statuses.
|
||||
|
||||
**Terminology:** workplan is the fleet term; `workstream` appears only in legacy
|
||||
API/MCP/frontmatter bridges until `STATE-WP-0069` retires them — see
|
||||
`the-custodian/canon/standards/workplan-terminology-fleet_v0.1.md`.
|
||||
|
||||
**Task block format** (one per `##` section):
|
||||
|
||||
```
|
||||
## Task Title
|
||||
|
||||
` ` `task
|
||||
id: REINAH-WP-NNNN-T01
|
||||
status: wait | todo | progress | done | cancel
|
||||
priority: high | medium | low
|
||||
state_hub_task_id: "<uuid>" # written by fix-consistency — do not edit
|
||||
` ` `
|
||||
|
||||
Task description text.
|
||||
```
|
||||
|
||||
Status progression: `todo` → `progress` → `done`; use `wait` for waiting/blocked work and `cancel` for stopped work.
|
||||
|
||||
**Residuals when finishing:** actionable leftovers become live work records
|
||||
before `status: finished` — usually an intake (`origin: residual`,
|
||||
`origin_ref: REINAH-WP-NNNN`) or a spawned workplan. Residual is a *role*,
|
||||
not a kind. Fleet list lives on State Hub, not in `SCOPE.md`.
|
||||
|
||||
To create a new workplan:
|
||||
1. Write the file following the format above
|
||||
2. Run `statehub fix-consistency` locally; ask the operator only if the CLI or
|
||||
State Hub API is unavailable.
|
||||
|
|
@ -0,0 +1,354 @@
|
|||
---
|
||||
id: REINAH-WP-0008
|
||||
type: workplan
|
||||
title: "Simpler metered retry: free rehearsal, zero-cost hold release, one-command renewal"
|
||||
domain: infotech
|
||||
repo: rein-aharness
|
||||
status: blocked
|
||||
owner: claude-code
|
||||
topic_slug: rein-aharness
|
||||
priority: high
|
||||
created: "2026-10-13"
|
||||
updated: "2026-10-16"
|
||||
related:
|
||||
- REINAH-WP-0003
|
||||
state_hub_workstream_id: "187c2599-84a6-5764-a14f-7e9121975bf2"
|
||||
---
|
||||
|
||||
# REINAH-WP-0008 — Simpler metered retry
|
||||
|
||||
## Origin
|
||||
|
||||
On 2026-10-13 the owner reconciled the 2026-09-27 hold for run `6efa9436`
|
||||
(`docs/evidence/2026-10-13-held-liability-reconciliation.json`). A corrected
|
||||
runtime `ee3d0a53…` was built
|
||||
(`docs/evidence/2026-10-13-corrected-owner-runtime-build.json`). The owner
|
||||
authorized a new envelope and one retry.
|
||||
|
||||
Preparing that retry took too many owner ceremonies:
|
||||
|
||||
1. The runtime is built on the workstation and copied to railiance01 by hand.
|
||||
2. `owner.json` and the spend policy hold five digests that the owner edits
|
||||
by hand.
|
||||
3. A failed attempt keeps its full hold even when no provider request was
|
||||
made, so every failure needs a new envelope, ledger and pins.
|
||||
4. Every attempt needs six native approvals.
|
||||
5. The launcher bug that failed run `6efa9436` was found only by spending
|
||||
money. A free check on the host would have caught it.
|
||||
|
||||
The owner agreed (2026-10-13) to fix items 5, 3 and 2 here, and to record
|
||||
the rest as decisions instead of blocking on them.
|
||||
|
||||
**Goal:** a retry becomes: install the artifact, rehearse for free, approve
|
||||
once, run.
|
||||
|
||||
## A. Free rehearsal on the host
|
||||
|
||||
```task
|
||||
id: REINAH-WP-0008-T01
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "c6536c8d-7862-5de6-ad2b-401077b60c04"
|
||||
```
|
||||
|
||||
Add a free rehearsal to the owner check. It runs everything
|
||||
`metered-once --check` does, then executes the admitted runtime's
|
||||
`rein-aharness` and `glas-harness` launchers inside bwrap from the mount
|
||||
path, with no network, credentials, provider route or ledger write. The
|
||||
sandbox profile is already resolved by `--check`. It prints one receipt with
|
||||
the runtime digest and each launcher's exit code.
|
||||
|
||||
Today's launcher check reads only the first line of each launcher. This
|
||||
rehearsal runs them, the same way the failing run did.
|
||||
|
||||
**Done when:** tests prove the rehearsal fails on a runtime whose launchers
|
||||
point at a missing build directory (the `6efa9436` failure) and passes on a
|
||||
relocated runtime. It must make no network calls and write nothing. The
|
||||
owner procedure in `docs/owner-bootstrap.md` makes the rehearsal a required
|
||||
step before any paid attempt.
|
||||
|
||||
### Implemented — 2026-10-13
|
||||
|
||||
`rein-aharness metered-once --owner-config <owner.json> --rehearse` (the flag
|
||||
implies `--check`, so it never reads a provider key or claims). The code is in
|
||||
`rein_aharness/owner_rehearsal.py`. It uses sand-boxer's isolation flags and
|
||||
read-only system binds, with the runtime read-only at `/opt/sandboxer/runtime`,
|
||||
`--unshare-net` and `--clearenv`. It runs each Python launcher with `--help`,
|
||||
plus `claude --version` when `build-info.json` declares Claude. A missing
|
||||
bwrap fails closed.
|
||||
|
||||
Tests (`tests/test_owner_rehearsal.py`, `tests/test_owner_bootstrap.py`):
|
||||
|
||||
- With real bwrap, a relocated runtime passes. The uv trampoline that
|
||||
re-executes a deleted build interpreter fails with 127, as run `6efa9436`
|
||||
did.
|
||||
- The rehearsal never claims and never reads the key.
|
||||
- The command line has no writable bind and no credential.
|
||||
|
||||
The corrected runtime `ee3d0a53…` rehearses clean: rein-aharness 0,
|
||||
glas-harness 0, claude 0.
|
||||
|
||||
## B. Release holds that provably cost nothing
|
||||
|
||||
```task
|
||||
id: REINAH-WP-0008-T02
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "6a413f5a-4ac2-5d5e-9b19-71b33445c0a9"
|
||||
```
|
||||
|
||||
`SpendLedger.reconcile` keeps the full hold (`max(held, observed)`). Add an
|
||||
evidence-backed release. If the run has zero provider-request reservations
|
||||
and its request route is revoked, reconciliation may set the liability to
|
||||
the observed amount. Otherwise the current rule stands. The release records
|
||||
which evidence allowed it, and the CLI flag says so, for example
|
||||
`--release-unused-hold`.
|
||||
|
||||
This changes a deliberate safety rule, so it needs owner sign-off on the
|
||||
exact rule before merge. That sign-off is recorded in T04's decision note.
|
||||
|
||||
**Done when:** tests cover all four cases. A run with no request
|
||||
reservations and a revoked route releases. A run with any request
|
||||
reservation, or with a live route, refuses the release. A second, identical
|
||||
reconcile changes nothing. The envelope is never marked breached by a
|
||||
release.
|
||||
|
||||
### Widened to observed-usage settlement — 2026-10-14
|
||||
|
||||
On 2026-10-13 the owner asked for three attempts within EUR 10 and chose
|
||||
option B: settle to actual usage instead of releasing only unused holds.
|
||||
Under full-hold accounting each request holds USD 4.64 (1M context plus
|
||||
64k output), so EUR 10 cannot fit three attempts.
|
||||
|
||||
Implemented as an opt-in envelope term (`settlement: observed-usage`) plus
|
||||
`max_runs`; see `docs/owner-bootstrap.md`. Under it:
|
||||
|
||||
- a completed request is charged its gateway-metered cost;
|
||||
- runs and reconciliations are charged
|
||||
`max(metered requests, reported or attested cost)`;
|
||||
- unfinished requests keep their full hold, and overruns still freeze the
|
||||
envelope.
|
||||
|
||||
The run 6efa9436 shape (failure before any request) now reconciles to its
|
||||
attested cost. Defaults and existing digests are unchanged. Tests:
|
||||
`tests/test_spend_settlement.py`, plus renewal cases in
|
||||
`tests/test_owner_renew.py`.
|
||||
|
||||
Merge-ready, but the rule still needs the owner's acceptance in decision
|
||||
`a93619a9`.
|
||||
|
||||
## D. One owner command for renewal
|
||||
|
||||
```task
|
||||
id: REINAH-WP-0008-T03
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "87457216-2371-51ca-b3d4-3917e6f180a2"
|
||||
```
|
||||
|
||||
Add `rein-aharness owner renew --owner <owner.json> --runtime <digest>
|
||||
--envelope <envelope.json>`. It does three things:
|
||||
|
||||
1. It refuses unless every reservation in the current ledger is settled.
|
||||
2. It writes the new spend policy, initializes a fresh ledger, and updates
|
||||
the runtime and spend-policy pins in `owner.json` together. Old files are
|
||||
kept as backups. Existing ledgers are never reset.
|
||||
3. It prints one summary for the owner to review: old to new runtime, old
|
||||
to new policy digest, envelope cap and attempt limit, and ledger path.
|
||||
|
||||
Add a dry-run mode that only prints the summary. Afterwards, run the T01
|
||||
rehearsal.
|
||||
|
||||
**Done when:** a test renews from the 2026-09-27 layout to a new envelope.
|
||||
The owner check passes on the result. A second renew with an unsettled
|
||||
reservation refuses. No digest has to be typed by hand.
|
||||
|
||||
### Implemented — 2026-10-13
|
||||
|
||||
The command is `rein-aharness owner renew --owner-config --policy --ledger
|
||||
--runtime-path --envelope [--apply]`, in `rein_aharness/owner_renew.py`.
|
||||
Without `--apply` it is a dry run. Choices:
|
||||
|
||||
- **Envelope file:** only the envelope fields may change. Worker, target,
|
||||
profile and grant pins carry over, and `SpendPolicy` validation runs on
|
||||
the result.
|
||||
- **Paths stay the same:** the old ledger is renamed to
|
||||
`.retired-<old envelope>`, never reset, and the old policy and owner.json
|
||||
are kept as `.bak`. Secrets Engine pins the `exec_owner` files by sha256
|
||||
and the ledger path through its fixed environment, so keeping the paths
|
||||
means the catalog needs only new file hashes. The command prints them.
|
||||
- **Refusals:** a held reservation, an open request route, an open SQLite
|
||||
transaction, an owner.json that doesn't match the policy, a reused
|
||||
envelope id, a disallowed field, or leftovers from an earlier renewal.
|
||||
|
||||
Tests (`tests/test_owner_renew.py`):
|
||||
|
||||
- A dry run changes nothing.
|
||||
- An applied renewal passes the real owner check (`prepare`) at the
|
||||
unchanged paths.
|
||||
- A charged reservation, the railiance01 state after the 2026-10-13
|
||||
reconciliation, renews, and the row survives in the retired ledger.
|
||||
- Bad envelopes and a second apply both refuse.
|
||||
|
||||
Finding for T06: renewal still changes the pinned file hashes, so each
|
||||
renewal needs a Secrets Engine catalog update and fresh approvals.
|
||||
|
||||
## C. Envelopes that cover several attempts (decision)
|
||||
|
||||
```task
|
||||
id: REINAH-WP-0008-T04
|
||||
status: done
|
||||
decision_id: "a93619a9-8a70-40d8-aaf9-c29357d2a043"
|
||||
priority: medium
|
||||
state_hub_task_id: "898e02af-51bb-5e8d-93d0-ae908466c612"
|
||||
```
|
||||
|
||||
Owner decision: should future envelopes be admitted as "up to N attempts
|
||||
within EUR X" instead of one attempt? The EUR 10 cap already fits two
|
||||
USD 4.64 request holds. Record the choice, together with T02's sign-off,
|
||||
as a decision note in `docs/`. T03's envelope format then reflects it.
|
||||
|
||||
### Decided — 2026-10-14
|
||||
|
||||
The owner chose up to three attempts within EUR 10 (EUR 5 per attempt) and
|
||||
observed-usage settlement, and resolved hub decision `a93619a9` with that
|
||||
choice. Record: `docs/decisions/2026-10-14-spend-envelope-settlement.md`.
|
||||
The terms are in spend memo `infd-20261014-b01`.
|
||||
|
||||
## E. Ship the owner runtime as a container image (decision)
|
||||
|
||||
```task
|
||||
id: REINAH-WP-0008-T05
|
||||
status: wait
|
||||
needs_human: true
|
||||
decision_id: "e6ca2f07-2708-4013-bfba-bf89fa0f1cb5"
|
||||
priority: low
|
||||
state_hub_task_id: "b486e86b-9d0f-501d-a394-771b40f771ee"
|
||||
```
|
||||
|
||||
Decision for later, with Platform (railiance) and Sand-boxer: should the
|
||||
owner runtime ship through the registry and GitOps, using the existing
|
||||
`make image` path, instead of rsync and a manual digest check? Out of scope
|
||||
here. If accepted, hand it off as a residual intake to the owning repos.
|
||||
|
||||
## F. Approvals per envelope (decision)
|
||||
|
||||
```task
|
||||
id: REINAH-WP-0008-T06
|
||||
status: wait
|
||||
needs_human: true
|
||||
decision_id: "e8986e40-f6f8-4c87-b704-bff50cc4958b"
|
||||
priority: low
|
||||
state_hub_task_id: "4cf721e6-724b-5c64-91bb-67b098bb97af"
|
||||
```
|
||||
|
||||
Decision for later, with Secrets Engine: should one approval set cover an
|
||||
envelope's attempts instead of six native approvals per attempt? The work
|
||||
lives outside this repo. If accepted, hand it off as a residual intake.
|
||||
|
||||
## Retry on the corrected runtime
|
||||
|
||||
```task
|
||||
id: REINAH-WP-0008-T07
|
||||
status: wait
|
||||
needs_human: true
|
||||
blocking_reason: "Owner: accept spend memo infd-20261014-b01, then attended install, rehearsal, renew and approvals on railiance01"
|
||||
priority: high
|
||||
state_hub_task_id: "80318842-ccaa-5484-a54a-3d9981eaec8b"
|
||||
```
|
||||
|
||||
Waits on T01 and T03. It does not need T02.
|
||||
|
||||
Steps:
|
||||
|
||||
1. Rebuild `ee3d0a53…`, or the then-current corrected runtime, from the
|
||||
pinned commits.
|
||||
2. Install it on railiance01.
|
||||
3. Run the T01 rehearsal.
|
||||
4. Renew with T03 to the owner-authorized new envelope (EUR 10, authorized
|
||||
2026-10-13).
|
||||
5. Run exactly one attended attempt, which consumes fresh native approvals.
|
||||
|
||||
Record the result in REINAH-WP-0003-T05/T06.
|
||||
|
||||
### Waiting on the owner — 2026-10-14
|
||||
|
||||
Everything this repo can do is done: the rehearsal (T01), the settlement rule
|
||||
(T02, T04) and renewal (T03) are in place. Draft memo `infd-20261014-b01` is
|
||||
in informed-decision `ee6a65b`. What remains needs the owner, in order:
|
||||
|
||||
1. Accept the memo in the attended sitting.
|
||||
2. ~~Rebuild `ee3d0a53` from the pinned commits~~ (done 2026-10-15 as
|
||||
`818b1b7c…`, see below) and install it on railiance01.
|
||||
3. Run `metered-once --rehearse`.
|
||||
4. Run `owner renew` with the memo's terms, then update the Secrets Engine
|
||||
file pins.
|
||||
5. Approve the native actions and run the first attempt.
|
||||
|
||||
Sand-boxer's steps 2–4 in message `82faf25c` are this same sequence.
|
||||
|
||||
### Runtime rebuilt — 2026-10-15
|
||||
|
||||
Step 1 is done. The owner approved a rebuild because `ee3d0a53…` was not kept
|
||||
on the workstation. Sand-boxer built `818b1b7c…` from the same pinned inputs
|
||||
and builder. The digest differs only because the build-source paths are
|
||||
embedded in the artifact. The bwrap startup smoke passed (sandbox `28df7a1d`)
|
||||
with no credentials and no model run. `818b1b7c…` is now the install
|
||||
candidate. It is not installed on railiance01 (still `b6e4e8a4…`) or admitted.
|
||||
Receipt: `docs/evidence/2026-10-15-rebuilt-owner-runtime.json`. The memo still
|
||||
still has to be created, admitted, loaded and accepted first. The owner steps
|
||||
are in informed-decision `docs/batches/2026-10-14-three-attempts/OPERATOR.md`.
|
||||
|
||||
### Memo queued — 2026-10-15
|
||||
|
||||
Owner step 1 is in progress via the lifecycle intake path (owner's choice; the
|
||||
review-record approval counts as spend authority). Workspace draft
|
||||
`2ccb4bda-e318-51d2-a8dd-709db768494f`, review
|
||||
`review-e2d2e98f-4701-45b0-abc1-4fe9f00bd1a8`, queued by the owner in the State
|
||||
Hub memo dispatcher; approval intent `accept` by `helixforge-factory`. Record:
|
||||
informed-decision `d5f1021` (sitting.json, OPERATOR.md). The accepted approval id
|
||||
becomes `authority_ref` for `owner renew`. Install candidate remains `818b1b7c`.
|
||||
|
||||
|
||||
### Workload-only Glas source integration — 2026-10-16
|
||||
|
||||
Reviewed and integrated the one-line runtime-contract lock advance from Glas
|
||||
`30be81808140a3a3d137e731d1fe9aed59ad8581` to published workload-only credential
|
||||
delivery `7357115c8311e55ee3d495f5266f501a17ec3c89`. llm-connect `37436bb5` and
|
||||
sandboxer `221126cb` remain pinned. This is source integration under T07, not
|
||||
native artifact installation or admission. T07 remains waiting with its existing
|
||||
owner gates.
|
||||
|
||||
Prior preparation used rein base `be5ef400` plus isolated lock-only commit
|
||||
`c811fc43` to build candidate
|
||||
`634e65fee5517ebee0d1080805fd84c81bd46af26a2b38ec1ab14bd2be17bf8f`. The
|
||||
candidate's installed packages passed actual SandboxManager/Glas gateway proof
|
||||
with unchanged `harness.agent-dev-local@1.1.1` (still unverified): real rein/Glas
|
||||
CLI and Claude 2.1.266 startup, read-only runtime, synthetic host-file and
|
||||
inherited environment/child-process denial, exact task identity/private state,
|
||||
deterministic workspace Git effect, task removal and destruction. A separately
|
||||
labelled synthetic route fixture passed workload-only delivery, output redaction
|
||||
and clean subsequent housekeeping through the same actual manager. Wrong
|
||||
actor/project/run, expired execution, expiry cleanup, cancellation and
|
||||
post-cancellation refusal passed. All 141 Glas tests and seven rein lock/rehearsal
|
||||
tests passed; whole artifact digest remained unchanged after proof.
|
||||
|
||||
The first proof runner correctly encountered the manager's PermissionError for
|
||||
wrong consumer but expected ValueError/RuntimeError. Its finally cleanup ran;
|
||||
the runner catch was refined and the failure retained. This does not earn an
|
||||
unchanged execution success. No model/provider request, real credential read,
|
||||
queue access or spend occurred.
|
||||
|
||||
The preparation artifact is not the artifact of this integrated owner revision.
|
||||
Rebuild from the exact committed owner source and verify its new digest and
|
||||
actual-manager proof before considering installation. Source inputs, receipts,
|
||||
reproducible proof and attempt history are retained in the
|
||||
[Custodian evidence directory](../../the-custodian/docs/changes/netkingdom-target-2026-10-16/agent-isolation-next/README.md).
|
||||
The same directory records the integrated-source rebuild separately.
|
||||
|
||||
Retain `818b1b7c`, `ee3d0a53`, installed `b6e4e8a4` and their outcomes as historical
|
||||
evidence. Native acceptance still requires the queued spend memo lifecycle,
|
||||
owner artifact installation/rehearsal, renewal and Secrets Engine file-pin
|
||||
updates, required native approvals and bounded real-model/tool proof. Actual
|
||||
interactive assignment migration and external tool-path denial remain Custodian
|
||||
acceptance obligations; no profile readiness or supervision authority changed.
|
||||
Binary file not shown.
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"schema": "custodian.source-identity-correction.v1",
|
||||
"status": "source_applied_publication_pending",
|
||||
"files": {
|
||||
"HEAD-source/AGENTS.md": "sha256:5d01af0ea255eab568ae72f326378219d1be4c77a9bc001ae12f4a1be2660034",
|
||||
"HEAD-source/workplans/REINAH-WP-0008-simpler-metered-retry.md": "sha256:9aac375bdb2e93c57cd513ab8cd5bf0c0979a0699ea0181e6d405899d906bf68",
|
||||
"current-driver-patch-selection.json": "sha256:02f3e03ebbd59f307bc9c1651f824beec2f0f3240a894f7f34e243ec060c3e6c",
|
||||
"driver-proposals/glas-harness/workplans/GLAS-WP-0012-first-local-profile-production-proof.md": "sha256:23f97bc183bef2b0f38eb40c64bc0bb010df1c1eb4596ff3b5e0f73da7c2fcfc",
|
||||
"driver-proposals/sand-boxer/workplans/SAND-WP-0015-bwrap-runtime-and-private-state.md": "sha256:10ff0a59b155bccd15bc2432619142062f6d04052deabb7cd8a796d908c894ca",
|
||||
"driver-proposals/sand-boxer-current/workplans/SAND-WP-0015-bwrap-runtime-and-private-state.md": "sha256:56022703fd4d973f40ddad8eb99cb02783b086549b24ef89f89157de33eafe31",
|
||||
"glas-harness-driver-reference.patch": "sha256:fb4518d2c1ed022230bbb2955970c20fcd288141dcd88718526defaca0895b2a",
|
||||
"glas-harness-driver-reference.preimage.sha256": "sha256:de5afac619d1863d9340961c321effbda3677cdfcb765e24d4e5c4bfe673095c",
|
||||
"index-source/AGENTS.md": "sha256:5d01af0ea255eab568ae72f326378219d1be4c77a9bc001ae12f4a1be2660034",
|
||||
"index-source/workplans/REINAH-WP-0008-simpler-metered-retry.md": "sha256:9aac375bdb2e93c57cd513ab8cd5bf0c0979a0699ea0181e6d405899d906bf68",
|
||||
"index.preimage": "sha256:08c186d73338c3eb03a8216c7025fdb6cb386a647802f4df7f096c2f177ff98c",
|
||||
"postimages/AGENTS.md": "sha256:da71c6e3d76d7c867b2db6e67cd919fba594acc3804d3784dd744438103560c2",
|
||||
"postimages/workplans/REINAH-WP-0008-simpler-metered-retry.md": "sha256:1118924bb159f8a52a37ace8b8663c75274b89928f7a8b957638428af224b054",
|
||||
"preimages/AGENTS.md": "sha256:5d01af0ea255eab568ae72f326378219d1be4c77a9bc001ae12f4a1be2660034",
|
||||
"preimages/workplans/REINAH-WP-0008-simpler-metered-retry.md": "sha256:9aac375bdb2e93c57cd513ab8cd5bf0c0979a0699ea0181e6d405899d906bf68",
|
||||
"preparation.json": "sha256:758faed4c436d70106bd9d109a440595bea4e744d3066e1bab1d2080edcd165b",
|
||||
"rein-aharness-source-correction.patch": "sha256:16c8223d22621aaccf3f430b88d1b3ec0178b60a4d4f210a34948d677f4f2441",
|
||||
"sand-boxer-current-driver-reference.patch": "sha256:0c4d631655b7657cadcf8be51861a4d95edb84ccc8a0c8b8cc39f7e66549da5f",
|
||||
"sand-boxer-driver-reference.patch": "sha256:63f234472f1728cdb10bdd24e27f90e05b5f3f17c91bc87eb9f973bcfd2ff8ff",
|
||||
"sand-boxer-driver-reference.preimage.sha256": "sha256:5bbe7e322db7c9279240de942ebb9ebc5eca11e6945540e8d0bb59de3e0e33b8",
|
||||
"verification.json": "sha256:a32b481c98e10fe10678c49b71b06fa62b2b4b8c4dc040c4d2f5a6732463ce09"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,264 @@
|
|||
# rein-aharness — Agent Instructions
|
||||
|
||||
## Repo Identity
|
||||
|
||||
**Purpose:** Claude-Code-CLI-driven rein: governed, unattended/scheduled agent runtime
|
||||
|
||||
**Domain:** infotech
|
||||
**Repo slug:** rein-aharness
|
||||
**Topic ID:** `cee7bedf-2b48-46ef-8601-006474f2ad7a`
|
||||
**Workplan prefix:** `REINAH-WP-`
|
||||
|
||||
`REIN-A-0001` through `REIN-A-0004` are immutable historical identifiers from
|
||||
before the fleet `PREFIX-WP-NNNN` convention. `HARNESS-WP-0001` through
|
||||
`HARNESS-WP-0003`, `HARNESS-WP-0008` and
|
||||
`HARNESS-WP-ADHOC-2026-09-04` are preserved historical ids. Workplan 0008
|
||||
keeps its originally issued HARNESS identity after the 2026-10-17 correction
|
||||
of an erroneous REINAH relabel; its existing REINAH filename is only a path. Preserve all of these in references and archives, but use
|
||||
the hub-registered `REINAH-WP-` prefix for every new workplan and task.
|
||||
|
||||
---
|
||||
|
||||
## Developer Workflow
|
||||
|
||||
```bash
|
||||
# Install (editable, including the governed profile-execution siblings)
|
||||
python3 -m venv .venv && source .venv/bin/activate
|
||||
pip install -e . -e ../llm-connect -e ../sand-boxer -e ../glas-harness pytest
|
||||
|
||||
# Test
|
||||
PYTHONPATH=".:$HOME/llm-connect" python3 -m pytest tests/ -q
|
||||
|
||||
# Run — bounded agentic session against a real target repo (real Claude Code
|
||||
# CLI call, real commit if successful)
|
||||
rein-aharness run --task-file examples/task-hello-sandbox.json --no-hub --no-metrics
|
||||
|
||||
# Build the Railiance deploy image
|
||||
make image
|
||||
```
|
||||
|
||||
## State Hub Integration
|
||||
|
||||
The Custodian State Hub tracks work across all domains. Interact via HTTP REST —
|
||||
there is no MCP server for Codex agents.
|
||||
|
||||
| Context | URL |
|
||||
|---------|-----|
|
||||
| Local workstation | `http://127.0.0.1:8000` |
|
||||
| Remote (railiance01, in-cluster) | `http://10.43.68.154:8000` |
|
||||
| Optional local edge relay | http://127.0.0.1:18080 |
|
||||
|
||||
When an operator has enabled the edge relay, set API_BASE to the relay URL.
|
||||
Queueable writes return an explicit queued receipt if the central hub is
|
||||
unreachable. Treat that as pending local evidence, then ask the operator to run
|
||||
statehub outbox status/replay after connectivity returns.
|
||||
|
||||
### Orient at session start
|
||||
|
||||
```bash
|
||||
# Offline brief — works without hub connection
|
||||
cat .custodian-brief.md
|
||||
|
||||
# Active workplans for this domain
|
||||
curl -s "http://127.0.0.1:8000/workplans/?topic_id=cee7bedf-2b48-46ef-8601-006474f2ad7a&status=active" \
|
||||
| python3 -m json.tool
|
||||
|
||||
# Check inbox
|
||||
curl -s "http://127.0.0.1:8000/messages/?to_agent=rein-aharness&unread_only=true" \
|
||||
| python3 -m json.tool
|
||||
```
|
||||
|
||||
Mark a message read:
|
||||
```bash
|
||||
curl -s -X PATCH "http://127.0.0.1:8000/messages/<id>/read" \
|
||||
-H "Content-Type: application/json" -d '{}'
|
||||
```
|
||||
|
||||
### Log progress (required at session close)
|
||||
|
||||
```bash
|
||||
curl -s -X POST http://127.0.0.1:8000/progress/ \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"summary": "what was done",
|
||||
"event_type": "note",
|
||||
"author": "codex",
|
||||
"workplan_id": "<uuid>",
|
||||
"task_id": "<uuid>"
|
||||
}'
|
||||
```
|
||||
|
||||
Omit `workplan_id` / `task_id` when not applicable.
|
||||
|
||||
### Update task status
|
||||
|
||||
```bash
|
||||
curl -s -X PATCH "http://127.0.0.1:8000/tasks/<task_id>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"status": "progress"}'
|
||||
# values: wait | todo | progress | done | cancel
|
||||
```
|
||||
|
||||
### Flag a task for human review
|
||||
|
||||
```bash
|
||||
curl -s -X PATCH "http://127.0.0.1:8000/tasks/<task_id>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"needs_human": true, "intervention_note": "reason"}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Session Protocol
|
||||
|
||||
**Start:**
|
||||
1. `cat .custodian-brief.md` — domain goal and open workplans (offline-safe)
|
||||
2. Check inbox: `GET /messages/?to_agent=rein-aharness&unread_only=true`; mark read
|
||||
3. Scan workplans: `ls workplans/` — note `status: ready`, `active`, or `blocked` files and open tasks
|
||||
4. Check human-needed tasks: `GET /tasks/?needs_human=true`
|
||||
|
||||
**During work:**
|
||||
- Update task statuses in workplan files as tasks progress
|
||||
- Record significant decisions via `POST /decisions/`
|
||||
|
||||
**Close:**
|
||||
1. Update workplan file task statuses to reflect progress
|
||||
2. If finishing a workplan: hand off **residuals** as live work records first
|
||||
(intake with `origin: residual` + `origin_ref: <WP-id>`, or a next workplan /
|
||||
decision / engagement). Do not park leftovers only in prose or `SCOPE.md`.
|
||||
Canon: `the-custodian/canon/standards/work-record-types_v0.1.md` § Residuals.
|
||||
3. Log: `POST /progress/` with a summary of what changed (name handoff ids)
|
||||
4. After workplan file changes, run:
|
||||
```bash
|
||||
statehub fix-consistency
|
||||
```
|
||||
Coding agents should run this directly; ask the operator only if the CLI or
|
||||
State Hub API is unavailable. This syncs task status from files into the hub DB.
|
||||
|
||||
---
|
||||
|
||||
## Credential and access routing
|
||||
|
||||
**Audience:** Codex, Claude Code, Grok, and custodian agents that call **llm-connect**
|
||||
for inference. Run this check **before** requesting secrets, API keys, SSH access,
|
||||
login tokens, or database passwords — in any repo, not only `ops-warden`.
|
||||
|
||||
ops-warden **issues SSH certificates only** (`warden sign`, `cert_command`). Every
|
||||
other credential need belongs to another subsystem. **Do not** message
|
||||
`ops-warden` on State Hub expecting a secret value; the reply is a pointer, not a key.
|
||||
|
||||
### Lookup (do this first)
|
||||
|
||||
```bash
|
||||
warden route find "<describe your need>" --json
|
||||
warden route show <catalog-id> --json
|
||||
```
|
||||
|
||||
Requires the `warden` CLI from `~/ops-warden` (`uv tool install .` or `uv run warden`).
|
||||
|
||||
| Agent runtime | How to orient |
|
||||
| --- | --- |
|
||||
| **Codex / Grok** (shell, HTTP State Hub) | `warden route` commands above; inbox `to_agent=rein-aharness` is for coordination, not secret vending |
|
||||
| **Claude Code** (MCP when available) | `get_domain_summary("custodian")` for workplans; **still** use `warden route` for credential ownership |
|
||||
| **llm-connect** (inference service) | Never put secret retrieval in prompts; route custody to OpenBao/operator paths surfaced by `warden route` |
|
||||
|
||||
### Quick routing table
|
||||
|
||||
| I need… | Owner | ops-warden executes? |
|
||||
| --- | --- | --- |
|
||||
| SSH cert (`adm`/`agt`/`atm`) | ops-warden | **Yes** — `warden sign` |
|
||||
| API key, DB password, provider token | OpenBao (`railiance-platform`) | No — route only |
|
||||
| Login / OIDC / MFA | key-cape / Keycloak | No — route only |
|
||||
| Authorization decision | flex-auth | No — route only |
|
||||
| activity-core → issue-core emission | activity-core + issue-core | No — `warden route show activity-core-issue-sink` |
|
||||
| SSH tunnel | ops-bridge (+ `cert_command` from warden) | No — route only |
|
||||
|
||||
### Anti-patterns (do not do these)
|
||||
|
||||
- `POST /messages/` to `ops-warden` asking for `ISSUE_CORE_API_KEY`, `OPENROUTER_API_KEY`, etc.
|
||||
- Inventing `warden secret`, `warden login`, `warden bao`, `warden tunnel` — they do not exist
|
||||
- Pasting secrets into Git, State Hub, workplans, logs, or chat
|
||||
|
||||
### Other capabilities (reuse-surface)
|
||||
|
||||
Non-credential capabilities are usually discovered through **reuse-surface** federation
|
||||
(`reuse-surface` registry / `capability.*` indexes). Credential routing is inlined in
|
||||
every repo's agent instructions because it is high-frequency, high-risk, and easy to
|
||||
get wrong.
|
||||
|
||||
**Canon:** `~/ops-warden/wiki/CredentialRouting.md` · catalog `~/ops-warden/registry/routing/catalog.yaml`
|
||||
|
||||
<!-- REPO-AGENTS-EXTENSIONS -->
|
||||
<!-- Append repo-specific agent instructions below this marker.
|
||||
The state-hub template sync preserves content after this line. -->
|
||||
|
||||
---
|
||||
|
||||
## Workplan Convention (ADR-001)
|
||||
|
||||
Work items originate as files in this repo — not in the hub. The hub is a
|
||||
read/cache/index layer that rebuilds from files.
|
||||
|
||||
**File location:** `workplans/REINAH-WP-NNNN-<slug>.md`
|
||||
|
||||
**Archived location:** finished workplans may move to
|
||||
`workplans/archived/YYMMDD-REINAH-WP-NNNN-<slug>.md`. The `YYMMDD` prefix is
|
||||
the completion/archive date; the frontmatter `id` does not change.
|
||||
|
||||
**Ad Hoc Tasks:** small opportunistic fixes discovered during a session use
|
||||
`workplans/ADHOC-YYYY-MM-DD.md` with task ids `ADHOC-YYYY-MM-DD-T01`, etc. Use
|
||||
this only for low-risk work completed directly; create a normal workplan for
|
||||
anything needing analysis, design, approval, dependencies, or multiple phases.
|
||||
|
||||
**Frontmatter:**
|
||||
|
||||
```yaml
|
||||
---
|
||||
id: REINAH-WP-NNNN
|
||||
type: workplan
|
||||
title: "..."
|
||||
domain: infotech
|
||||
repo: rein-aharness
|
||||
status: proposed | ready | active | blocked | backlog | finished | archived
|
||||
owner: codex
|
||||
topic_slug: ...
|
||||
created: "YYYY-MM-DD"
|
||||
updated: "YYYY-MM-DD"
|
||||
state_hub_workstream_id: "<uuid>" # fix-consistency — do not edit (legacy field name; workplan UUID)
|
||||
---
|
||||
```
|
||||
|
||||
Use `proposed` for a new draft, `ready` after review against current repo
|
||||
state, and `finished` after implementation. `stalled` and `needs_review` are
|
||||
derived health labels, not frontmatter statuses.
|
||||
|
||||
**Terminology:** workplan is the fleet term; `workstream` appears only in legacy
|
||||
API/MCP/frontmatter bridges until `STATE-WP-0069` retires them — see
|
||||
`the-custodian/canon/standards/workplan-terminology-fleet_v0.1.md`.
|
||||
|
||||
**Task block format** (one per `##` section):
|
||||
|
||||
```
|
||||
## Task Title
|
||||
|
||||
` ` `task
|
||||
id: REINAH-WP-NNNN-T01
|
||||
status: wait | todo | progress | done | cancel
|
||||
priority: high | medium | low
|
||||
state_hub_task_id: "<uuid>" # written by fix-consistency — do not edit
|
||||
` ` `
|
||||
|
||||
Task description text.
|
||||
```
|
||||
|
||||
Status progression: `todo` → `progress` → `done`; use `wait` for waiting/blocked work and `cancel` for stopped work.
|
||||
|
||||
**Residuals when finishing:** actionable leftovers become live work records
|
||||
before `status: finished` — usually an intake (`origin: residual`,
|
||||
`origin_ref: REINAH-WP-NNNN`) or a spawned workplan. Residual is a *role*,
|
||||
not a kind. Fleet list lives on State Hub, not in `SCOPE.md`.
|
||||
|
||||
To create a new workplan:
|
||||
1. Write the file following the format above
|
||||
2. Run `statehub fix-consistency` locally; ask the operator only if the CLI or
|
||||
State Hub API is unavailable.
|
||||
|
|
@ -0,0 +1,365 @@
|
|||
---
|
||||
id: HARNESS-WP-0008
|
||||
type: workplan
|
||||
title: "Simpler metered retry: free rehearsal, zero-cost hold release, one-command renewal"
|
||||
domain: infotech
|
||||
repo: rein-aharness
|
||||
status: blocked
|
||||
owner: claude-code
|
||||
topic_slug: rein-aharness
|
||||
priority: high
|
||||
created: "2026-10-13"
|
||||
updated: "2026-10-17"
|
||||
related:
|
||||
- REINAH-WP-0003
|
||||
state_hub_workstream_id: "187c2599-84a6-5764-a14f-7e9121975bf2"
|
||||
---
|
||||
|
||||
# HARNESS-WP-0008 — Simpler metered retry
|
||||
|
||||
## Origin
|
||||
|
||||
Identity correction, 2026-10-17: restore the originally issued
|
||||
`HARNESS-WP-0008` and T01–T07 canonical IDs, retaining every existing Hub UUID,
|
||||
status, decision and human/native gate. Commit `a85de661` briefly relabelled
|
||||
this workplan `REINAH-WP-0008` while preserving HARNESS-derived UUIDs in response
|
||||
to a C-26 warning. Canon grandfathering preserves existing IDs; C-26 applies
|
||||
to new plans and supplies no record-rekey authority. The REINAH filename remains
|
||||
as an existing backing path, and the erroneous label remains historical
|
||||
provenance rather than another canonical record. Future new workplans still
|
||||
use REINAH-WP. No UUID migration, retirement, human disposition, installation,
|
||||
spend authority or native acceptance is granted by this source correction.
|
||||
|
||||
On 2026-10-13 the owner reconciled the 2026-09-27 hold for run `6efa9436`
|
||||
(`docs/evidence/2026-10-13-held-liability-reconciliation.json`). A corrected
|
||||
runtime `ee3d0a53…` was built
|
||||
(`docs/evidence/2026-10-13-corrected-owner-runtime-build.json`). The owner
|
||||
authorized a new envelope and one retry.
|
||||
|
||||
Preparing that retry took too many owner ceremonies:
|
||||
|
||||
1. The runtime is built on the workstation and copied to railiance01 by hand.
|
||||
2. `owner.json` and the spend policy hold five digests that the owner edits
|
||||
by hand.
|
||||
3. A failed attempt keeps its full hold even when no provider request was
|
||||
made, so every failure needs a new envelope, ledger and pins.
|
||||
4. Every attempt needs six native approvals.
|
||||
5. The launcher bug that failed run `6efa9436` was found only by spending
|
||||
money. A free check on the host would have caught it.
|
||||
|
||||
The owner agreed (2026-10-13) to fix items 5, 3 and 2 here, and to record
|
||||
the rest as decisions instead of blocking on them.
|
||||
|
||||
**Goal:** a retry becomes: install the artifact, rehearse for free, approve
|
||||
once, run.
|
||||
|
||||
## A. Free rehearsal on the host
|
||||
|
||||
```task
|
||||
id: HARNESS-WP-0008-T01
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "c6536c8d-7862-5de6-ad2b-401077b60c04"
|
||||
```
|
||||
|
||||
Add a free rehearsal to the owner check. It runs everything
|
||||
`metered-once --check` does, then executes the admitted runtime's
|
||||
`rein-aharness` and `glas-harness` launchers inside bwrap from the mount
|
||||
path, with no network, credentials, provider route or ledger write. The
|
||||
sandbox profile is already resolved by `--check`. It prints one receipt with
|
||||
the runtime digest and each launcher's exit code.
|
||||
|
||||
Today's launcher check reads only the first line of each launcher. This
|
||||
rehearsal runs them, the same way the failing run did.
|
||||
|
||||
**Done when:** tests prove the rehearsal fails on a runtime whose launchers
|
||||
point at a missing build directory (the `6efa9436` failure) and passes on a
|
||||
relocated runtime. It must make no network calls and write nothing. The
|
||||
owner procedure in `docs/owner-bootstrap.md` makes the rehearsal a required
|
||||
step before any paid attempt.
|
||||
|
||||
### Implemented — 2026-10-13
|
||||
|
||||
`rein-aharness metered-once --owner-config <owner.json> --rehearse` (the flag
|
||||
implies `--check`, so it never reads a provider key or claims). The code is in
|
||||
`rein_aharness/owner_rehearsal.py`. It uses sand-boxer's isolation flags and
|
||||
read-only system binds, with the runtime read-only at `/opt/sandboxer/runtime`,
|
||||
`--unshare-net` and `--clearenv`. It runs each Python launcher with `--help`,
|
||||
plus `claude --version` when `build-info.json` declares Claude. A missing
|
||||
bwrap fails closed.
|
||||
|
||||
Tests (`tests/test_owner_rehearsal.py`, `tests/test_owner_bootstrap.py`):
|
||||
|
||||
- With real bwrap, a relocated runtime passes. The uv trampoline that
|
||||
re-executes a deleted build interpreter fails with 127, as run `6efa9436`
|
||||
did.
|
||||
- The rehearsal never claims and never reads the key.
|
||||
- The command line has no writable bind and no credential.
|
||||
|
||||
The corrected runtime `ee3d0a53…` rehearses clean: rein-aharness 0,
|
||||
glas-harness 0, claude 0.
|
||||
|
||||
## B. Release holds that provably cost nothing
|
||||
|
||||
```task
|
||||
id: HARNESS-WP-0008-T02
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "6a413f5a-4ac2-5d5e-9b19-71b33445c0a9"
|
||||
```
|
||||
|
||||
`SpendLedger.reconcile` keeps the full hold (`max(held, observed)`). Add an
|
||||
evidence-backed release. If the run has zero provider-request reservations
|
||||
and its request route is revoked, reconciliation may set the liability to
|
||||
the observed amount. Otherwise the current rule stands. The release records
|
||||
which evidence allowed it, and the CLI flag says so, for example
|
||||
`--release-unused-hold`.
|
||||
|
||||
This changes a deliberate safety rule, so it needs owner sign-off on the
|
||||
exact rule before merge. That sign-off is recorded in T04's decision note.
|
||||
|
||||
**Done when:** tests cover all four cases. A run with no request
|
||||
reservations and a revoked route releases. A run with any request
|
||||
reservation, or with a live route, refuses the release. A second, identical
|
||||
reconcile changes nothing. The envelope is never marked breached by a
|
||||
release.
|
||||
|
||||
### Widened to observed-usage settlement — 2026-10-14
|
||||
|
||||
On 2026-10-13 the owner asked for three attempts within EUR 10 and chose
|
||||
option B: settle to actual usage instead of releasing only unused holds.
|
||||
Under full-hold accounting each request holds USD 4.64 (1M context plus
|
||||
64k output), so EUR 10 cannot fit three attempts.
|
||||
|
||||
Implemented as an opt-in envelope term (`settlement: observed-usage`) plus
|
||||
`max_runs`; see `docs/owner-bootstrap.md`. Under it:
|
||||
|
||||
- a completed request is charged its gateway-metered cost;
|
||||
- runs and reconciliations are charged
|
||||
`max(metered requests, reported or attested cost)`;
|
||||
- unfinished requests keep their full hold, and overruns still freeze the
|
||||
envelope.
|
||||
|
||||
The run 6efa9436 shape (failure before any request) now reconciles to its
|
||||
attested cost. Defaults and existing digests are unchanged. Tests:
|
||||
`tests/test_spend_settlement.py`, plus renewal cases in
|
||||
`tests/test_owner_renew.py`.
|
||||
|
||||
Merge-ready, but the rule still needs the owner's acceptance in decision
|
||||
`a93619a9`.
|
||||
|
||||
## D. One owner command for renewal
|
||||
|
||||
```task
|
||||
id: HARNESS-WP-0008-T03
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "87457216-2371-51ca-b3d4-3917e6f180a2"
|
||||
```
|
||||
|
||||
Add `rein-aharness owner renew --owner <owner.json> --runtime <digest>
|
||||
--envelope <envelope.json>`. It does three things:
|
||||
|
||||
1. It refuses unless every reservation in the current ledger is settled.
|
||||
2. It writes the new spend policy, initializes a fresh ledger, and updates
|
||||
the runtime and spend-policy pins in `owner.json` together. Old files are
|
||||
kept as backups. Existing ledgers are never reset.
|
||||
3. It prints one summary for the owner to review: old to new runtime, old
|
||||
to new policy digest, envelope cap and attempt limit, and ledger path.
|
||||
|
||||
Add a dry-run mode that only prints the summary. Afterwards, run the T01
|
||||
rehearsal.
|
||||
|
||||
**Done when:** a test renews from the 2026-09-27 layout to a new envelope.
|
||||
The owner check passes on the result. A second renew with an unsettled
|
||||
reservation refuses. No digest has to be typed by hand.
|
||||
|
||||
### Implemented — 2026-10-13
|
||||
|
||||
The command is `rein-aharness owner renew --owner-config --policy --ledger
|
||||
--runtime-path --envelope [--apply]`, in `rein_aharness/owner_renew.py`.
|
||||
Without `--apply` it is a dry run. Choices:
|
||||
|
||||
- **Envelope file:** only the envelope fields may change. Worker, target,
|
||||
profile and grant pins carry over, and `SpendPolicy` validation runs on
|
||||
the result.
|
||||
- **Paths stay the same:** the old ledger is renamed to
|
||||
`.retired-<old envelope>`, never reset, and the old policy and owner.json
|
||||
are kept as `.bak`. Secrets Engine pins the `exec_owner` files by sha256
|
||||
and the ledger path through its fixed environment, so keeping the paths
|
||||
means the catalog needs only new file hashes. The command prints them.
|
||||
- **Refusals:** a held reservation, an open request route, an open SQLite
|
||||
transaction, an owner.json that doesn't match the policy, a reused
|
||||
envelope id, a disallowed field, or leftovers from an earlier renewal.
|
||||
|
||||
Tests (`tests/test_owner_renew.py`):
|
||||
|
||||
- A dry run changes nothing.
|
||||
- An applied renewal passes the real owner check (`prepare`) at the
|
||||
unchanged paths.
|
||||
- A charged reservation, the railiance01 state after the 2026-10-13
|
||||
reconciliation, renews, and the row survives in the retired ledger.
|
||||
- Bad envelopes and a second apply both refuse.
|
||||
|
||||
Finding for T06: renewal still changes the pinned file hashes, so each
|
||||
renewal needs a Secrets Engine catalog update and fresh approvals.
|
||||
|
||||
## C. Envelopes that cover several attempts (decision)
|
||||
|
||||
```task
|
||||
id: HARNESS-WP-0008-T04
|
||||
status: done
|
||||
decision_id: "a93619a9-8a70-40d8-aaf9-c29357d2a043"
|
||||
priority: medium
|
||||
state_hub_task_id: "898e02af-51bb-5e8d-93d0-ae908466c612"
|
||||
```
|
||||
|
||||
Owner decision: should future envelopes be admitted as "up to N attempts
|
||||
within EUR X" instead of one attempt? The EUR 10 cap already fits two
|
||||
USD 4.64 request holds. Record the choice, together with T02's sign-off,
|
||||
as a decision note in `docs/`. T03's envelope format then reflects it.
|
||||
|
||||
### Decided — 2026-10-14
|
||||
|
||||
The owner chose up to three attempts within EUR 10 (EUR 5 per attempt) and
|
||||
observed-usage settlement, and resolved hub decision `a93619a9` with that
|
||||
choice. Record: `docs/decisions/2026-10-14-spend-envelope-settlement.md`.
|
||||
The terms are in spend memo `infd-20261014-b01`.
|
||||
|
||||
## E. Ship the owner runtime as a container image (decision)
|
||||
|
||||
```task
|
||||
id: HARNESS-WP-0008-T05
|
||||
status: wait
|
||||
needs_human: true
|
||||
decision_id: "e6ca2f07-2708-4013-bfba-bf89fa0f1cb5"
|
||||
priority: low
|
||||
state_hub_task_id: "b486e86b-9d0f-501d-a394-771b40f771ee"
|
||||
```
|
||||
|
||||
Decision for later, with Platform (railiance) and Sand-boxer: should the
|
||||
owner runtime ship through the registry and GitOps, using the existing
|
||||
`make image` path, instead of rsync and a manual digest check? Out of scope
|
||||
here. If accepted, hand it off as a residual intake to the owning repos.
|
||||
|
||||
## F. Approvals per envelope (decision)
|
||||
|
||||
```task
|
||||
id: HARNESS-WP-0008-T06
|
||||
status: wait
|
||||
needs_human: true
|
||||
decision_id: "e8986e40-f6f8-4c87-b704-bff50cc4958b"
|
||||
priority: low
|
||||
state_hub_task_id: "4cf721e6-724b-5c64-91bb-67b098bb97af"
|
||||
```
|
||||
|
||||
Decision for later, with Secrets Engine: should one approval set cover an
|
||||
envelope's attempts instead of six native approvals per attempt? The work
|
||||
lives outside this repo. If accepted, hand it off as a residual intake.
|
||||
|
||||
## Retry on the corrected runtime
|
||||
|
||||
```task
|
||||
id: HARNESS-WP-0008-T07
|
||||
status: wait
|
||||
needs_human: true
|
||||
blocking_reason: "Owner: accept spend memo infd-20261014-b01, then attended install, rehearsal, renew and approvals on railiance01"
|
||||
priority: high
|
||||
state_hub_task_id: "80318842-ccaa-5484-a54a-3d9981eaec8b"
|
||||
```
|
||||
|
||||
Waits on T01 and T03. It does not need T02.
|
||||
|
||||
Steps:
|
||||
|
||||
1. Rebuild `ee3d0a53…`, or the then-current corrected runtime, from the
|
||||
pinned commits.
|
||||
2. Install it on railiance01.
|
||||
3. Run the T01 rehearsal.
|
||||
4. Renew with T03 to the owner-authorized new envelope (EUR 10, authorized
|
||||
2026-10-13).
|
||||
5. Run exactly one attended attempt, which consumes fresh native approvals.
|
||||
|
||||
Record the result in REINAH-WP-0003-T05/T06.
|
||||
|
||||
### Waiting on the owner — 2026-10-14
|
||||
|
||||
Everything this repo can do is done: the rehearsal (T01), the settlement rule
|
||||
(T02, T04) and renewal (T03) are in place. Draft memo `infd-20261014-b01` is
|
||||
in informed-decision `ee6a65b`. What remains needs the owner, in order:
|
||||
|
||||
1. Accept the memo in the attended sitting.
|
||||
2. ~~Rebuild `ee3d0a53` from the pinned commits~~ (done 2026-10-15 as
|
||||
`818b1b7c…`, see below) and install it on railiance01.
|
||||
3. Run `metered-once --rehearse`.
|
||||
4. Run `owner renew` with the memo's terms, then update the Secrets Engine
|
||||
file pins.
|
||||
5. Approve the native actions and run the first attempt.
|
||||
|
||||
Sand-boxer's steps 2–4 in message `82faf25c` are this same sequence.
|
||||
|
||||
### Runtime rebuilt — 2026-10-15
|
||||
|
||||
Step 1 is done. The owner approved a rebuild because `ee3d0a53…` was not kept
|
||||
on the workstation. Sand-boxer built `818b1b7c…` from the same pinned inputs
|
||||
and builder. The digest differs only because the build-source paths are
|
||||
embedded in the artifact. The bwrap startup smoke passed (sandbox `28df7a1d`)
|
||||
with no credentials and no model run. `818b1b7c…` is now the install
|
||||
candidate. It is not installed on railiance01 (still `b6e4e8a4…`) or admitted.
|
||||
Receipt: `docs/evidence/2026-10-15-rebuilt-owner-runtime.json`. The memo still
|
||||
still has to be created, admitted, loaded and accepted first. The owner steps
|
||||
are in informed-decision `docs/batches/2026-10-14-three-attempts/OPERATOR.md`.
|
||||
|
||||
### Memo queued — 2026-10-15
|
||||
|
||||
Owner step 1 is in progress via the lifecycle intake path (owner's choice; the
|
||||
review-record approval counts as spend authority). Workspace draft
|
||||
`2ccb4bda-e318-51d2-a8dd-709db768494f`, review
|
||||
`review-e2d2e98f-4701-45b0-abc1-4fe9f00bd1a8`, queued by the owner in the State
|
||||
Hub memo dispatcher; approval intent `accept` by `helixforge-factory`. Record:
|
||||
informed-decision `d5f1021` (sitting.json, OPERATOR.md). The accepted approval id
|
||||
becomes `authority_ref` for `owner renew`. Install candidate remains `818b1b7c`.
|
||||
|
||||
|
||||
### Workload-only Glas source integration — 2026-10-16
|
||||
|
||||
Reviewed and integrated the one-line runtime-contract lock advance from Glas
|
||||
`30be81808140a3a3d137e731d1fe9aed59ad8581` to published workload-only credential
|
||||
delivery `7357115c8311e55ee3d495f5266f501a17ec3c89`. llm-connect `37436bb5` and
|
||||
sandboxer `221126cb` remain pinned. This is source integration under T07, not
|
||||
native artifact installation or admission. T07 remains waiting with its existing
|
||||
owner gates.
|
||||
|
||||
Prior preparation used rein base `be5ef400` plus isolated lock-only commit
|
||||
`c811fc43` to build candidate
|
||||
`634e65fee5517ebee0d1080805fd84c81bd46af26a2b38ec1ab14bd2be17bf8f`. The
|
||||
candidate's installed packages passed actual SandboxManager/Glas gateway proof
|
||||
with unchanged `harness.agent-dev-local@1.1.1` (still unverified): real rein/Glas
|
||||
CLI and Claude 2.1.266 startup, read-only runtime, synthetic host-file and
|
||||
inherited environment/child-process denial, exact task identity/private state,
|
||||
deterministic workspace Git effect, task removal and destruction. A separately
|
||||
labelled synthetic route fixture passed workload-only delivery, output redaction
|
||||
and clean subsequent housekeeping through the same actual manager. Wrong
|
||||
actor/project/run, expired execution, expiry cleanup, cancellation and
|
||||
post-cancellation refusal passed. All 141 Glas tests and seven rein lock/rehearsal
|
||||
tests passed; whole artifact digest remained unchanged after proof.
|
||||
|
||||
The first proof runner correctly encountered the manager's PermissionError for
|
||||
wrong consumer but expected ValueError/RuntimeError. Its finally cleanup ran;
|
||||
the runner catch was refined and the failure retained. This does not earn an
|
||||
unchanged execution success. No model/provider request, real credential read,
|
||||
queue access or spend occurred.
|
||||
|
||||
The preparation artifact is not the artifact of this integrated owner revision.
|
||||
Rebuild from the exact committed owner source and verify its new digest and
|
||||
actual-manager proof before considering installation. Source inputs, receipts,
|
||||
reproducible proof and attempt history are retained in the
|
||||
[Custodian evidence directory](../../the-custodian/docs/changes/netkingdom-target-2026-10-16/agent-isolation-next/README.md).
|
||||
The same directory records the integrated-source rebuild separately.
|
||||
|
||||
Retain `818b1b7c`, `ee3d0a53`, installed `b6e4e8a4` and their outcomes as historical
|
||||
evidence. Native acceptance still requires the queued spend memo lifecycle,
|
||||
owner artifact installation/rehearsal, renewal and Secrets Engine file-pin
|
||||
updates, required native approvals and bounded real-model/tool proof. Actual
|
||||
interactive assignment migration and external tool-path denial remain Custodian
|
||||
acceptance obligations; no profile readiness or supervision authority changed.
|
||||
|
|
@ -0,0 +1,262 @@
|
|||
# rein-aharness — Agent Instructions
|
||||
|
||||
## Repo Identity
|
||||
|
||||
**Purpose:** Claude-Code-CLI-driven rein: governed, unattended/scheduled agent runtime
|
||||
|
||||
**Domain:** infotech
|
||||
**Repo slug:** rein-aharness
|
||||
**Topic ID:** `cee7bedf-2b48-46ef-8601-006474f2ad7a`
|
||||
**Workplan prefix:** `REINAH-WP-`
|
||||
|
||||
`REIN-A-0001` through `REIN-A-0004` are immutable historical identifiers from
|
||||
before the fleet `PREFIX-WP-NNNN` convention. `HARNESS-WP-0001` through
|
||||
`HARNESS-WP-0003` and `HARNESS-WP-ADHOC-2026-09-04` are older ids from the
|
||||
agent-harness name. Preserve all of these in references and archives, but use
|
||||
the hub-registered `REINAH-WP-` prefix for every new workplan and task.
|
||||
|
||||
---
|
||||
|
||||
## Developer Workflow
|
||||
|
||||
```bash
|
||||
# Install (editable, including the governed profile-execution siblings)
|
||||
python3 -m venv .venv && source .venv/bin/activate
|
||||
pip install -e . -e ../llm-connect -e ../sand-boxer -e ../glas-harness pytest
|
||||
|
||||
# Test
|
||||
PYTHONPATH=".:$HOME/llm-connect" python3 -m pytest tests/ -q
|
||||
|
||||
# Run — bounded agentic session against a real target repo (real Claude Code
|
||||
# CLI call, real commit if successful)
|
||||
rein-aharness run --task-file examples/task-hello-sandbox.json --no-hub --no-metrics
|
||||
|
||||
# Build the Railiance deploy image
|
||||
make image
|
||||
```
|
||||
|
||||
## State Hub Integration
|
||||
|
||||
The Custodian State Hub tracks work across all domains. Interact via HTTP REST —
|
||||
there is no MCP server for Codex agents.
|
||||
|
||||
| Context | URL |
|
||||
|---------|-----|
|
||||
| Local workstation | `http://127.0.0.1:8000` |
|
||||
| Remote (railiance01, in-cluster) | `http://10.43.68.154:8000` |
|
||||
| Optional local edge relay | http://127.0.0.1:18080 |
|
||||
|
||||
When an operator has enabled the edge relay, set API_BASE to the relay URL.
|
||||
Queueable writes return an explicit queued receipt if the central hub is
|
||||
unreachable. Treat that as pending local evidence, then ask the operator to run
|
||||
statehub outbox status/replay after connectivity returns.
|
||||
|
||||
### Orient at session start
|
||||
|
||||
```bash
|
||||
# Offline brief — works without hub connection
|
||||
cat .custodian-brief.md
|
||||
|
||||
# Active workplans for this domain
|
||||
curl -s "http://127.0.0.1:8000/workplans/?topic_id=cee7bedf-2b48-46ef-8601-006474f2ad7a&status=active" \
|
||||
| python3 -m json.tool
|
||||
|
||||
# Check inbox
|
||||
curl -s "http://127.0.0.1:8000/messages/?to_agent=rein-aharness&unread_only=true" \
|
||||
| python3 -m json.tool
|
||||
```
|
||||
|
||||
Mark a message read:
|
||||
```bash
|
||||
curl -s -X PATCH "http://127.0.0.1:8000/messages/<id>/read" \
|
||||
-H "Content-Type: application/json" -d '{}'
|
||||
```
|
||||
|
||||
### Log progress (required at session close)
|
||||
|
||||
```bash
|
||||
curl -s -X POST http://127.0.0.1:8000/progress/ \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"summary": "what was done",
|
||||
"event_type": "note",
|
||||
"author": "codex",
|
||||
"workplan_id": "<uuid>",
|
||||
"task_id": "<uuid>"
|
||||
}'
|
||||
```
|
||||
|
||||
Omit `workplan_id` / `task_id` when not applicable.
|
||||
|
||||
### Update task status
|
||||
|
||||
```bash
|
||||
curl -s -X PATCH "http://127.0.0.1:8000/tasks/<task_id>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"status": "progress"}'
|
||||
# values: wait | todo | progress | done | cancel
|
||||
```
|
||||
|
||||
### Flag a task for human review
|
||||
|
||||
```bash
|
||||
curl -s -X PATCH "http://127.0.0.1:8000/tasks/<task_id>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"needs_human": true, "intervention_note": "reason"}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Session Protocol
|
||||
|
||||
**Start:**
|
||||
1. `cat .custodian-brief.md` — domain goal and open workplans (offline-safe)
|
||||
2. Check inbox: `GET /messages/?to_agent=rein-aharness&unread_only=true`; mark read
|
||||
3. Scan workplans: `ls workplans/` — note `status: ready`, `active`, or `blocked` files and open tasks
|
||||
4. Check human-needed tasks: `GET /tasks/?needs_human=true`
|
||||
|
||||
**During work:**
|
||||
- Update task statuses in workplan files as tasks progress
|
||||
- Record significant decisions via `POST /decisions/`
|
||||
|
||||
**Close:**
|
||||
1. Update workplan file task statuses to reflect progress
|
||||
2. If finishing a workplan: hand off **residuals** as live work records first
|
||||
(intake with `origin: residual` + `origin_ref: <WP-id>`, or a next workplan /
|
||||
decision / engagement). Do not park leftovers only in prose or `SCOPE.md`.
|
||||
Canon: `the-custodian/canon/standards/work-record-types_v0.1.md` § Residuals.
|
||||
3. Log: `POST /progress/` with a summary of what changed (name handoff ids)
|
||||
4. After workplan file changes, run:
|
||||
```bash
|
||||
statehub fix-consistency
|
||||
```
|
||||
Coding agents should run this directly; ask the operator only if the CLI or
|
||||
State Hub API is unavailable. This syncs task status from files into the hub DB.
|
||||
|
||||
---
|
||||
|
||||
## Credential and access routing
|
||||
|
||||
**Audience:** Codex, Claude Code, Grok, and custodian agents that call **llm-connect**
|
||||
for inference. Run this check **before** requesting secrets, API keys, SSH access,
|
||||
login tokens, or database passwords — in any repo, not only `ops-warden`.
|
||||
|
||||
ops-warden **issues SSH certificates only** (`warden sign`, `cert_command`). Every
|
||||
other credential need belongs to another subsystem. **Do not** message
|
||||
`ops-warden` on State Hub expecting a secret value; the reply is a pointer, not a key.
|
||||
|
||||
### Lookup (do this first)
|
||||
|
||||
```bash
|
||||
warden route find "<describe your need>" --json
|
||||
warden route show <catalog-id> --json
|
||||
```
|
||||
|
||||
Requires the `warden` CLI from `~/ops-warden` (`uv tool install .` or `uv run warden`).
|
||||
|
||||
| Agent runtime | How to orient |
|
||||
| --- | --- |
|
||||
| **Codex / Grok** (shell, HTTP State Hub) | `warden route` commands above; inbox `to_agent=rein-aharness` is for coordination, not secret vending |
|
||||
| **Claude Code** (MCP when available) | `get_domain_summary("custodian")` for workplans; **still** use `warden route` for credential ownership |
|
||||
| **llm-connect** (inference service) | Never put secret retrieval in prompts; route custody to OpenBao/operator paths surfaced by `warden route` |
|
||||
|
||||
### Quick routing table
|
||||
|
||||
| I need… | Owner | ops-warden executes? |
|
||||
| --- | --- | --- |
|
||||
| SSH cert (`adm`/`agt`/`atm`) | ops-warden | **Yes** — `warden sign` |
|
||||
| API key, DB password, provider token | OpenBao (`railiance-platform`) | No — route only |
|
||||
| Login / OIDC / MFA | key-cape / Keycloak | No — route only |
|
||||
| Authorization decision | flex-auth | No — route only |
|
||||
| activity-core → issue-core emission | activity-core + issue-core | No — `warden route show activity-core-issue-sink` |
|
||||
| SSH tunnel | ops-bridge (+ `cert_command` from warden) | No — route only |
|
||||
|
||||
### Anti-patterns (do not do these)
|
||||
|
||||
- `POST /messages/` to `ops-warden` asking for `ISSUE_CORE_API_KEY`, `OPENROUTER_API_KEY`, etc.
|
||||
- Inventing `warden secret`, `warden login`, `warden bao`, `warden tunnel` — they do not exist
|
||||
- Pasting secrets into Git, State Hub, workplans, logs, or chat
|
||||
|
||||
### Other capabilities (reuse-surface)
|
||||
|
||||
Non-credential capabilities are usually discovered through **reuse-surface** federation
|
||||
(`reuse-surface` registry / `capability.*` indexes). Credential routing is inlined in
|
||||
every repo's agent instructions because it is high-frequency, high-risk, and easy to
|
||||
get wrong.
|
||||
|
||||
**Canon:** `~/ops-warden/wiki/CredentialRouting.md` · catalog `~/ops-warden/registry/routing/catalog.yaml`
|
||||
|
||||
<!-- REPO-AGENTS-EXTENSIONS -->
|
||||
<!-- Append repo-specific agent instructions below this marker.
|
||||
The state-hub template sync preserves content after this line. -->
|
||||
|
||||
---
|
||||
|
||||
## Workplan Convention (ADR-001)
|
||||
|
||||
Work items originate as files in this repo — not in the hub. The hub is a
|
||||
read/cache/index layer that rebuilds from files.
|
||||
|
||||
**File location:** `workplans/REINAH-WP-NNNN-<slug>.md`
|
||||
|
||||
**Archived location:** finished workplans may move to
|
||||
`workplans/archived/YYMMDD-REINAH-WP-NNNN-<slug>.md`. The `YYMMDD` prefix is
|
||||
the completion/archive date; the frontmatter `id` does not change.
|
||||
|
||||
**Ad Hoc Tasks:** small opportunistic fixes discovered during a session use
|
||||
`workplans/ADHOC-YYYY-MM-DD.md` with task ids `ADHOC-YYYY-MM-DD-T01`, etc. Use
|
||||
this only for low-risk work completed directly; create a normal workplan for
|
||||
anything needing analysis, design, approval, dependencies, or multiple phases.
|
||||
|
||||
**Frontmatter:**
|
||||
|
||||
```yaml
|
||||
---
|
||||
id: REINAH-WP-NNNN
|
||||
type: workplan
|
||||
title: "..."
|
||||
domain: infotech
|
||||
repo: rein-aharness
|
||||
status: proposed | ready | active | blocked | backlog | finished | archived
|
||||
owner: codex
|
||||
topic_slug: ...
|
||||
created: "YYYY-MM-DD"
|
||||
updated: "YYYY-MM-DD"
|
||||
state_hub_workstream_id: "<uuid>" # fix-consistency — do not edit (legacy field name; workplan UUID)
|
||||
---
|
||||
```
|
||||
|
||||
Use `proposed` for a new draft, `ready` after review against current repo
|
||||
state, and `finished` after implementation. `stalled` and `needs_review` are
|
||||
derived health labels, not frontmatter statuses.
|
||||
|
||||
**Terminology:** workplan is the fleet term; `workstream` appears only in legacy
|
||||
API/MCP/frontmatter bridges until `STATE-WP-0069` retires them — see
|
||||
`the-custodian/canon/standards/workplan-terminology-fleet_v0.1.md`.
|
||||
|
||||
**Task block format** (one per `##` section):
|
||||
|
||||
```
|
||||
## Task Title
|
||||
|
||||
` ` `task
|
||||
id: REINAH-WP-NNNN-T01
|
||||
status: wait | todo | progress | done | cancel
|
||||
priority: high | medium | low
|
||||
state_hub_task_id: "<uuid>" # written by fix-consistency — do not edit
|
||||
` ` `
|
||||
|
||||
Task description text.
|
||||
```
|
||||
|
||||
Status progression: `todo` → `progress` → `done`; use `wait` for waiting/blocked work and `cancel` for stopped work.
|
||||
|
||||
**Residuals when finishing:** actionable leftovers become live work records
|
||||
before `status: finished` — usually an intake (`origin: residual`,
|
||||
`origin_ref: REINAH-WP-NNNN`) or a spawned workplan. Residual is a *role*,
|
||||
not a kind. Fleet list lives on State Hub, not in `SCOPE.md`.
|
||||
|
||||
To create a new workplan:
|
||||
1. Write the file following the format above
|
||||
2. Run `statehub fix-consistency` locally; ask the operator only if the CLI or
|
||||
State Hub API is unavailable.
|
||||
|
|
@ -0,0 +1,354 @@
|
|||
---
|
||||
id: REINAH-WP-0008
|
||||
type: workplan
|
||||
title: "Simpler metered retry: free rehearsal, zero-cost hold release, one-command renewal"
|
||||
domain: infotech
|
||||
repo: rein-aharness
|
||||
status: blocked
|
||||
owner: claude-code
|
||||
topic_slug: rein-aharness
|
||||
priority: high
|
||||
created: "2026-10-13"
|
||||
updated: "2026-10-16"
|
||||
related:
|
||||
- REINAH-WP-0003
|
||||
state_hub_workstream_id: "187c2599-84a6-5764-a14f-7e9121975bf2"
|
||||
---
|
||||
|
||||
# REINAH-WP-0008 — Simpler metered retry
|
||||
|
||||
## Origin
|
||||
|
||||
On 2026-10-13 the owner reconciled the 2026-09-27 hold for run `6efa9436`
|
||||
(`docs/evidence/2026-10-13-held-liability-reconciliation.json`). A corrected
|
||||
runtime `ee3d0a53…` was built
|
||||
(`docs/evidence/2026-10-13-corrected-owner-runtime-build.json`). The owner
|
||||
authorized a new envelope and one retry.
|
||||
|
||||
Preparing that retry took too many owner ceremonies:
|
||||
|
||||
1. The runtime is built on the workstation and copied to railiance01 by hand.
|
||||
2. `owner.json` and the spend policy hold five digests that the owner edits
|
||||
by hand.
|
||||
3. A failed attempt keeps its full hold even when no provider request was
|
||||
made, so every failure needs a new envelope, ledger and pins.
|
||||
4. Every attempt needs six native approvals.
|
||||
5. The launcher bug that failed run `6efa9436` was found only by spending
|
||||
money. A free check on the host would have caught it.
|
||||
|
||||
The owner agreed (2026-10-13) to fix items 5, 3 and 2 here, and to record
|
||||
the rest as decisions instead of blocking on them.
|
||||
|
||||
**Goal:** a retry becomes: install the artifact, rehearse for free, approve
|
||||
once, run.
|
||||
|
||||
## A. Free rehearsal on the host
|
||||
|
||||
```task
|
||||
id: REINAH-WP-0008-T01
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "c6536c8d-7862-5de6-ad2b-401077b60c04"
|
||||
```
|
||||
|
||||
Add a free rehearsal to the owner check. It runs everything
|
||||
`metered-once --check` does, then executes the admitted runtime's
|
||||
`rein-aharness` and `glas-harness` launchers inside bwrap from the mount
|
||||
path, with no network, credentials, provider route or ledger write. The
|
||||
sandbox profile is already resolved by `--check`. It prints one receipt with
|
||||
the runtime digest and each launcher's exit code.
|
||||
|
||||
Today's launcher check reads only the first line of each launcher. This
|
||||
rehearsal runs them, the same way the failing run did.
|
||||
|
||||
**Done when:** tests prove the rehearsal fails on a runtime whose launchers
|
||||
point at a missing build directory (the `6efa9436` failure) and passes on a
|
||||
relocated runtime. It must make no network calls and write nothing. The
|
||||
owner procedure in `docs/owner-bootstrap.md` makes the rehearsal a required
|
||||
step before any paid attempt.
|
||||
|
||||
### Implemented — 2026-10-13
|
||||
|
||||
`rein-aharness metered-once --owner-config <owner.json> --rehearse` (the flag
|
||||
implies `--check`, so it never reads a provider key or claims). The code is in
|
||||
`rein_aharness/owner_rehearsal.py`. It uses sand-boxer's isolation flags and
|
||||
read-only system binds, with the runtime read-only at `/opt/sandboxer/runtime`,
|
||||
`--unshare-net` and `--clearenv`. It runs each Python launcher with `--help`,
|
||||
plus `claude --version` when `build-info.json` declares Claude. A missing
|
||||
bwrap fails closed.
|
||||
|
||||
Tests (`tests/test_owner_rehearsal.py`, `tests/test_owner_bootstrap.py`):
|
||||
|
||||
- With real bwrap, a relocated runtime passes. The uv trampoline that
|
||||
re-executes a deleted build interpreter fails with 127, as run `6efa9436`
|
||||
did.
|
||||
- The rehearsal never claims and never reads the key.
|
||||
- The command line has no writable bind and no credential.
|
||||
|
||||
The corrected runtime `ee3d0a53…` rehearses clean: rein-aharness 0,
|
||||
glas-harness 0, claude 0.
|
||||
|
||||
## B. Release holds that provably cost nothing
|
||||
|
||||
```task
|
||||
id: REINAH-WP-0008-T02
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "6a413f5a-4ac2-5d5e-9b19-71b33445c0a9"
|
||||
```
|
||||
|
||||
`SpendLedger.reconcile` keeps the full hold (`max(held, observed)`). Add an
|
||||
evidence-backed release. If the run has zero provider-request reservations
|
||||
and its request route is revoked, reconciliation may set the liability to
|
||||
the observed amount. Otherwise the current rule stands. The release records
|
||||
which evidence allowed it, and the CLI flag says so, for example
|
||||
`--release-unused-hold`.
|
||||
|
||||
This changes a deliberate safety rule, so it needs owner sign-off on the
|
||||
exact rule before merge. That sign-off is recorded in T04's decision note.
|
||||
|
||||
**Done when:** tests cover all four cases. A run with no request
|
||||
reservations and a revoked route releases. A run with any request
|
||||
reservation, or with a live route, refuses the release. A second, identical
|
||||
reconcile changes nothing. The envelope is never marked breached by a
|
||||
release.
|
||||
|
||||
### Widened to observed-usage settlement — 2026-10-14
|
||||
|
||||
On 2026-10-13 the owner asked for three attempts within EUR 10 and chose
|
||||
option B: settle to actual usage instead of releasing only unused holds.
|
||||
Under full-hold accounting each request holds USD 4.64 (1M context plus
|
||||
64k output), so EUR 10 cannot fit three attempts.
|
||||
|
||||
Implemented as an opt-in envelope term (`settlement: observed-usage`) plus
|
||||
`max_runs`; see `docs/owner-bootstrap.md`. Under it:
|
||||
|
||||
- a completed request is charged its gateway-metered cost;
|
||||
- runs and reconciliations are charged
|
||||
`max(metered requests, reported or attested cost)`;
|
||||
- unfinished requests keep their full hold, and overruns still freeze the
|
||||
envelope.
|
||||
|
||||
The run 6efa9436 shape (failure before any request) now reconciles to its
|
||||
attested cost. Defaults and existing digests are unchanged. Tests:
|
||||
`tests/test_spend_settlement.py`, plus renewal cases in
|
||||
`tests/test_owner_renew.py`.
|
||||
|
||||
Merge-ready, but the rule still needs the owner's acceptance in decision
|
||||
`a93619a9`.
|
||||
|
||||
## D. One owner command for renewal
|
||||
|
||||
```task
|
||||
id: REINAH-WP-0008-T03
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "87457216-2371-51ca-b3d4-3917e6f180a2"
|
||||
```
|
||||
|
||||
Add `rein-aharness owner renew --owner <owner.json> --runtime <digest>
|
||||
--envelope <envelope.json>`. It does three things:
|
||||
|
||||
1. It refuses unless every reservation in the current ledger is settled.
|
||||
2. It writes the new spend policy, initializes a fresh ledger, and updates
|
||||
the runtime and spend-policy pins in `owner.json` together. Old files are
|
||||
kept as backups. Existing ledgers are never reset.
|
||||
3. It prints one summary for the owner to review: old to new runtime, old
|
||||
to new policy digest, envelope cap and attempt limit, and ledger path.
|
||||
|
||||
Add a dry-run mode that only prints the summary. Afterwards, run the T01
|
||||
rehearsal.
|
||||
|
||||
**Done when:** a test renews from the 2026-09-27 layout to a new envelope.
|
||||
The owner check passes on the result. A second renew with an unsettled
|
||||
reservation refuses. No digest has to be typed by hand.
|
||||
|
||||
### Implemented — 2026-10-13
|
||||
|
||||
The command is `rein-aharness owner renew --owner-config --policy --ledger
|
||||
--runtime-path --envelope [--apply]`, in `rein_aharness/owner_renew.py`.
|
||||
Without `--apply` it is a dry run. Choices:
|
||||
|
||||
- **Envelope file:** only the envelope fields may change. Worker, target,
|
||||
profile and grant pins carry over, and `SpendPolicy` validation runs on
|
||||
the result.
|
||||
- **Paths stay the same:** the old ledger is renamed to
|
||||
`.retired-<old envelope>`, never reset, and the old policy and owner.json
|
||||
are kept as `.bak`. Secrets Engine pins the `exec_owner` files by sha256
|
||||
and the ledger path through its fixed environment, so keeping the paths
|
||||
means the catalog needs only new file hashes. The command prints them.
|
||||
- **Refusals:** a held reservation, an open request route, an open SQLite
|
||||
transaction, an owner.json that doesn't match the policy, a reused
|
||||
envelope id, a disallowed field, or leftovers from an earlier renewal.
|
||||
|
||||
Tests (`tests/test_owner_renew.py`):
|
||||
|
||||
- A dry run changes nothing.
|
||||
- An applied renewal passes the real owner check (`prepare`) at the
|
||||
unchanged paths.
|
||||
- A charged reservation, the railiance01 state after the 2026-10-13
|
||||
reconciliation, renews, and the row survives in the retired ledger.
|
||||
- Bad envelopes and a second apply both refuse.
|
||||
|
||||
Finding for T06: renewal still changes the pinned file hashes, so each
|
||||
renewal needs a Secrets Engine catalog update and fresh approvals.
|
||||
|
||||
## C. Envelopes that cover several attempts (decision)
|
||||
|
||||
```task
|
||||
id: REINAH-WP-0008-T04
|
||||
status: done
|
||||
decision_id: "a93619a9-8a70-40d8-aaf9-c29357d2a043"
|
||||
priority: medium
|
||||
state_hub_task_id: "898e02af-51bb-5e8d-93d0-ae908466c612"
|
||||
```
|
||||
|
||||
Owner decision: should future envelopes be admitted as "up to N attempts
|
||||
within EUR X" instead of one attempt? The EUR 10 cap already fits two
|
||||
USD 4.64 request holds. Record the choice, together with T02's sign-off,
|
||||
as a decision note in `docs/`. T03's envelope format then reflects it.
|
||||
|
||||
### Decided — 2026-10-14
|
||||
|
||||
The owner chose up to three attempts within EUR 10 (EUR 5 per attempt) and
|
||||
observed-usage settlement, and resolved hub decision `a93619a9` with that
|
||||
choice. Record: `docs/decisions/2026-10-14-spend-envelope-settlement.md`.
|
||||
The terms are in spend memo `infd-20261014-b01`.
|
||||
|
||||
## E. Ship the owner runtime as a container image (decision)
|
||||
|
||||
```task
|
||||
id: REINAH-WP-0008-T05
|
||||
status: wait
|
||||
needs_human: true
|
||||
decision_id: "e6ca2f07-2708-4013-bfba-bf89fa0f1cb5"
|
||||
priority: low
|
||||
state_hub_task_id: "b486e86b-9d0f-501d-a394-771b40f771ee"
|
||||
```
|
||||
|
||||
Decision for later, with Platform (railiance) and Sand-boxer: should the
|
||||
owner runtime ship through the registry and GitOps, using the existing
|
||||
`make image` path, instead of rsync and a manual digest check? Out of scope
|
||||
here. If accepted, hand it off as a residual intake to the owning repos.
|
||||
|
||||
## F. Approvals per envelope (decision)
|
||||
|
||||
```task
|
||||
id: REINAH-WP-0008-T06
|
||||
status: wait
|
||||
needs_human: true
|
||||
decision_id: "e8986e40-f6f8-4c87-b704-bff50cc4958b"
|
||||
priority: low
|
||||
state_hub_task_id: "4cf721e6-724b-5c64-91bb-67b098bb97af"
|
||||
```
|
||||
|
||||
Decision for later, with Secrets Engine: should one approval set cover an
|
||||
envelope's attempts instead of six native approvals per attempt? The work
|
||||
lives outside this repo. If accepted, hand it off as a residual intake.
|
||||
|
||||
## Retry on the corrected runtime
|
||||
|
||||
```task
|
||||
id: REINAH-WP-0008-T07
|
||||
status: wait
|
||||
needs_human: true
|
||||
blocking_reason: "Owner: accept spend memo infd-20261014-b01, then attended install, rehearsal, renew and approvals on railiance01"
|
||||
priority: high
|
||||
state_hub_task_id: "80318842-ccaa-5484-a54a-3d9981eaec8b"
|
||||
```
|
||||
|
||||
Waits on T01 and T03. It does not need T02.
|
||||
|
||||
Steps:
|
||||
|
||||
1. Rebuild `ee3d0a53…`, or the then-current corrected runtime, from the
|
||||
pinned commits.
|
||||
2. Install it on railiance01.
|
||||
3. Run the T01 rehearsal.
|
||||
4. Renew with T03 to the owner-authorized new envelope (EUR 10, authorized
|
||||
2026-10-13).
|
||||
5. Run exactly one attended attempt, which consumes fresh native approvals.
|
||||
|
||||
Record the result in REINAH-WP-0003-T05/T06.
|
||||
|
||||
### Waiting on the owner — 2026-10-14
|
||||
|
||||
Everything this repo can do is done: the rehearsal (T01), the settlement rule
|
||||
(T02, T04) and renewal (T03) are in place. Draft memo `infd-20261014-b01` is
|
||||
in informed-decision `ee6a65b`. What remains needs the owner, in order:
|
||||
|
||||
1. Accept the memo in the attended sitting.
|
||||
2. ~~Rebuild `ee3d0a53` from the pinned commits~~ (done 2026-10-15 as
|
||||
`818b1b7c…`, see below) and install it on railiance01.
|
||||
3. Run `metered-once --rehearse`.
|
||||
4. Run `owner renew` with the memo's terms, then update the Secrets Engine
|
||||
file pins.
|
||||
5. Approve the native actions and run the first attempt.
|
||||
|
||||
Sand-boxer's steps 2–4 in message `82faf25c` are this same sequence.
|
||||
|
||||
### Runtime rebuilt — 2026-10-15
|
||||
|
||||
Step 1 is done. The owner approved a rebuild because `ee3d0a53…` was not kept
|
||||
on the workstation. Sand-boxer built `818b1b7c…` from the same pinned inputs
|
||||
and builder. The digest differs only because the build-source paths are
|
||||
embedded in the artifact. The bwrap startup smoke passed (sandbox `28df7a1d`)
|
||||
with no credentials and no model run. `818b1b7c…` is now the install
|
||||
candidate. It is not installed on railiance01 (still `b6e4e8a4…`) or admitted.
|
||||
Receipt: `docs/evidence/2026-10-15-rebuilt-owner-runtime.json`. The memo still
|
||||
still has to be created, admitted, loaded and accepted first. The owner steps
|
||||
are in informed-decision `docs/batches/2026-10-14-three-attempts/OPERATOR.md`.
|
||||
|
||||
### Memo queued — 2026-10-15
|
||||
|
||||
Owner step 1 is in progress via the lifecycle intake path (owner's choice; the
|
||||
review-record approval counts as spend authority). Workspace draft
|
||||
`2ccb4bda-e318-51d2-a8dd-709db768494f`, review
|
||||
`review-e2d2e98f-4701-45b0-abc1-4fe9f00bd1a8`, queued by the owner in the State
|
||||
Hub memo dispatcher; approval intent `accept` by `helixforge-factory`. Record:
|
||||
informed-decision `d5f1021` (sitting.json, OPERATOR.md). The accepted approval id
|
||||
becomes `authority_ref` for `owner renew`. Install candidate remains `818b1b7c`.
|
||||
|
||||
|
||||
### Workload-only Glas source integration — 2026-10-16
|
||||
|
||||
Reviewed and integrated the one-line runtime-contract lock advance from Glas
|
||||
`30be81808140a3a3d137e731d1fe9aed59ad8581` to published workload-only credential
|
||||
delivery `7357115c8311e55ee3d495f5266f501a17ec3c89`. llm-connect `37436bb5` and
|
||||
sandboxer `221126cb` remain pinned. This is source integration under T07, not
|
||||
native artifact installation or admission. T07 remains waiting with its existing
|
||||
owner gates.
|
||||
|
||||
Prior preparation used rein base `be5ef400` plus isolated lock-only commit
|
||||
`c811fc43` to build candidate
|
||||
`634e65fee5517ebee0d1080805fd84c81bd46af26a2b38ec1ab14bd2be17bf8f`. The
|
||||
candidate's installed packages passed actual SandboxManager/Glas gateway proof
|
||||
with unchanged `harness.agent-dev-local@1.1.1` (still unverified): real rein/Glas
|
||||
CLI and Claude 2.1.266 startup, read-only runtime, synthetic host-file and
|
||||
inherited environment/child-process denial, exact task identity/private state,
|
||||
deterministic workspace Git effect, task removal and destruction. A separately
|
||||
labelled synthetic route fixture passed workload-only delivery, output redaction
|
||||
and clean subsequent housekeeping through the same actual manager. Wrong
|
||||
actor/project/run, expired execution, expiry cleanup, cancellation and
|
||||
post-cancellation refusal passed. All 141 Glas tests and seven rein lock/rehearsal
|
||||
tests passed; whole artifact digest remained unchanged after proof.
|
||||
|
||||
The first proof runner correctly encountered the manager's PermissionError for
|
||||
wrong consumer but expected ValueError/RuntimeError. Its finally cleanup ran;
|
||||
the runner catch was refined and the failure retained. This does not earn an
|
||||
unchanged execution success. No model/provider request, real credential read,
|
||||
queue access or spend occurred.
|
||||
|
||||
The preparation artifact is not the artifact of this integrated owner revision.
|
||||
Rebuild from the exact committed owner source and verify its new digest and
|
||||
actual-manager proof before considering installation. Source inputs, receipts,
|
||||
reproducible proof and attempt history are retained in the
|
||||
[Custodian evidence directory](../../the-custodian/docs/changes/netkingdom-target-2026-10-16/agent-isolation-next/README.md).
|
||||
The same directory records the integrated-source rebuild separately.
|
||||
|
||||
Retain `818b1b7c`, `ee3d0a53`, installed `b6e4e8a4` and their outcomes as historical
|
||||
evidence. Native acceptance still requires the queued spend memo lifecycle,
|
||||
owner artifact installation/rehearsal, renewal and Secrets Engine file-pin
|
||||
updates, required native approvals and bounded real-model/tool proof. Actual
|
||||
interactive assignment migration and external tool-path denial remain Custodian
|
||||
acceptance obligations; no profile readiness or supervision authority changed.
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"observed_at": "2026-10-17T06:55:02.225819+00:00",
|
||||
"head": "9b88cadaa817e5abea7950b4a264d4e2739a5ddb",
|
||||
"status_before": "",
|
||||
"initial_review_manifest_sha256": "sha256:cae64cd2c52623dadc1905f0f418a91c99ab1eb981e02accc28855fe22c43f28",
|
||||
"index_sha256": "sha256:08c186d73338c3eb03a8216c7025fdb6cb386a647802f4df7f096c2f177ff98c",
|
||||
"all_8_uuids_preserved": true,
|
||||
"all_7_status_human_decision_and_other_task_fields_preserved": true,
|
||||
"canonical_write_pending": true,
|
||||
"native_proposal_compatibility": "Original native-install R1/R2 and command helpers remain immutable. Stable REINAH-prefixed proposal ID is an opaque trial ID and is not renamed; actual canonical owner task is clarified as HARNESS-WP-0008-T07 with identical UUID80318842-ccaa-5484-a54a-3d9981eaec8b. This is no action revision, scope change or authority."
|
||||
}
|
||||
|
|
@ -0,0 +1,116 @@
|
|||
--- a/AGENTS.md
|
||||
+++ b/AGENTS.md
|
||||
@@ -11,8 +11,10 @@
|
||||
|
||||
`REIN-A-0001` through `REIN-A-0004` are immutable historical identifiers from
|
||||
before the fleet `PREFIX-WP-NNNN` convention. `HARNESS-WP-0001` through
|
||||
-`HARNESS-WP-0003` and `HARNESS-WP-ADHOC-2026-09-04` are older ids from the
|
||||
-agent-harness name. Preserve all of these in references and archives, but use
|
||||
+`HARNESS-WP-0003`, `HARNESS-WP-0008` and
|
||||
+`HARNESS-WP-ADHOC-2026-09-04` are preserved historical ids. Workplan 0008
|
||||
+keeps its originally issued HARNESS identity after the 2026-10-17 correction
|
||||
+of an erroneous REINAH relabel; its existing REINAH filename is only a path. Preserve all of these in references and archives, but use
|
||||
the hub-registered `REINAH-WP-` prefix for every new workplan and task.
|
||||
|
||||
---
|
||||
--- a/workplans/REINAH-WP-0008-simpler-metered-retry.md
|
||||
+++ b/workplans/REINAH-WP-0008-simpler-metered-retry.md
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
-id: REINAH-WP-0008
|
||||
+id: HARNESS-WP-0008
|
||||
type: workplan
|
||||
title: "Simpler metered retry: free rehearsal, zero-cost hold release, one-command renewal"
|
||||
domain: infotech
|
||||
@@ -9,15 +9,26 @@
|
||||
topic_slug: rein-aharness
|
||||
priority: high
|
||||
created: "2026-10-13"
|
||||
-updated: "2026-10-16"
|
||||
+updated: "2026-10-17"
|
||||
related:
|
||||
- REINAH-WP-0003
|
||||
state_hub_workstream_id: "187c2599-84a6-5764-a14f-7e9121975bf2"
|
||||
---
|
||||
|
||||
-# REINAH-WP-0008 — Simpler metered retry
|
||||
+# HARNESS-WP-0008 — Simpler metered retry
|
||||
|
||||
## Origin
|
||||
+
|
||||
+Identity correction, 2026-10-17: restore the originally issued
|
||||
+`HARNESS-WP-0008` and T01–T07 canonical IDs, retaining every existing Hub UUID,
|
||||
+status, decision and human/native gate. Commit `a85de661` briefly relabelled
|
||||
+this workplan `REINAH-WP-0008` while preserving HARNESS-derived UUIDs in response
|
||||
+to a C-26 warning. Canon grandfathering preserves existing IDs; C-26 applies
|
||||
+to new plans and supplies no record-rekey authority. The REINAH filename remains
|
||||
+as an existing backing path, and the erroneous label remains historical
|
||||
+provenance rather than another canonical record. Future new workplans still
|
||||
+use REINAH-WP. No UUID migration, retirement, human disposition, installation,
|
||||
+spend authority or native acceptance is granted by this source correction.
|
||||
|
||||
On 2026-10-13 the owner reconciled the 2026-09-27 hold for run `6efa9436`
|
||||
(`docs/evidence/2026-10-13-held-liability-reconciliation.json`). A corrected
|
||||
@@ -45,7 +56,7 @@
|
||||
## A. Free rehearsal on the host
|
||||
|
||||
```task
|
||||
-id: REINAH-WP-0008-T01
|
||||
+id: HARNESS-WP-0008-T01
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "c6536c8d-7862-5de6-ad2b-401077b60c04"
|
||||
@@ -91,7 +102,7 @@
|
||||
## B. Release holds that provably cost nothing
|
||||
|
||||
```task
|
||||
-id: REINAH-WP-0008-T02
|
||||
+id: HARNESS-WP-0008-T02
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "6a413f5a-4ac2-5d5e-9b19-71b33445c0a9"
|
||||
@@ -140,7 +151,7 @@
|
||||
## D. One owner command for renewal
|
||||
|
||||
```task
|
||||
-id: REINAH-WP-0008-T03
|
||||
+id: HARNESS-WP-0008-T03
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "87457216-2371-51ca-b3d4-3917e6f180a2"
|
||||
@@ -196,7 +207,7 @@
|
||||
## C. Envelopes that cover several attempts (decision)
|
||||
|
||||
```task
|
||||
-id: REINAH-WP-0008-T04
|
||||
+id: HARNESS-WP-0008-T04
|
||||
status: done
|
||||
decision_id: "a93619a9-8a70-40d8-aaf9-c29357d2a043"
|
||||
priority: medium
|
||||
@@ -218,7 +229,7 @@
|
||||
## E. Ship the owner runtime as a container image (decision)
|
||||
|
||||
```task
|
||||
-id: REINAH-WP-0008-T05
|
||||
+id: HARNESS-WP-0008-T05
|
||||
status: wait
|
||||
needs_human: true
|
||||
decision_id: "e6ca2f07-2708-4013-bfba-bf89fa0f1cb5"
|
||||
@@ -234,7 +245,7 @@
|
||||
## F. Approvals per envelope (decision)
|
||||
|
||||
```task
|
||||
-id: REINAH-WP-0008-T06
|
||||
+id: HARNESS-WP-0008-T06
|
||||
status: wait
|
||||
needs_human: true
|
||||
decision_id: "e8986e40-f6f8-4c87-b704-bff50cc4958b"
|
||||
@@ -249,7 +260,7 @@
|
||||
## Retry on the corrected runtime
|
||||
|
||||
```task
|
||||
-id: REINAH-WP-0008-T07
|
||||
+id: HARNESS-WP-0008-T07
|
||||
status: wait
|
||||
needs_human: true
|
||||
blocking_reason: "Owner: accept spend memo infd-20261014-b01, then attended install, rehearsal, renew and approvals on railiance01"
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
--- a/workplans/SAND-WP-0015-bwrap-runtime-and-private-state.md
|
||||
+++ b/workplans/SAND-WP-0015-bwrap-runtime-and-private-state.md
|
||||
@@ -89,7 +89,7 @@
|
||||
id: SAND-WP-0015-T04
|
||||
status: wait
|
||||
blocked_on: message-from:rein-aharness
|
||||
-blocking_reason: "Tracked as REINAH-WP-0008-T07 (owner, attended): accept spend memo infd-20261014-b01 (up to three attempts within EUR 10, observed-usage settlement), rebuild/install ee3d0a53 on railiance01, rehearse, renew, then a successful attempt."
|
||||
+blocking_reason: "Tracked as HARNESS-WP-0008-T07 (owner, attended): accept spend memo infd-20261014-b01 (up to three attempts within EUR 10, observed-usage settlement), rebuild/install ee3d0a53 on railiance01, rehearse, renew, then a successful attempt."
|
||||
priority: high
|
||||
state_hub_task_id: "58817ef6-76d9-5e34-908f-c024e6c99f93"
|
||||
```
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
--- a/workplans/SAND-WP-0015-bwrap-runtime-and-private-state.md
|
||||
+++ b/workplans/SAND-WP-0015-bwrap-runtime-and-private-state.md
|
||||
@@ -89,7 +89,7 @@
|
||||
id: SAND-WP-0015-T04
|
||||
status: wait
|
||||
blocked_on: message-from:rein-aharness
|
||||
-blocking_reason: "Tracked as REINAH-WP-0008-T07 (owner, attended): accept spend memo infd-20261014-b01 (up to three attempts within EUR 10, observed-usage settlement), rebuild/install ee3d0a53 on railiance01, rehearse, renew, then a successful attempt."
|
||||
+blocking_reason: "Tracked as HARNESS-WP-0008-T07 (owner, attended): accept spend memo infd-20261014-b01 (up to three attempts within EUR 10, observed-usage settlement), rebuild/install ee3d0a53 on railiance01, rehearse, renew, then a successful attempt."
|
||||
priority: high
|
||||
state_hub_task_id: "58817ef6-76d9-5e34-908f-c024e6c99f93"
|
||||
```
|
||||
@@ -354,12 +354,12 @@
|
||||
T04 stays `wait`; the plan stays `blocked`.
|
||||
|
||||
2026-10-14 check: rein-aharness pushed `9a4469d` (corrected build record) and
|
||||
-opened REINAH-WP-0008 (simpler metered retry, active). No railiance01 install/
|
||||
+opened HARNESS-WP-0008 (simpler metered retry, active). No railiance01 install/
|
||||
admission of `ee3d0a53` or successful acceptance receipt yet; request sent on
|
||||
hub message `82faf25c`. T04 stays `wait`; plan stays `blocked`.
|
||||
|
||||
2026-10-14 rein-aharness reply (thread `82faf25c`): step 1 done (`9a4469d` on
|
||||
-origin/main). Steps 2-4 are REINAH-WP-0008-T07. Budget is now up to three
|
||||
+origin/main). Steps 2-4 are HARNESS-WP-0008-T07. Budget is now up to three
|
||||
attempts within EUR 10 under memo `infd-20261014-b01` (observed-usage
|
||||
settlement); order: memo acceptance in the attended informed-decision sitting,
|
||||
rebuild and install `ee3d0a53` on railiance01, `metered-once --rehearse`,
|
||||
@@ -371,5 +371,5 @@
|
||||
absolute source paths (`direct_url.json`, `build-info.json`). Owner-entrypoint
|
||||
startup smoke passed in sandbox `28df7a1d`; no credentials or model calls.
|
||||
Sent to rein-aharness (message `8a7962f7`, thread `82faf25c`) to record as the
|
||||
-then-current corrected runtime for REINAH-WP-0008-T07 and to load the unsigned
|
||||
+then-current corrected runtime for HARNESS-WP-0008-T07 and to load the unsigned
|
||||
memo sitting. Nothing installed on railiance01. T04 stays `wait`.
|
||||
|
|
@ -0,0 +1 @@
|
|||
sha256:230d1ef37aa9c2023335a617492a72678c4671a8bc82d5577e9d7a7cea6e8fe4
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
{
|
||||
"observed_at": "2026-10-17T06:55:41.814838+00:00",
|
||||
"status": "authorized_source_identity_restored_not_published",
|
||||
"source_changed_paths": [
|
||||
{
|
||||
"path": "AGENTS.md",
|
||||
"before_sha256": "sha256:5d01af0ea255eab568ae72f326378219d1be4c77a9bc001ae12f4a1be2660034",
|
||||
"after_sha256": "sha256:da71c6e3d76d7c867b2db6e67cd919fba594acc3804d3784dd744438103560c2"
|
||||
},
|
||||
{
|
||||
"path": "workplans/REINAH-WP-0008-simpler-metered-retry.md",
|
||||
"before_sha256": "sha256:9aac375bdb2e93c57cd513ab8cd5bf0c0979a0699ea0181e6d405899d906bf68",
|
||||
"after_sha256": "sha256:1118924bb159f8a52a37ace8b8663c75274b89928f7a8b957638428af224b054"
|
||||
}
|
||||
],
|
||||
"head_unchanged": true,
|
||||
"index_unchanged": true,
|
||||
"source_uuid_identity_consistent": true,
|
||||
"all_hub_rows_and_two_dependency_rows_unchanged": true,
|
||||
"task_bindings": [
|
||||
{
|
||||
"canonical_source_id": "HARNESS-WP-0008-T01",
|
||||
"uuid": "c6536c8d-7862-5de6-ad2b-401077b60c04",
|
||||
"source_derived_uuid_matches_existing": true,
|
||||
"hub_unchanged": true,
|
||||
"status": "done",
|
||||
"needs_human": false,
|
||||
"record_id": null
|
||||
},
|
||||
{
|
||||
"canonical_source_id": "HARNESS-WP-0008-T02",
|
||||
"uuid": "6a413f5a-4ac2-5d5e-9b19-71b33445c0a9",
|
||||
"source_derived_uuid_matches_existing": true,
|
||||
"hub_unchanged": true,
|
||||
"status": "done",
|
||||
"needs_human": false,
|
||||
"record_id": null
|
||||
},
|
||||
{
|
||||
"canonical_source_id": "HARNESS-WP-0008-T03",
|
||||
"uuid": "87457216-2371-51ca-b3d4-3917e6f180a2",
|
||||
"source_derived_uuid_matches_existing": true,
|
||||
"hub_unchanged": true,
|
||||
"status": "done",
|
||||
"needs_human": false,
|
||||
"record_id": null
|
||||
},
|
||||
{
|
||||
"canonical_source_id": "HARNESS-WP-0008-T04",
|
||||
"uuid": "898e02af-51bb-5e8d-93d0-ae908466c612",
|
||||
"source_derived_uuid_matches_existing": true,
|
||||
"hub_unchanged": true,
|
||||
"status": "done",
|
||||
"needs_human": false,
|
||||
"record_id": null
|
||||
},
|
||||
{
|
||||
"canonical_source_id": "HARNESS-WP-0008-T05",
|
||||
"uuid": "b486e86b-9d0f-501d-a394-771b40f771ee",
|
||||
"source_derived_uuid_matches_existing": true,
|
||||
"hub_unchanged": true,
|
||||
"status": "wait",
|
||||
"needs_human": true,
|
||||
"record_id": null
|
||||
},
|
||||
{
|
||||
"canonical_source_id": "HARNESS-WP-0008-T06",
|
||||
"uuid": "4cf721e6-724b-5c64-91bb-67b098bb97af",
|
||||
"source_derived_uuid_matches_existing": true,
|
||||
"hub_unchanged": true,
|
||||
"status": "wait",
|
||||
"needs_human": true,
|
||||
"record_id": null
|
||||
},
|
||||
{
|
||||
"canonical_source_id": "HARNESS-WP-0008-T07",
|
||||
"uuid": "80318842-ccaa-5484-a54a-3d9981eaec8b",
|
||||
"source_derived_uuid_matches_existing": true,
|
||||
"hub_unchanged": true,
|
||||
"status": "wait",
|
||||
"needs_human": true,
|
||||
"record_id": null
|
||||
}
|
||||
],
|
||||
"no_hub_write": true,
|
||||
"no_retirement_acknowledged": true,
|
||||
"no_uuid_migration": true,
|
||||
"no_sync_or_publish": true,
|
||||
"driver_patches_only": true,
|
||||
"native_proposals_and_helpers_unchanged": true,
|
||||
"initial_review_history": "Initial review recommended separately revising native-install references; root clarified opaque stable proposal ID and pending R2 action stay immutable. The current companion mapping supersedes that recommendation without changing R1 review bytes.",
|
||||
"owner_task_mapping": {
|
||||
"old_erroneous_source_label": "REINAH-WP-0008-T07",
|
||||
"actual_canonical_owner_task": "HARNESS-WP-0008-T07",
|
||||
"unchanged_uuid": "80318842-ccaa-5484-a54a-3d9981eaec8b",
|
||||
"native_action_scope_or_revision_changed": false
|
||||
},
|
||||
"next_step": "Root reviews two current driver-reference patches, then normal owner preparation/source publication and receiving without retirement acknowledgement; confirm 7 null record_ids converge on HARNESS IDs with no UUID/status/human-gate change."
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
{
|
||||
"status": "source_metadata_only",
|
||||
"references": [
|
||||
{
|
||||
"repo": "rein-aharness",
|
||||
"path": "AGENTS.md",
|
||||
"head_observed": "9b88cadaa817e5abea7950b4a264d4e2739a5ddb",
|
||||
"sha256": "sha256:5d01af0ea255eab568ae72f326378219d1be4c77a9bc001ae12f4a1be2660034"
|
||||
},
|
||||
{
|
||||
"repo": "rein-aharness",
|
||||
"path": "workplans/REINAH-WP-0008-simpler-metered-retry.md",
|
||||
"head_observed": "9b88cadaa817e5abea7950b4a264d4e2739a5ddb",
|
||||
"sha256": "sha256:9aac375bdb2e93c57cd513ab8cd5bf0c0979a0699ea0181e6d405899d906bf68"
|
||||
},
|
||||
{
|
||||
"repo": "repo-manager",
|
||||
"path": "src/repo_manager/identifiers.py",
|
||||
"head_observed": "3bcb1e1cf3e58b263f690534f7f28024d75cdbf6",
|
||||
"sha256": "sha256:4e35fcd009c20822574fd33171176bd14a1ff2dd5af8b0bb2820954974593a2a"
|
||||
},
|
||||
{
|
||||
"repo": "repo-manager",
|
||||
"path": "docs/work-record-uuid-derivation_v1.md",
|
||||
"head_observed": "3bcb1e1cf3e58b263f690534f7f28024d75cdbf6",
|
||||
"sha256": "sha256:600f5c16a0f9325df56dd698311d0151571742782664a2d2bc791b8f5d964157"
|
||||
},
|
||||
{
|
||||
"repo": "state-hub",
|
||||
"path": "api/services/forge_projection.py",
|
||||
"head_observed": "200cbd0a61a805d0fe29f9d6d1c49a6cf1f1703a",
|
||||
"sha256": "sha256:b980e62cbd85c3e7df42b01abf14354e04133374db94af6531dcd91629c3487d"
|
||||
},
|
||||
{
|
||||
"repo": "state-hub",
|
||||
"path": "api/services/work_record_identifier_migration.py",
|
||||
"head_observed": "200cbd0a61a805d0fe29f9d6d1c49a6cf1f1703a",
|
||||
"sha256": "sha256:f02fc379d03b2ae78e2b2ed781430d45e025221fd55558be3df60105046ecf53"
|
||||
},
|
||||
{
|
||||
"repo": "state-hub",
|
||||
"path": "api/models/work_record_identifier_alias.py",
|
||||
"head_observed": "200cbd0a61a805d0fe29f9d6d1c49a6cf1f1703a",
|
||||
"sha256": "sha256:101e6013d52e5aeb66a44c8b666ff40cdbcd9e52028149c7b891d801039c71e5"
|
||||
},
|
||||
{
|
||||
"repo": "state-hub",
|
||||
"path": "tests/test_forge_projection.py",
|
||||
"head_observed": "200cbd0a61a805d0fe29f9d6d1c49a6cf1f1703a",
|
||||
"sha256": "sha256:3e41a2ba7b68dd86ec6d31984063d0d7846b41ec40a4545f9fcf9582c20cf9d2"
|
||||
},
|
||||
{
|
||||
"repo": "the-custodian",
|
||||
"path": "canon/standards/work-record-types_v0.1.md",
|
||||
"sha256": "sha256:ab5d9de07fd899fc5334c9912571ed3d6748f2ee4bd0c9f81c5606c4727024d1"
|
||||
},
|
||||
{
|
||||
"repo": "the-custodian",
|
||||
"path": "docs/changes/netkingdom-target-2026-10-17/native-gates-r2/rein-aharness-receiving.json",
|
||||
"sha256": "sha256:583561db0386f95650125045f786b96370d7743e132d24cf50342a56208333fd"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
"""Hash selected public image files without pulling, installing or running it."""
|
||||
from pathlib import Path, PurePosixPath
|
||||
import hashlib
|
||||
import io
|
||||
import json
|
||||
import re
|
||||
import tarfile
|
||||
import time
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
|
||||
BASE = Path(__file__).resolve().parent
|
||||
HOST = 'https://forgejo.coulomb.social'
|
||||
IMAGE = '/v2/coulomb/state-hub/'
|
||||
DIGEST = 'sha256:c802690ee1811b3d77df4077936f9cdced1385514e8673fcbc9232b84d645015'
|
||||
DEADLINE = time.monotonic() + 180
|
||||
class NoRedirect(urllib.request.HTTPRedirectHandler):
|
||||
def redirect_request(self, *args, **kwargs):
|
||||
raise RuntimeError('redirect_refused')
|
||||
opener = urllib.request.build_opener(urllib.request.ProxyHandler({}), NoRedirect)
|
||||
def get(path, limit, token=None, accept=None):
|
||||
assert time.monotonic() < DEADLINE
|
||||
headers = {}
|
||||
if token:
|
||||
headers['Authorization'] = 'Bearer ' + token
|
||||
if accept:
|
||||
headers['Accept'] = accept
|
||||
with opener.open(urllib.request.Request(HOST + path, headers=headers), timeout=30) as response:
|
||||
raw = response.read(limit + 1)
|
||||
assert len(raw) <= limit
|
||||
return raw
|
||||
query = urllib.parse.urlencode({'service': 'container_registry', 'scope': 'repository:coulomb/state-hub:pull'})
|
||||
auth = json.loads(get('/v2/token?' + query, 65536))
|
||||
token = auth.get('token', auth.get('access_token'))
|
||||
assert isinstance(token, str) and 0 < len(token) < 60000
|
||||
raw = get(IMAGE + 'manifests/' + DIGEST, 1048576, token,
|
||||
'application/vnd.docker.distribution.manifest.v2+json')
|
||||
assert 'sha256:' + hashlib.sha256(raw).hexdigest() == DIGEST
|
||||
manifest = json.loads(raw)
|
||||
assert manifest['schemaVersion'] == 2 and len(manifest['layers']) <= 80
|
||||
assert sum(x['size'] for x in manifest['layers']) <= 500 * 1024**2
|
||||
files, packages, layers = {}, {}, []
|
||||
for layer in manifest['layers']:
|
||||
pin = layer['digest']
|
||||
assert re.fullmatch('sha256:[0-9a-f]{64}', pin) and 0 <= layer['size'] <= 160 * 1024**2
|
||||
blob = get(IMAGE + 'blobs/' + pin, layer['size'], token)
|
||||
assert len(blob) == layer['size'] and 'sha256:' + hashlib.sha256(blob).hexdigest() == pin
|
||||
with tarfile.open(fileobj=io.BytesIO(blob), mode='r|*') as archive:
|
||||
count = 0
|
||||
for entry in archive:
|
||||
count += 1
|
||||
assert count <= 100000 and time.monotonic() < DEADLINE
|
||||
path = entry.name.removeprefix('./')
|
||||
assert not path.startswith('/') and '..' not in PurePosixPath(path).parts
|
||||
parent, _, basename = path.rpartition('/')
|
||||
# Apply OCI deletions to the selected metadata view.
|
||||
if basename.startswith('.wh.'):
|
||||
erased = parent + '/' + basename[4:]
|
||||
for key in list(files):
|
||||
if key == erased or key.startswith(erased + '/'):
|
||||
del files[key]
|
||||
for key in list(packages):
|
||||
if key == erased or key.startswith(erased + '/'):
|
||||
del packages[key]
|
||||
continue
|
||||
code = (path == 'app/api/services/historical_projection.py'
|
||||
or path.startswith('app/migrations/versions/') and path.endswith('.py')
|
||||
or '/site-packages/hub_core/' in path and path.endswith('.py')
|
||||
or path.startswith('tmp/hub-core/hub_core/') and path.endswith('.py')
|
||||
or path == 'tmp/.ci-build-id')
|
||||
metadata = '/site-packages/' in path and path.endswith('.dist-info/METADATA')
|
||||
if not code and not metadata:
|
||||
continue
|
||||
assert entry.isfile() and entry.size <= 2 * 1024**2
|
||||
stream = archive.extractfile(entry)
|
||||
content = stream.read(2 * 1024**2 + 1)
|
||||
assert len(content) == entry.size
|
||||
if code:
|
||||
files[path] = 'sha256:' + hashlib.sha256(content).hexdigest()
|
||||
if metadata:
|
||||
text = content.decode('utf-8')
|
||||
name = re.findall(r'^Name: ([A-Za-z0-9_.-]+)$', text, re.M)
|
||||
version = re.findall(r'^Version: ([A-Za-z0-9_.+!-]+)$', text, re.M)
|
||||
assert len(name) == len(version) == 1
|
||||
packages[path] = {'name': name[0], 'version': version[0],
|
||||
'metadata_sha256': 'sha256:' + hashlib.sha256(content).hexdigest()}
|
||||
layers.append({'digest': pin, 'compressed_bytes': len(blob), 'archive_entries': count})
|
||||
del blob
|
||||
expected = 'sha256:' + hashlib.sha256(Path('/home/worsch/state-hub/api/services/historical_projection.py').read_bytes()).hexdigest()
|
||||
assert files.get('app/api/services/historical_projection.py') == expected
|
||||
assert files.get('tmp/.ci-build-id') == 'sha256:' + hashlib.sha256(b'51bc70d\n').hexdigest()
|
||||
migration = 'app/migrations/versions/c4d5e6f7a8b9_inbox_feed_revision.py'
|
||||
assert files.get(migration) == 'sha256:48f1730037be99b5623e878d7f20a06bec243ce27317fde81c82e16006d7b3ce'
|
||||
del auth, token
|
||||
result = {'schema': 'statehub.public-image-inspection.v1', 'status': 'verified_selected_image_files',
|
||||
'source_revision': '51bc70d1ff8f25450a8650d8e50caf053093a02e', 'image_digest': DIGEST,
|
||||
'anonymous_protocol_only': True, 'token_retained': False, 'image_executed': False,
|
||||
'image_installed_or_daemon_pulled': False, 'layers': layers, 'files': files,
|
||||
'python_package_metadata': packages,
|
||||
'limitations': ['Selected code and package metadata inventory; no full application acceptance or hermetic build provenance.',
|
||||
'Floating dependency origins remain unknown; observed bytes and versions are bound to this immutable image digest.']}
|
||||
(BASE / 'statehub-public-image-inspection.json').write_text(json.dumps(result, indent=2) + '\n')
|
||||
print(json.dumps({'status': result['status'], 'layers': len(layers), 'selected_files': len(files), 'packages': len(packages)}))
|
||||
|
|
@ -0,0 +1,368 @@
|
|||
---
|
||||
id: KEY-WP-0039
|
||||
type: workplan
|
||||
title: "Negative-check tokens for the secrets-engine OpenBao JWT role"
|
||||
domain: infotech
|
||||
repo: key-cape
|
||||
status: active
|
||||
flavor: implementation
|
||||
owner: claude-code
|
||||
topic_slug: netkingdom
|
||||
created: "2026-10-10"
|
||||
updated: "2026-10-17"
|
||||
depends_on: []
|
||||
related_repos:
|
||||
- railiance-platform
|
||||
- secrets-engine
|
||||
state_hub_workstream_id: "09c3bddf-6058-5603-8488-cccdb7be4f86"
|
||||
---
|
||||
|
||||
# KEY-WP-0039 - Negative-check tokens for the secrets-engine OpenBao JWT role
|
||||
|
||||
## Why
|
||||
|
||||
CCR-2026-0038 (railiance-platform RPF-WP-0035-T02) requires the OpenBao role
|
||||
`secrets-engine-openbao` on `auth/keycape-services` to refuse JWTs with a wrong
|
||||
`aud`, `sub`, `tenant`, `principal_type`, `roles` or `scope`, a coding-agent JWT
|
||||
and an expired JWT. The positive login proof passed on 2026-10-10 (secrets-engine
|
||||
`docs/evidence/2026-10-10-keycape-openbao-probe.json`). railiance-platform asked
|
||||
whether KeyCape can issue the negative tokens without touching the
|
||||
`secrets-engine` registration.
|
||||
|
||||
KeyCape signs only what a registration declares. It has no way to mint a token
|
||||
with arbitrary claims, and adding one would create a forging path in the issuer.
|
||||
So the negative tokens must come from real, separate registrations that differ
|
||||
from `secrets-engine` in exactly one property each.
|
||||
|
||||
## Boundaries
|
||||
|
||||
- The `secrets-engine` registration is not changed.
|
||||
- Probe clients are confidential `client_credentials` clients with the minimum
|
||||
60-second lifetime, scopes that grant nothing, and secrets minted into custody
|
||||
like any other client. They are removed after the proof.
|
||||
- The probe script prints only accept or refuse booleans per case, never tokens or
|
||||
secrets. It runs attended.
|
||||
|
||||
## T01 — Define the probe registrations
|
||||
|
||||
```task
|
||||
id: KEY-WP-0039-T01
|
||||
status: done
|
||||
priority: medium
|
||||
state_hub_task_id: "4d06b059-3768-53bb-83a4-a9a71c0850b8"
|
||||
```
|
||||
|
||||
Write a non-secret request packet under `docs/` with one probe client per claim
|
||||
mismatch, each identical to `secrets-engine` except for:
|
||||
|
||||
- `aud`: a different access-token resource audience
|
||||
- `sub`: a different service subject
|
||||
- `tenant`: a tenant other than `platform`
|
||||
- `roles`: a role other than `secrets-engine`
|
||||
- `scope`: a scope outside the role's bound scope
|
||||
|
||||
Two cases need no probe client. A service token always carries
|
||||
`principal_type: service` (`src/internal/server/oidc/token.go`), so the wrong
|
||||
`principal_type` case uses a human access token from an attended login. The
|
||||
coding-agent case uses the existing `codex-railiance-platform` registration (role
|
||||
`coding-agent`, `docs/openbao-service-auth-contract.md`) if it is registered live;
|
||||
T01 confirms that.
|
||||
|
||||
The expired-JWT case reuses any probe client: request a token, wait past its
|
||||
60-second lifetime, then present it.
|
||||
|
||||
Acceptance: railiance-platform agrees the packet covers every refusal that
|
||||
CCR-2026-0038 names.
|
||||
|
||||
2026-10-10: railiance-platform agreed that the case list covers every
|
||||
CCR-2026-0038 claim negative. The remaining CCR checks (capabilities-self deny, no
|
||||
provider fallback) are theirs and secrets-engine's. Packet written at
|
||||
`docs/openbao-negative-probe-clients.yaml`. It departs from the agreed list in two
|
||||
ways, both sent back for re-agreement:
|
||||
|
||||
- **Expired case.** A token from another probe already fails a claim, so its
|
||||
refusal does not show that expiry was checked. It needs a sixth client,
|
||||
`openbao-probe-expiry`, identical to `secrets-engine-openbao` in every claim. That
|
||||
makes it a second credential able to log in as secrets-engine for the session.
|
||||
OpenBao's `expiration_leeway` (150s by default) also extends the wait.
|
||||
- **Audience pin.** A service token's `aud` is its `clientId` unless `audience` is
|
||||
set. Every probe except the aud probe therefore pins
|
||||
`audience: secrets-engine-openbao`, so it fails only on its own claim.
|
||||
|
||||
Condition (a), no OpenBao custody, holds. The values still have to reach KeyCape's
|
||||
environment, because KeyCape resolves `secretRef` at startup. The packet uses a
|
||||
transient Kubernetes Secret that is deleted in the same session.
|
||||
|
||||
## T02 — Register the probe clients, run the attended probe, then remove them
|
||||
|
||||
```task
|
||||
id: KEY-WP-0039-T02
|
||||
status: wait
|
||||
depends_on: [KEY-WP-0039-T01, KEY-WP-0038-T02, KEY-WP-0038-T03]
|
||||
blocking_reason: "Ordered identity gate: Forgejo first SecurityCase registration/claims proof, then NetKingdom registry session and attended human/coding-agent custody; registration authorization already resolved."
|
||||
priority: medium
|
||||
state_hub_task_id: "03da4c38-1dac-53b1-8b0b-e697df48e9d3"
|
||||
```
|
||||
|
||||
2026-10-11: requalified — live registration go caafe4be was already resolved; owner authorization present. See the-custodian docs/decisions/2026-10-11-security-unblock-decisions.md.
|
||||
|
||||
Write the attended script, which prints booleans only.
|
||||
|
||||
2026-10-10: script written at `scripts/openbao_negative_probe.py`, with tests in
|
||||
`scripts/test_openbao_negative_probe.py`. It meets condition (b): one refused
|
||||
boolean per case plus the refusal class (`claim_mismatch`, `expired`,
|
||||
`not_service_principal`), and no token, secret, claim value or OpenBao error text.
|
||||
A transport failure or an unexpected response is reported as `inconclusive`, never
|
||||
as a refusal. A token that OpenBao accepts is revoked at once and fails the run.
|
||||
Still waiting: railiance-platform's re-agreement on the two departures above, and
|
||||
an attended registry session with net-kingdom. Register the clients
|
||||
through the normal registration path, hand the run to railiance-platform, record
|
||||
a value-free receipt in `docs/evidence/`, and deregister the clients afterwards.
|
||||
|
||||
Acceptance: every case is refused, the receipt is committed, and the probe clients
|
||||
are gone.
|
||||
|
||||
|
||||
2026-10-11 source preparation: T01 remains progress because the exact expiry
|
||||
client and audience-pin amendments require platform-owner re-agreement; the
|
||||
prior agreement covered the earlier case list, not this revised packet. The
|
||||
prepared runner revision adds a valid expiry positive control and immediate
|
||||
revoke, exact decoded claim-isolation preflight (not signature evidence),
|
||||
semantic refusal classes, bounded expiry TTL and redirect refusal. Seventeen
|
||||
local tests pass; original-source semantic regressions demonstrate two false
|
||||
successes. See `docs/openbao-negative-probe-session-proposal.md` for exact owner
|
||||
returns, live authority, effective leeway, custody boundaries and cleanup. No
|
||||
live token, registry or OpenBao action occurred. T02 continues to wait for owner
|
||||
re-agreement plus separate attended live authorization. MASON-WP-0009-T05
|
||||
operator/ESO capability and nonmember checks remain independent acceptance.
|
||||
|
||||
2026-10-11: Platform independently re-agreed the final amended design (session proposal SHA25623f13d2a5a4204f749f35c5d44ef39d213838cd2fce1c36fb4775254ef1c7f09). Original conditional review and narrow confirmation are retained in railiance-platform/docs/reviews/2026-10-11-keycape-openbao-negative-session-owner-review*. Source acceptance T01 is complete; actual registry/leeway/readiness, founder live go, negative/cleanup and separate capability/no-fallback obligations remain T02 and owner plans.
|
||||
|
||||
|
||||
2026-10-17 Custodian coordinator finalization review (not a KeyCape formal owner
|
||||
acknowledgment): the [infrastructure assessment](../../the-custodian/docs/changes/netkingdom-target-2026-10-17/key39-infra-finalization-r1/ASSESSMENT.md)
|
||||
confirms all 14 executable/test/producer-decision pins and three Platform source
|
||||
pins still match. Source reviews remain complete: T01/T03 done, T02 wait. Existing
|
||||
source acceptance remains valid. Resolved registration go caafe4be covers only
|
||||
the original secrets-engine-openbao client (KEY-WP-0037-T03, deployed 6f79a2af,
|
||||
NK-WP-0046 exact-match helper); it does not authorize new probe clients or
|
||||
coding-client custody, and no renewed original-client approval is requested.
|
||||
The standing attended rule remains separate, for reviewed commands within
|
||||
existing scope; no new protocol or blanket approval request is introduced. The
|
||||
remaining sequence is KEY-WP-0038-T02/T03 first-case registration/claims proof,
|
||||
admission of the existing coding-client proposal with sanitized owner readiness
|
||||
returns, then one attended negative/revoke/cleanup receipt committed with probe
|
||||
clients removed. No native acceptance or credential/registration action is
|
||||
inferred from this coordinator review.
|
||||
|
||||
|
||||
## KeyCape issuer-source compatibility inspection for lifecycle v2
|
||||
|
||||
```task
|
||||
id: KEY-WP-0039-T03
|
||||
status: done
|
||||
priority: medium
|
||||
state_hub_task_id: "54d37ad8-2352-51c2-bb2e-af6070dbbd73"
|
||||
```
|
||||
|
||||
2026-10-11: Completed KeyCape's own issuer-source inspection for
|
||||
KEY-WP-0039-T02/attended-lifecycle-v2, exact manifest SHA256
|
||||
6fc0fe47f99a97c11c6e7538f5ea02054335df7d46055d3f84bed4e194aeaaed.
|
||||
Read AGENTS.md, governing INTENT.md, current client-credentials claim source,
|
||||
registration fragments, native caller-authentication commands and frozen v2
|
||||
issuer transport/probe wiring. Seventeen unchanged runner tests pass locally.
|
||||
This task completes only issuer-source inspection; the resulting narrow
|
||||
source acknowledgment is in docs/reviews/2026-10-11-key39-lifecycle-v2-keycape-source-ack.json (retained frozen prior acknowledgment).
|
||||
T01 stays done and T02 stays open for actual native registration, token input,
|
||||
refusal and cleanup proof. No Secret, token, credential or native environment was accessed; no owner
|
||||
checkout was changed during this source inspection. Owner source files were
|
||||
read for the bounded issuer compatibility check.
|
||||
|
||||
Current token.go:306-309 returns HTTP 401 invalid_profile_usage with
|
||||
feature=client_id for a removed confidential client; wrong-secret
|
||||
feature=Authorization cannot count as removed-registration evidence. The
|
||||
proposal and packet comments are narrowly corrected against that current
|
||||
issuer contract. Prior T01/source re-agreement history is preserved; this source
|
||||
acknowledgment supplies no NetKingdom runtime-owner review or founder live go.
|
||||
|
||||
The private token-producer handoff remains a real execution gap under T02.
|
||||
Existing keycape login/service-token commands require a new private output
|
||||
file; login also needs a concrete admitted public PKCE client/callback and
|
||||
attended browser/MFA. The lifecycle expects an in-memory private stdin object
|
||||
containing human_token and coding_agent_token, but no reviewed file-to-process
|
||||
or process-to-process producer, admitted human client/callback, custody-injected
|
||||
coding-agent source, native placement or producer cleanup receipt has been
|
||||
provided. No unattended token producer or admitted secret route is invented.
|
||||
Resolve those exact owner contracts before founder native approval; existing
|
||||
static registration is not live token custody or producer admission.
|
||||
|
||||
|
||||
2026-10-11 same T03 source-inspection refinement for lifecycle V4: completed
|
||||
KeyCape issuer-facing inspection before the new formal acknowledgment in
|
||||
docs/reviews/2026-10-11-key39-lifecycle-v4-keycape-source-ack.json. Exact V4
|
||||
manifest SHA256 d079f85c47d5c2635e1ee633ea4d1346b1737ce2be9ca273f22a38e620f418d8,
|
||||
executor b4687c2d07819c43b45d0cddcad0488b62eb49175b2a06a43f7806a0838d3a4d.
|
||||
NetKingdom's independent V4 source review is approved, receipt SHA256
|
||||
4f4498203a31bd46dbc3715c0df1cbb9eee63bed5b2c1baf430ff3c4a37c13cb.
|
||||
The V2/V3 unsuccessful reviews and earlier KeyCape acknowledgment remain
|
||||
retained as one correction trial; they are not extra scored successes.
|
||||
|
||||
V4 retains the unchanged issuer runner and six-client claim design, and fixes
|
||||
finite/nonboolean windows, true total request deadlines, late-known OpenBao
|
||||
session private revoke/refusal and guarded bounded final Secret deletion.
|
||||
The issuer-facing acknowledgement covers only stopping late token exchanges,
|
||||
claim source/shape and preserving token cleanup failure/unknown outcomes; it
|
||||
does not replace NetKingdom runtime or Platform backend acceptance. Tests: 41
|
||||
supplied lifecycle, 17 unchanged runner and 17 independent owner checks pass
|
||||
locally. No native tokens, credentials, issuer mutations or live cleanup were
|
||||
performed. T01 stays done; T02 stays open and the genuine private token-producer
|
||||
handoff remains its next source prerequisite before actual founder native go.
|
||||
|
||||
|
||||
2026-10-11 same T03 source-inspection refinement for lifecycle V5: completed
|
||||
KeyCape's own current issuer-source/claim/cleanup-contract inspection before
|
||||
any new formal acknowledgment. Current token.go:306-309 still distinguishes
|
||||
removed-client invalid_profile_usage feature=client_id from wrong-secret
|
||||
feature=Authorization. The six-client design, audience source and expiry
|
||||
positive control remain unchanged, and the original runner's 17 tests pass.
|
||||
Exact V5 manifest SHA256
|
||||
d1f997624db46693a7126a6eb6435f6b3a353d5bd67dcc8a539683e4b7147285;
|
||||
executor 3bcceebc845b17492040c76223d77088257c547a2b890198e7b5c2b04a0cd360.
|
||||
V5 has 46 passing supplied lifecycle checks. It adds a single exceptional
|
||||
response-close abort under a fixed20ms cap clipped to nonresettable aggregate
|
||||
cleanup, and to positive original request time remaining, with expired aggregate
|
||||
zero calls, fixed private errors and truthful known-session revoke/unknown
|
||||
outcomes. This inspection covers issuer-facing token lifetime and claim
|
||||
interfaces only; NetKingdom and Platform independently own their runtime and
|
||||
backend review decisions. Previous V2/V3 Net source refusals and Platform V4
|
||||
exceptional-close refusal remain retained; KeyCape's V2/V4 acknowledgments are
|
||||
historical narrow source inspections, never aggregate native approval.
|
||||
T01 remains done, T02 open. No native actions or credentials occurred. The real
|
||||
private token-producer/client/callback/coding-agent custody/cleanup handoff gap
|
||||
stays the next prerequisite; no unattended admission is inferred.
|
||||
|
||||
Exact independent V5 source decisions were then verified: NetKingdom approved
|
||||
receipt SHA256 32a62bd1ca1bd7311965d016d4d6a6e4d9177f3423c63add436d9d309865eacc
|
||||
(46 supplied +17 runner +21 independent checks); Platform approved receipt
|
||||
SHA256 ba872dc53061b1dcca9dea1e8897597c394c1f9365e0fc49ee6f2095f57f11e0
|
||||
(46 supplied +5 independent +17 previously verified runner checks). Those are
|
||||
foreign owners' own source decisions, not KeyCape runtime/backend reviews or
|
||||
founder native authority. Formal KeyCape bounded issuer acknowledgment follows
|
||||
in docs/reviews/2026-10-11-key39-lifecycle-v5-keycape-source-ack.json.
|
||||
|
||||
|
||||
2026-10-11 T02 continuing source preparation: private producer decision packet
|
||||
`KEY-WP-0039-T02/private-producer-handoff-v1` stages supported existing CLI
|
||||
flows, selected existing netkingdom-bootstrap-console literal-loopback human
|
||||
client subject to owner purpose/MFA/listener admission, and the existing
|
||||
codex-railiance-platform source identity subject to actual live/custody/consumer
|
||||
admission. No existing coding-agent delivery receipt was found; AppRole tokens
|
||||
do not substitute for issuer JWTs. A bounded pipe-only adapter for the current
|
||||
CLI's two caller-owned private files has 18 passing synthetic checks, with
|
||||
identity-guarded explicit file consumption and fixed value-free failure output.
|
||||
Decoded claim shape does not prove signatures or authenticated admission.
|
||||
See docs/openbao-negative-token-producer-decision.md and .json for exact owner
|
||||
choices and missing receipts. The exact trusted workstation-to-node launcher,
|
||||
private-file allowance and railiance01 Bao route/public-health guard still need
|
||||
source review and founder disposition before any token/temporary credential/
|
||||
mutation/restart. No registration, custody, login, native action or credential
|
||||
request occurred. V5 and failed review histories retained; T01 stays done and
|
||||
T02 open. No new work record or native acceptance is inferred.
|
||||
|
||||
|
||||
2026-10-11 same T02 source-preparation correction for
|
||||
KEY-WP-0039-T02/private-producer-handoff-v2: NetKingdom's exact V1 consumer
|
||||
refusal remains recorded (no child-ready gate, stdio alias, non-atomic unlink).
|
||||
V2 has source-pinned private launch/ready handshake compatible with exact
|
||||
Net V9 manifest84542ddc: fresh nonce/source/envelope/health/window marker before
|
||||
ANY caller-token file load/consume/write; separate local data/control pipes
|
||||
reject kernel identity aliases of stdin/stdout/stderr. Adapter read/write budget
|
||||
only clips downward after readiness. 31 distinct producer/launcher synthetic
|
||||
checks and 74 compatible consumer supplied checks pass. No SSH/native/login or
|
||||
credential access was performed. Stat/unlink remains non-atomic and requires
|
||||
continuously exclusive sameUID custody; the retained race test is a custody
|
||||
violation counterexample, not a claim of repaired filesystem atomicity. Receipt
|
||||
keeps atomic delete/copy cleanup unverified and possible copies true. Exact
|
||||
trusted launcher/directSSH profile/private tmpfs allowance, selected human
|
||||
client/MFA purpose reuse and coding-agent registration/custody/delivery remain
|
||||
unadmitted owner choices. Native node route, role/leeway/window/founder decision
|
||||
and actual negative/cleanup acceptance remain pending. T01done and T02open; no
|
||||
new task or plan, and V1/V5-V8/current failure histories are not overwritten.
|
||||
|
||||
|
||||
2026-10-11 same T02 producer-preparation correction V3: Platform V2 pair
|
||||
source refusal RPF-KEY39-PAIR-V2-F01 is retained. Private write uncertainty
|
||||
now propagates into sticky remote consumer uncertainty before launcher early
|
||||
return; local SSH termination never proves remote no-effects/recovery. Actual
|
||||
full-payload then interrupted confirmation and4096-byte partialpipe failures
|
||||
are retained regressions. 33 distinct source checks pass. Exact Net V9 source
|
||||
bytes unchanged; new source reviews required, not implicit repin. Native
|
||||
client/MFA/coding-agent custody/transport/tmpfsfile and actualroute/session
|
||||
authority remain unadmitted. T01done/T02open; no new task/plan or native action.
|
||||
|
||||
|
||||
2026-10-11 same T02 producer-preparation V4: exact V3 Net consumer refusal
|
||||
NET-PRODUCER-V3-F01 retained. Actual CLI contract acquisition now bounded
|
||||
nofollow/nonblocking regular stable reads and one ORIGINAL5-second preparation
|
||||
budget across contract/source/envelope/SSHspawn/ready/file/delivery phases, not
|
||||
a renewed launcher allowance. 40 own source tests plus9 retained paired
|
||||
wrapper/child handshake regressions pass with synthetic/no native transport.
|
||||
V3stickyremoteunknown/full+partialdelivery regressions retained; Net V9 exact
|
||||
bytes unchanged. Prior issuer/custody/transport/file/route/readiness-Argo/role/
|
||||
maintenanceexception/founder choices remain unadmitted. No SSH/network/Secrets/
|
||||
credentials/native/owner checkout changes; no task/plan duplication. T01done
|
||||
and T02open; source correction is not native acceptance.
|
||||
|
||||
|
||||
2026-10-11 T02 source-design disposition checkpoint: founder accepted exact
|
||||
source memo `infd-20261011-key39-source-design-r1`, review
|
||||
`review-62626f54-0212-4684-9b45-0a7222d61957` version1 at `2026-10-11T17:19:01Z`.
|
||||
Disposition `disp-870f9c1b-0444-4216-ae7f-f5463d590e36`, approval
|
||||
`ca118afc-d746-4454-a5b9-9b387b120cd9`; authenticated person subject
|
||||
`uid=platform-root,ou=people,dc=netkingdom,dc=local`. Exact canonical public review URL:
|
||||
`https://decisions.coulomb.social/review?memo_id=review-62626f54-0212-4684-9b45-0a7222d61957`.
|
||||
Root's authenticated confirmed readback is
|
||||
`the-custodian/docs/evidence/2026-10-10-security-consolidation/key39-source-design-publication/founder-source-disposition-readback-r1.json`,
|
||||
SHA256 `453213194d605336e9c424976f1ac27691def39e225026f0a99628d1cfc97d1d`; observed
|
||||
`2026-10-11T17:30:47.080081+00:00`. Source commands SHA256
|
||||
`36e20813596f3ddbb86e01fd0b838867ece2f4916c5880305cf12cd61b567f89` and source inventory SHA256
|
||||
`aea196db9e513900d5e7165455d6e9c3dc68749a606dc219ec340ac595642cc0` bind the accepted source direction.
|
||||
|
||||
Acceptance covers recommended source preparation for existing bootstrap-console
|
||||
human-client purpose reuse, returning existing coding-agent custody admission or
|
||||
preparing a narrow owner proposal, proposed founder directSSH source profile,
|
||||
and conditional exclusive-owner tmpfs files. It does not establish actual
|
||||
registration/MFA/listener, coding-agent custody/delivery, transport/file admission,
|
||||
node-local Bao route, issuer/OpenBao role/leeway/maintenance readiness or fresh
|
||||
attended native go. The published choice documents remain historical exact
|
||||
pending proposals; this appended receipt records their later source disposition.
|
||||
T02 remains wait on external owner custody/admission and separate attended
|
||||
execution authorization. Source acceptance permits further source preparation;
|
||||
actual registry, token exchange, negative probe, revoke and cleanup remain pending. T01 and T03
|
||||
remain done. No native or credential action is authorized by this source receipt.
|
||||
See docs/proposals/2026-10-11-key39-next-execution-prerequisites.json for exact
|
||||
owner returns and safe metadata evidence needed before preparing a runnable
|
||||
attended execution proposal.
|
||||
|
||||
2026-10-11 ordered-goal qualification (T02 only): the final source runner already
|
||||
exists and its 17 local semantic tests pass. No new helper or source protocol is
|
||||
needed to repeat accepted preparation. Native execution follows the first
|
||||
Forgejo SecurityCase registration and claims proof (KEY-WP-0038-T02/T03);
|
||||
only those task metadata were checked, not wait-task bodies. NetKingdom owns the
|
||||
registry/env/restart session, KeyCape owns issuance/isolation, and Platform owns
|
||||
leeway/refusal/capability/no-fallback/cleanup proof. The live registration go is
|
||||
already recorded resolved; do not ask again. Attended human authentication and
|
||||
owner-held existing coding-agent custody go on the single attended checklist.
|
||||
No credentials, live registry/API changes or KEY-WP-0040 recovery work occurred.
|
||||
|
||||
2026-10-17 Platform owner returns (source-prepared, not admitted):
|
||||
railiance-platform `docs/proposals/2026-10-17-key39-t02-owner-returns.md`
|
||||
(commit `0a9617a`). The coding client `codex-railiance-platform` has no
|
||||
admission, custody or delivery record. The existing narrow proposal
|
||||
`docs/proposals/2026-10-11-key39-coding-client-custody-proposal.json` (`9460f6f`)
|
||||
remains the one to admit, so the coding-agent case stays unrunnable until owners
|
||||
admit it. The role source (`openbao/auth/secrets-engine-jwt-role.json`) matches
|
||||
the packet's claim design. It declares no `expiration_leeway`, so the expiry
|
||||
probe must assume OpenBao's 150s default until an attended sanitized read
|
||||
confirms the live value. The node-local Bao origin is not declared in source and
|
||||
also needs that attended read. T02 stays wait.
|
||||
|
|
@ -0,0 +1,368 @@
|
|||
---
|
||||
id: KEY-WP-0039
|
||||
type: workplan
|
||||
title: "Negative-check tokens for the secrets-engine OpenBao JWT role"
|
||||
domain: infotech
|
||||
repo: key-cape
|
||||
status: active
|
||||
flavor: implementation
|
||||
owner: claude-code
|
||||
topic_slug: netkingdom
|
||||
created: "2026-10-10"
|
||||
updated: "2026-10-17"
|
||||
depends_on: []
|
||||
related_repos:
|
||||
- railiance-platform
|
||||
- secrets-engine
|
||||
state_hub_workstream_id: "09c3bddf-6058-5603-8488-cccdb7be4f86"
|
||||
---
|
||||
|
||||
# KEY-WP-0039 - Negative-check tokens for the secrets-engine OpenBao JWT role
|
||||
|
||||
## Why
|
||||
|
||||
CCR-2026-0038 (railiance-platform RPF-WP-0035-T02) requires the OpenBao role
|
||||
`secrets-engine-openbao` on `auth/keycape-services` to refuse JWTs with a wrong
|
||||
`aud`, `sub`, `tenant`, `principal_type`, `roles` or `scope`, a coding-agent JWT
|
||||
and an expired JWT. The positive login proof passed on 2026-10-10 (secrets-engine
|
||||
`docs/evidence/2026-10-10-keycape-openbao-probe.json`). railiance-platform asked
|
||||
whether KeyCape can issue the negative tokens without touching the
|
||||
`secrets-engine` registration.
|
||||
|
||||
KeyCape signs only what a registration declares. It has no way to mint a token
|
||||
with arbitrary claims, and adding one would create a forging path in the issuer.
|
||||
So the negative tokens must come from real, separate registrations that differ
|
||||
from `secrets-engine` in exactly one property each.
|
||||
|
||||
## Boundaries
|
||||
|
||||
- The `secrets-engine` registration is not changed.
|
||||
- Probe clients are confidential `client_credentials` clients with the minimum
|
||||
60-second lifetime, scopes that grant nothing, and secrets minted into custody
|
||||
like any other client. They are removed after the proof.
|
||||
- The probe script prints only accept or refuse booleans per case, never tokens or
|
||||
secrets. It runs attended.
|
||||
|
||||
## T01 — Define the probe registrations
|
||||
|
||||
```task
|
||||
id: KEY-WP-0039-T01
|
||||
status: done
|
||||
priority: medium
|
||||
state_hub_task_id: "4d06b059-3768-53bb-83a4-a9a71c0850b8"
|
||||
```
|
||||
|
||||
Write a non-secret request packet under `docs/` with one probe client per claim
|
||||
mismatch, each identical to `secrets-engine` except for:
|
||||
|
||||
- `aud`: a different access-token resource audience
|
||||
- `sub`: a different service subject
|
||||
- `tenant`: a tenant other than `platform`
|
||||
- `roles`: a role other than `secrets-engine`
|
||||
- `scope`: a scope outside the role's bound scope
|
||||
|
||||
Two cases need no probe client. A service token always carries
|
||||
`principal_type: service` (`src/internal/server/oidc/token.go`), so the wrong
|
||||
`principal_type` case uses a human access token from an attended login. The
|
||||
coding-agent case uses the existing `codex-railiance-platform` registration (role
|
||||
`coding-agent`, `docs/openbao-service-auth-contract.md`) if it is registered live;
|
||||
T01 confirms that.
|
||||
|
||||
The expired-JWT case reuses any probe client: request a token, wait past its
|
||||
60-second lifetime, then present it.
|
||||
|
||||
Acceptance: railiance-platform agrees the packet covers every refusal that
|
||||
CCR-2026-0038 names.
|
||||
|
||||
2026-10-10: railiance-platform agreed that the case list covers every
|
||||
CCR-2026-0038 claim negative. The remaining CCR checks (capabilities-self deny, no
|
||||
provider fallback) are theirs and secrets-engine's. Packet written at
|
||||
`docs/openbao-negative-probe-clients.yaml`. It departs from the agreed list in two
|
||||
ways, both sent back for re-agreement:
|
||||
|
||||
- **Expired case.** A token from another probe already fails a claim, so its
|
||||
refusal does not show that expiry was checked. It needs a sixth client,
|
||||
`openbao-probe-expiry`, identical to `secrets-engine-openbao` in every claim. That
|
||||
makes it a second credential able to log in as secrets-engine for the session.
|
||||
OpenBao's `expiration_leeway` (150s by default) also extends the wait.
|
||||
- **Audience pin.** A service token's `aud` is its `clientId` unless `audience` is
|
||||
set. Every probe except the aud probe therefore pins
|
||||
`audience: secrets-engine-openbao`, so it fails only on its own claim.
|
||||
|
||||
Condition (a), no OpenBao custody, holds. The values still have to reach KeyCape's
|
||||
environment, because KeyCape resolves `secretRef` at startup. The packet uses a
|
||||
transient Kubernetes Secret that is deleted in the same session.
|
||||
|
||||
## T02 — Register the probe clients, run the attended probe, then remove them
|
||||
|
||||
```task
|
||||
id: KEY-WP-0039-T02
|
||||
status: wait
|
||||
depends_on: [KEY-WP-0039-T01, KEY-WP-0038-T02, KEY-WP-0038-T03]
|
||||
blocking_reason: "Ordered identity gate: Forgejo first SecurityCase registration/claims proof, then NetKingdom registry session and attended human/coding-agent custody; original secrets-engine-openbao registration go remains resolved; probe/coding-client custody and attended readiness are not established."
|
||||
priority: medium
|
||||
state_hub_task_id: "03da4c38-1dac-53b1-8b0b-e697df48e9d3"
|
||||
```
|
||||
|
||||
2026-10-11: requalified — live registration go caafe4be was already resolved; owner authorization present. See the-custodian docs/decisions/2026-10-11-security-unblock-decisions.md.
|
||||
|
||||
Write the attended script, which prints booleans only.
|
||||
|
||||
2026-10-10: script written at `scripts/openbao_negative_probe.py`, with tests in
|
||||
`scripts/test_openbao_negative_probe.py`. It meets condition (b): one refused
|
||||
boolean per case plus the refusal class (`claim_mismatch`, `expired`,
|
||||
`not_service_principal`), and no token, secret, claim value or OpenBao error text.
|
||||
A transport failure or an unexpected response is reported as `inconclusive`, never
|
||||
as a refusal. A token that OpenBao accepts is revoked at once and fails the run.
|
||||
Still waiting: railiance-platform's re-agreement on the two departures above, and
|
||||
an attended registry session with net-kingdom. Register the clients
|
||||
through the normal registration path, hand the run to railiance-platform, record
|
||||
a value-free receipt in `docs/evidence/`, and deregister the clients afterwards.
|
||||
|
||||
Acceptance: every case is refused, the receipt is committed, and the probe clients
|
||||
are gone.
|
||||
|
||||
|
||||
2026-10-11 source preparation: T01 remains progress because the exact expiry
|
||||
client and audience-pin amendments require platform-owner re-agreement; the
|
||||
prior agreement covered the earlier case list, not this revised packet. The
|
||||
prepared runner revision adds a valid expiry positive control and immediate
|
||||
revoke, exact decoded claim-isolation preflight (not signature evidence),
|
||||
semantic refusal classes, bounded expiry TTL and redirect refusal. Seventeen
|
||||
local tests pass; original-source semantic regressions demonstrate two false
|
||||
successes. See `docs/openbao-negative-probe-session-proposal.md` for exact owner
|
||||
returns, live authority, effective leeway, custody boundaries and cleanup. No
|
||||
live token, registry or OpenBao action occurred. T02 continues to wait for owner
|
||||
re-agreement plus separate attended live authorization. MASON-WP-0009-T05
|
||||
operator/ESO capability and nonmember checks remain independent acceptance.
|
||||
|
||||
2026-10-11: Platform independently re-agreed the final amended design (session proposal SHA25623f13d2a5a4204f749f35c5d44ef39d213838cd2fce1c36fb4775254ef1c7f09). Original conditional review and narrow confirmation are retained in railiance-platform/docs/reviews/2026-10-11-keycape-openbao-negative-session-owner-review*. Source acceptance T01 is complete; actual registry/leeway/readiness, founder live go, negative/cleanup and separate capability/no-fallback obligations remain T02 and owner plans.
|
||||
|
||||
|
||||
2026-10-17 Custodian coordinator finalization review (not a KeyCape formal owner
|
||||
acknowledgment): the [infrastructure assessment](../../the-custodian/docs/changes/netkingdom-target-2026-10-17/key39-infra-finalization-r1/ASSESSMENT.md)
|
||||
confirms all 14 executable/test/producer-decision pins and three Platform source
|
||||
pins still match. Source reviews remain complete: T01/T03 done, T02 wait. Existing
|
||||
source acceptance remains valid. Resolved registration go caafe4be covers only
|
||||
the original secrets-engine-openbao client (KEY-WP-0037-T03, deployed 6f79a2af,
|
||||
NK-WP-0046 exact-match helper); it does not authorize new probe clients or
|
||||
coding-client custody, and no renewed original-client approval is requested.
|
||||
The standing attended rule remains separate, for reviewed commands within
|
||||
existing scope; no new protocol or blanket approval request is introduced. The
|
||||
remaining sequence is KEY-WP-0038-T02/T03 first-case registration/claims proof,
|
||||
admission of the existing coding-client proposal with sanitized owner readiness
|
||||
returns, then one attended negative/revoke/cleanup receipt committed with probe
|
||||
clients removed. No native acceptance or credential/registration action is
|
||||
inferred from this coordinator review.
|
||||
|
||||
|
||||
## KeyCape issuer-source compatibility inspection for lifecycle v2
|
||||
|
||||
```task
|
||||
id: KEY-WP-0039-T03
|
||||
status: done
|
||||
priority: medium
|
||||
state_hub_task_id: "54d37ad8-2352-51c2-bb2e-af6070dbbd73"
|
||||
```
|
||||
|
||||
2026-10-11: Completed KeyCape's own issuer-source inspection for
|
||||
KEY-WP-0039-T02/attended-lifecycle-v2, exact manifest SHA256
|
||||
6fc0fe47f99a97c11c6e7538f5ea02054335df7d46055d3f84bed4e194aeaaed.
|
||||
Read AGENTS.md, governing INTENT.md, current client-credentials claim source,
|
||||
registration fragments, native caller-authentication commands and frozen v2
|
||||
issuer transport/probe wiring. Seventeen unchanged runner tests pass locally.
|
||||
This task completes only issuer-source inspection; the resulting narrow
|
||||
source acknowledgment is in docs/reviews/2026-10-11-key39-lifecycle-v2-keycape-source-ack.json (retained frozen prior acknowledgment).
|
||||
T01 stays done and T02 stays open for actual native registration, token input,
|
||||
refusal and cleanup proof. No Secret, token, credential or native environment was accessed; no owner
|
||||
checkout was changed during this source inspection. Owner source files were
|
||||
read for the bounded issuer compatibility check.
|
||||
|
||||
Current token.go:306-309 returns HTTP 401 invalid_profile_usage with
|
||||
feature=client_id for a removed confidential client; wrong-secret
|
||||
feature=Authorization cannot count as removed-registration evidence. The
|
||||
proposal and packet comments are narrowly corrected against that current
|
||||
issuer contract. Prior T01/source re-agreement history is preserved; this source
|
||||
acknowledgment supplies no NetKingdom runtime-owner review or founder live go.
|
||||
|
||||
The private token-producer handoff remains a real execution gap under T02.
|
||||
Existing keycape login/service-token commands require a new private output
|
||||
file; login also needs a concrete admitted public PKCE client/callback and
|
||||
attended browser/MFA. The lifecycle expects an in-memory private stdin object
|
||||
containing human_token and coding_agent_token, but no reviewed file-to-process
|
||||
or process-to-process producer, admitted human client/callback, custody-injected
|
||||
coding-agent source, native placement or producer cleanup receipt has been
|
||||
provided. No unattended token producer or admitted secret route is invented.
|
||||
Resolve those exact owner contracts before founder native approval; existing
|
||||
static registration is not live token custody or producer admission.
|
||||
|
||||
|
||||
2026-10-11 same T03 source-inspection refinement for lifecycle V4: completed
|
||||
KeyCape issuer-facing inspection before the new formal acknowledgment in
|
||||
docs/reviews/2026-10-11-key39-lifecycle-v4-keycape-source-ack.json. Exact V4
|
||||
manifest SHA256 d079f85c47d5c2635e1ee633ea4d1346b1737ce2be9ca273f22a38e620f418d8,
|
||||
executor b4687c2d07819c43b45d0cddcad0488b62eb49175b2a06a43f7806a0838d3a4d.
|
||||
NetKingdom's independent V4 source review is approved, receipt SHA256
|
||||
4f4498203a31bd46dbc3715c0df1cbb9eee63bed5b2c1baf430ff3c4a37c13cb.
|
||||
The V2/V3 unsuccessful reviews and earlier KeyCape acknowledgment remain
|
||||
retained as one correction trial; they are not extra scored successes.
|
||||
|
||||
V4 retains the unchanged issuer runner and six-client claim design, and fixes
|
||||
finite/nonboolean windows, true total request deadlines, late-known OpenBao
|
||||
session private revoke/refusal and guarded bounded final Secret deletion.
|
||||
The issuer-facing acknowledgement covers only stopping late token exchanges,
|
||||
claim source/shape and preserving token cleanup failure/unknown outcomes; it
|
||||
does not replace NetKingdom runtime or Platform backend acceptance. Tests: 41
|
||||
supplied lifecycle, 17 unchanged runner and 17 independent owner checks pass
|
||||
locally. No native tokens, credentials, issuer mutations or live cleanup were
|
||||
performed. T01 stays done; T02 stays open and the genuine private token-producer
|
||||
handoff remains its next source prerequisite before actual founder native go.
|
||||
|
||||
|
||||
2026-10-11 same T03 source-inspection refinement for lifecycle V5: completed
|
||||
KeyCape's own current issuer-source/claim/cleanup-contract inspection before
|
||||
any new formal acknowledgment. Current token.go:306-309 still distinguishes
|
||||
removed-client invalid_profile_usage feature=client_id from wrong-secret
|
||||
feature=Authorization. The six-client design, audience source and expiry
|
||||
positive control remain unchanged, and the original runner's 17 tests pass.
|
||||
Exact V5 manifest SHA256
|
||||
d1f997624db46693a7126a6eb6435f6b3a353d5bd67dcc8a539683e4b7147285;
|
||||
executor 3bcceebc845b17492040c76223d77088257c547a2b890198e7b5c2b04a0cd360.
|
||||
V5 has 46 passing supplied lifecycle checks. It adds a single exceptional
|
||||
response-close abort under a fixed20ms cap clipped to nonresettable aggregate
|
||||
cleanup, and to positive original request time remaining, with expired aggregate
|
||||
zero calls, fixed private errors and truthful known-session revoke/unknown
|
||||
outcomes. This inspection covers issuer-facing token lifetime and claim
|
||||
interfaces only; NetKingdom and Platform independently own their runtime and
|
||||
backend review decisions. Previous V2/V3 Net source refusals and Platform V4
|
||||
exceptional-close refusal remain retained; KeyCape's V2/V4 acknowledgments are
|
||||
historical narrow source inspections, never aggregate native approval.
|
||||
T01 remains done, T02 open. No native actions or credentials occurred. The real
|
||||
private token-producer/client/callback/coding-agent custody/cleanup handoff gap
|
||||
stays the next prerequisite; no unattended admission is inferred.
|
||||
|
||||
Exact independent V5 source decisions were then verified: NetKingdom approved
|
||||
receipt SHA256 32a62bd1ca1bd7311965d016d4d6a6e4d9177f3423c63add436d9d309865eacc
|
||||
(46 supplied +17 runner +21 independent checks); Platform approved receipt
|
||||
SHA256 ba872dc53061b1dcca9dea1e8897597c394c1f9365e0fc49ee6f2095f57f11e0
|
||||
(46 supplied +5 independent +17 previously verified runner checks). Those are
|
||||
foreign owners' own source decisions, not KeyCape runtime/backend reviews or
|
||||
founder native authority. Formal KeyCape bounded issuer acknowledgment follows
|
||||
in docs/reviews/2026-10-11-key39-lifecycle-v5-keycape-source-ack.json.
|
||||
|
||||
|
||||
2026-10-11 T02 continuing source preparation: private producer decision packet
|
||||
`KEY-WP-0039-T02/private-producer-handoff-v1` stages supported existing CLI
|
||||
flows, selected existing netkingdom-bootstrap-console literal-loopback human
|
||||
client subject to owner purpose/MFA/listener admission, and the existing
|
||||
codex-railiance-platform source identity subject to actual live/custody/consumer
|
||||
admission. No existing coding-agent delivery receipt was found; AppRole tokens
|
||||
do not substitute for issuer JWTs. A bounded pipe-only adapter for the current
|
||||
CLI's two caller-owned private files has 18 passing synthetic checks, with
|
||||
identity-guarded explicit file consumption and fixed value-free failure output.
|
||||
Decoded claim shape does not prove signatures or authenticated admission.
|
||||
See docs/openbao-negative-token-producer-decision.md and .json for exact owner
|
||||
choices and missing receipts. The exact trusted workstation-to-node launcher,
|
||||
private-file allowance and railiance01 Bao route/public-health guard still need
|
||||
source review and founder disposition before any token/temporary credential/
|
||||
mutation/restart. No registration, custody, login, native action or credential
|
||||
request occurred. V5 and failed review histories retained; T01 stays done and
|
||||
T02 open. No new work record or native acceptance is inferred.
|
||||
|
||||
|
||||
2026-10-11 same T02 source-preparation correction for
|
||||
KEY-WP-0039-T02/private-producer-handoff-v2: NetKingdom's exact V1 consumer
|
||||
refusal remains recorded (no child-ready gate, stdio alias, non-atomic unlink).
|
||||
V2 has source-pinned private launch/ready handshake compatible with exact
|
||||
Net V9 manifest84542ddc: fresh nonce/source/envelope/health/window marker before
|
||||
ANY caller-token file load/consume/write; separate local data/control pipes
|
||||
reject kernel identity aliases of stdin/stdout/stderr. Adapter read/write budget
|
||||
only clips downward after readiness. 31 distinct producer/launcher synthetic
|
||||
checks and 74 compatible consumer supplied checks pass. No SSH/native/login or
|
||||
credential access was performed. Stat/unlink remains non-atomic and requires
|
||||
continuously exclusive sameUID custody; the retained race test is a custody
|
||||
violation counterexample, not a claim of repaired filesystem atomicity. Receipt
|
||||
keeps atomic delete/copy cleanup unverified and possible copies true. Exact
|
||||
trusted launcher/directSSH profile/private tmpfs allowance, selected human
|
||||
client/MFA purpose reuse and coding-agent registration/custody/delivery remain
|
||||
unadmitted owner choices. Native node route, role/leeway/window/founder decision
|
||||
and actual negative/cleanup acceptance remain pending. T01done and T02open; no
|
||||
new task or plan, and V1/V5-V8/current failure histories are not overwritten.
|
||||
|
||||
|
||||
2026-10-11 same T02 producer-preparation correction V3: Platform V2 pair
|
||||
source refusal RPF-KEY39-PAIR-V2-F01 is retained. Private write uncertainty
|
||||
now propagates into sticky remote consumer uncertainty before launcher early
|
||||
return; local SSH termination never proves remote no-effects/recovery. Actual
|
||||
full-payload then interrupted confirmation and4096-byte partialpipe failures
|
||||
are retained regressions. 33 distinct source checks pass. Exact Net V9 source
|
||||
bytes unchanged; new source reviews required, not implicit repin. Native
|
||||
client/MFA/coding-agent custody/transport/tmpfsfile and actualroute/session
|
||||
authority remain unadmitted. T01done/T02open; no new task/plan or native action.
|
||||
|
||||
|
||||
2026-10-11 same T02 producer-preparation V4: exact V3 Net consumer refusal
|
||||
NET-PRODUCER-V3-F01 retained. Actual CLI contract acquisition now bounded
|
||||
nofollow/nonblocking regular stable reads and one ORIGINAL5-second preparation
|
||||
budget across contract/source/envelope/SSHspawn/ready/file/delivery phases, not
|
||||
a renewed launcher allowance. 40 own source tests plus9 retained paired
|
||||
wrapper/child handshake regressions pass with synthetic/no native transport.
|
||||
V3stickyremoteunknown/full+partialdelivery regressions retained; Net V9 exact
|
||||
bytes unchanged. Prior issuer/custody/transport/file/route/readiness-Argo/role/
|
||||
maintenanceexception/founder choices remain unadmitted. No SSH/network/Secrets/
|
||||
credentials/native/owner checkout changes; no task/plan duplication. T01done
|
||||
and T02open; source correction is not native acceptance.
|
||||
|
||||
|
||||
2026-10-11 T02 source-design disposition checkpoint: founder accepted exact
|
||||
source memo `infd-20261011-key39-source-design-r1`, review
|
||||
`review-62626f54-0212-4684-9b45-0a7222d61957` version1 at `2026-10-11T17:19:01Z`.
|
||||
Disposition `disp-870f9c1b-0444-4216-ae7f-f5463d590e36`, approval
|
||||
`ca118afc-d746-4454-a5b9-9b387b120cd9`; authenticated person subject
|
||||
`uid=platform-root,ou=people,dc=netkingdom,dc=local`. Exact canonical public review URL:
|
||||
`https://decisions.coulomb.social/review?memo_id=review-62626f54-0212-4684-9b45-0a7222d61957`.
|
||||
Root's authenticated confirmed readback is
|
||||
`the-custodian/docs/evidence/2026-10-10-security-consolidation/key39-source-design-publication/founder-source-disposition-readback-r1.json`,
|
||||
SHA256 `453213194d605336e9c424976f1ac27691def39e225026f0a99628d1cfc97d1d`; observed
|
||||
`2026-10-11T17:30:47.080081+00:00`. Source commands SHA256
|
||||
`36e20813596f3ddbb86e01fd0b838867ece2f4916c5880305cf12cd61b567f89` and source inventory SHA256
|
||||
`aea196db9e513900d5e7165455d6e9c3dc68749a606dc219ec340ac595642cc0` bind the accepted source direction.
|
||||
|
||||
Acceptance covers recommended source preparation for existing bootstrap-console
|
||||
human-client purpose reuse, returning existing coding-agent custody admission or
|
||||
preparing a narrow owner proposal, proposed founder directSSH source profile,
|
||||
and conditional exclusive-owner tmpfs files. It does not establish actual
|
||||
registration/MFA/listener, coding-agent custody/delivery, transport/file admission,
|
||||
node-local Bao route, issuer/OpenBao role/leeway/maintenance readiness or fresh
|
||||
attended native go. The published choice documents remain historical exact
|
||||
pending proposals; this appended receipt records their later source disposition.
|
||||
T02 remains wait on external owner custody/admission and separate attended
|
||||
execution authorization. Source acceptance permits further source preparation;
|
||||
actual registry, token exchange, negative probe, revoke and cleanup remain pending. T01 and T03
|
||||
remain done. No native or credential action is authorized by this source receipt.
|
||||
See docs/proposals/2026-10-11-key39-next-execution-prerequisites.json for exact
|
||||
owner returns and safe metadata evidence needed before preparing a runnable
|
||||
attended execution proposal.
|
||||
|
||||
2026-10-11 ordered-goal qualification (T02 only): the final source runner already
|
||||
exists and its 17 local semantic tests pass. No new helper or source protocol is
|
||||
needed to repeat accepted preparation. Native execution follows the first
|
||||
Forgejo SecurityCase registration and claims proof (KEY-WP-0038-T02/T03);
|
||||
only those task metadata were checked, not wait-task bodies. NetKingdom owns the
|
||||
registry/env/restart session, KeyCape owns issuance/isolation, and Platform owns
|
||||
leeway/refusal/capability/no-fallback/cleanup proof. The live registration go is
|
||||
already recorded resolved; do not ask again. Attended human authentication and
|
||||
owner-held existing coding-agent custody go on the single attended checklist.
|
||||
No credentials, live registry/API changes or KEY-WP-0040 recovery work occurred.
|
||||
|
||||
2026-10-17 Platform owner returns (source-prepared, not admitted):
|
||||
railiance-platform `docs/proposals/2026-10-17-key39-t02-owner-returns.md`
|
||||
(commit `0a9617a`). The coding client `codex-railiance-platform` has no
|
||||
admission, custody or delivery record. The existing narrow proposal
|
||||
`docs/proposals/2026-10-11-key39-coding-client-custody-proposal.json` (`9460f6f`)
|
||||
remains the one to admit, so the coding-agent case stays unrunnable until owners
|
||||
admit it. The role source (`openbao/auth/secrets-engine-jwt-role.json`) matches
|
||||
the packet's claim design. It declares no `expiration_leeway`, so the expiry
|
||||
probe must assume OpenBao's 150s default until an attended sanitized read
|
||||
confirms the live value. The node-local Bao origin is not declared in source and
|
||||
also needs that attended read. T02 stays wait.
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
from pathlib import Path
|
||||
import subprocess,json,hashlib,re,yaml,urllib.request
|
||||
root=Path('/home/worsch/key-cape');out=Path('/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1');out.mkdir()
|
||||
rel='workplans/KEY-WP-0039-openbao-negative-probe-tokens.md';p=root/rel
|
||||
|
||||
def git(*args):return subprocess.check_output(['git','-C',str(root),*args]).decode()
|
||||
base=git('rev-parse','HEAD').strip();assert base=='26ec9bb9b195ad377448570d6b161fd682311a0b';assert not git('status','--porcelain=v1');assert git('ls-remote','origin','refs/heads/main').split()[0]==base
|
||||
with urllib.request.urlopen('http://127.0.0.1:8000/tasks/03da4c38-1dac-53b1-8b0b-e697df48e9d3',timeout=30) as response:task_before=json.load(response)
|
||||
assert task_before.get('needs_human') is False
|
||||
before=p.read_text();old='registration authorization already resolved.';new='original secrets-engine-openbao registration go remains resolved; probe/coding-client custody and attended readiness are not established.';assert before.count(old)==1;after=before.replace(old,new)
|
||||
blocks=lambda text:[yaml.safe_load(t) for t in re.findall(r'^```task\n(.*?)^```',text,re.M|re.S)]
|
||||
b=blocks(before);a=blocks(after);assert len(a)==len(b)==3
|
||||
for oldblock,newblock in zip(b,a):
|
||||
if oldblock['id']=='KEY-WP-0039-T02':
|
||||
expected={**oldblock,'blocking_reason':oldblock['blocking_reason'].replace(old,new)};assert newblock==expected
|
||||
else:assert oldblock==newblock
|
||||
assert before.split('---',2)[1]==after.split('---',2)[1]
|
||||
(out/'KEY39.preimage.md').write_text(before);(out/'KEY39.proposed.md').write_text(after)
|
||||
baseline={'base_commit':base,'branch':git('branch','--show-current').strip(),'status':git('status','--porcelain=v1'),'index':git('ls-files','--stage'),'path':rel,'before_sha256':hashlib.sha256(before.encode()).hexdigest(),'after_sha256':hashlib.sha256(after.encode()).hexdigest(),'frontmatter':yaml.safe_load(before.split('---',2)[1]),'task_blocks':b,'task_blocks_after':a,'only_task_field_delta':{'task':'KEY-WP-0039-T02','field':'blocking_reason','old':b[1]['blocking_reason'],'new':a[1]['blocking_reason']},'needs_human_before':False,'task_before_public_metadata':{k:task_before.get(k) for k in ('id','status','needs_human','workplan_id','blocking_reason')}};(out/'baseline.json').write_text(json.dumps(baseline,indent=2)+'\n')
|
||||
p.write_text(after);(out/'one-path.patch').write_text(git('diff','--',rel));assert git('diff','--name-only').splitlines()==[rel]
|
||||
print(json.dumps({'status':'only authorized blocking_reason suffix changed','needs_human':False,'all_other_task_and_frontmatter_fields_unchanged':True,'base':base}))
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,173 @@
|
|||
{
|
||||
"schema": "state-hub.repository-projection-reconcile.v1",
|
||||
"instance_role": "primary",
|
||||
"instance_label": "railiance01",
|
||||
"expected_commit": "d803e9282a47b49daf930fb905312fa5104c0ace",
|
||||
"derived_commit": "d803e9282a47b49daf930fb905312fa5104c0ace",
|
||||
"outcome": {
|
||||
"schema": "state-hub.projection-reset.v1",
|
||||
"repo_slug": "key-cape",
|
||||
"commit": "d803e9282a47b49daf930fb905312fa5104c0ace",
|
||||
"status": "applied",
|
||||
"counts": {
|
||||
"created": 0,
|
||||
"updated": 44,
|
||||
"retired": 0,
|
||||
"refused": 0,
|
||||
"released": 0,
|
||||
"created_tasks": 0,
|
||||
"updated_tasks": 0,
|
||||
"cancelled_tasks": 0,
|
||||
"created_edges": 0,
|
||||
"removed_edges": 0,
|
||||
"unresolved_edges": 1,
|
||||
"external_edges": 8
|
||||
},
|
||||
"created": [],
|
||||
"updated": [
|
||||
"ADHOC-2026-09-05",
|
||||
"ADHOC-2026-09-07",
|
||||
"ADHOC-2026-09-27",
|
||||
"KEY-WP-0001",
|
||||
"KEY-WP-0002",
|
||||
"KEY-WP-0003",
|
||||
"KEY-WP-0004",
|
||||
"KEY-WP-0005",
|
||||
"KEY-WP-0006",
|
||||
"KEY-WP-0007",
|
||||
"KEY-WP-0008",
|
||||
"KEY-WP-0009",
|
||||
"KEY-WP-0010",
|
||||
"KEY-WP-0011",
|
||||
"KEY-WP-0012",
|
||||
"KEY-WP-0013",
|
||||
"KEY-WP-0014",
|
||||
"KEY-WP-0015",
|
||||
"KEY-WP-0016",
|
||||
"KEY-WP-0017",
|
||||
"KEY-WP-0018",
|
||||
"KEY-WP-0019",
|
||||
"KEY-WP-0020",
|
||||
"KEY-WP-0021",
|
||||
"KEY-WP-0022",
|
||||
"KEY-WP-0023",
|
||||
"KEY-WP-0024",
|
||||
"KEY-WP-0025",
|
||||
"KEY-WP-0026",
|
||||
"KEY-WP-0027",
|
||||
"KEY-WP-0028",
|
||||
"KEY-WP-0029",
|
||||
"KEY-WP-0030",
|
||||
"KEY-WP-0031",
|
||||
"KEY-WP-0032",
|
||||
"KEY-WP-0033",
|
||||
"KEY-WP-0034",
|
||||
"KEY-WP-0035",
|
||||
"KEY-WP-0036",
|
||||
"KEY-WP-0037",
|
||||
"KEY-WP-0038",
|
||||
"KEY-WP-0039",
|
||||
"KEY-WP-0040",
|
||||
"KEY-WP-0041"
|
||||
],
|
||||
"retired": [],
|
||||
"released": [],
|
||||
"created_tasks": [],
|
||||
"updated_tasks": [],
|
||||
"cancelled_tasks": [],
|
||||
"created_edges": [],
|
||||
"removed_edges": [],
|
||||
"unresolved_edges": [
|
||||
{
|
||||
"workplan": "KEY-WP-0010",
|
||||
"from": "KEY-WP-0010",
|
||||
"target": "RAILIANCE-WP-0027",
|
||||
"reason": "missing"
|
||||
}
|
||||
],
|
||||
"external_edges": [
|
||||
{
|
||||
"workplan": "KEY-WP-0038",
|
||||
"from": "KEY-WP-0038-T03",
|
||||
"target": "KEY-WP-0038-T02",
|
||||
"relationship_type": "starts_after",
|
||||
"from_workplan_id": "098e3e2c-a138-560e-b2d3-3a15eb49d8a9",
|
||||
"from_task_id": "40ddaa29-7245-508c-ad48-ddc063e25115",
|
||||
"to_workplan_id": null,
|
||||
"to_task_id": "ddcfb482-8f33-5c81-9ce8-84be8fa5dc30"
|
||||
},
|
||||
{
|
||||
"workplan": "KEY-WP-0038",
|
||||
"from": "KEY-WP-0038-T02",
|
||||
"target": "KEY-WP-0038-T01",
|
||||
"relationship_type": "starts_after",
|
||||
"from_workplan_id": "098e3e2c-a138-560e-b2d3-3a15eb49d8a9",
|
||||
"from_task_id": "ddcfb482-8f33-5c81-9ce8-84be8fa5dc30",
|
||||
"to_workplan_id": null,
|
||||
"to_task_id": "5f019d13-1fa3-5b88-a27a-75a3339ca96e"
|
||||
},
|
||||
{
|
||||
"workplan": "KEY-WP-0038",
|
||||
"from": "KEY-WP-0038-T02",
|
||||
"target": "NK-WP-0048-T06",
|
||||
"relationship_type": "starts_after",
|
||||
"from_workplan_id": "098e3e2c-a138-560e-b2d3-3a15eb49d8a9",
|
||||
"from_task_id": "ddcfb482-8f33-5c81-9ce8-84be8fa5dc30",
|
||||
"to_workplan_id": null,
|
||||
"to_task_id": "d9b26bcf-a7c6-59a2-92a0-f913d76a203c"
|
||||
},
|
||||
{
|
||||
"workplan": "KEY-WP-0039",
|
||||
"from": "KEY-WP-0039-T02",
|
||||
"target": "KEY-WP-0038-T03",
|
||||
"relationship_type": "starts_after",
|
||||
"from_workplan_id": "09c3bddf-6058-5603-8488-cccdb7be4f86",
|
||||
"from_task_id": "03da4c38-1dac-53b1-8b0b-e697df48e9d3",
|
||||
"to_workplan_id": null,
|
||||
"to_task_id": "40ddaa29-7245-508c-ad48-ddc063e25115"
|
||||
},
|
||||
{
|
||||
"workplan": "KEY-WP-0039",
|
||||
"from": "KEY-WP-0039-T02",
|
||||
"target": "KEY-WP-0039-T01",
|
||||
"relationship_type": "starts_after",
|
||||
"from_workplan_id": "09c3bddf-6058-5603-8488-cccdb7be4f86",
|
||||
"from_task_id": "03da4c38-1dac-53b1-8b0b-e697df48e9d3",
|
||||
"to_workplan_id": null,
|
||||
"to_task_id": "4d06b059-3768-53bb-83a4-a9a71c0850b8"
|
||||
},
|
||||
{
|
||||
"workplan": "KEY-WP-0039",
|
||||
"from": "KEY-WP-0039-T02",
|
||||
"target": "KEY-WP-0038-T02",
|
||||
"relationship_type": "starts_after",
|
||||
"from_workplan_id": "09c3bddf-6058-5603-8488-cccdb7be4f86",
|
||||
"from_task_id": "03da4c38-1dac-53b1-8b0b-e697df48e9d3",
|
||||
"to_workplan_id": null,
|
||||
"to_task_id": "ddcfb482-8f33-5c81-9ce8-84be8fa5dc30"
|
||||
},
|
||||
{
|
||||
"workplan": "KEY-WP-0007",
|
||||
"from": "KEY-WP-0007",
|
||||
"target": "KEY-WP-0005",
|
||||
"relationship_type": "blocks",
|
||||
"from_workplan_id": "24db4178-705c-5b09-b7db-ee7a345d71a0",
|
||||
"from_task_id": null,
|
||||
"to_workplan_id": "711a8303-69aa-5db6-9043-92530c6dc81b",
|
||||
"to_task_id": null
|
||||
},
|
||||
{
|
||||
"workplan": "KEY-WP-0009",
|
||||
"from": "KEY-WP-0009",
|
||||
"target": "KEY-WP-0006",
|
||||
"relationship_type": "blocks",
|
||||
"from_workplan_id": "c1a9b1cc-2ff0-566a-b544-1a2ef967fc0d",
|
||||
"from_task_id": null,
|
||||
"to_workplan_id": "d8d20d8f-61e0-54e2-9a88-a3958f0b6f2d",
|
||||
"to_task_id": null
|
||||
}
|
||||
],
|
||||
"refused": [],
|
||||
"notes": []
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
from pathlib import Path
|
||||
import hashlib,json,subprocess,urllib.request,urllib.error,yaml,re
|
||||
root=Path('/home/worsch/key-cape');out=Path('/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/key39-finalization-blocking-reason-r1');base='http://127.0.0.1:8000';head='d803e9282a47b49daf930fb905312fa5104c0ace'
|
||||
def git(*args):return subprocess.check_output(['git','-C',str(root),*args]).decode()
|
||||
assert git('rev-parse','HEAD').strip()==head
|
||||
assert git('ls-remote','origin','refs/heads/main').split()[0]==head
|
||||
assert not git('status','--porcelain=v1')
|
||||
def get(path):
|
||||
with urllib.request.urlopen(base+path,timeout=30) as response:return json.load(response)
|
||||
health=get('/state/health');assert (health['instance_role'],health['instance_label'])==('primary','railiance01')
|
||||
request_path='/repos/key-cape/work-record-projection/reconcile';body={'expected_commit':head,'acknowledge_retirements':False}
|
||||
digest=hashlib.sha256(json.dumps({'method':'POST','path':request_path,'body':body},sort_keys=True,separators=(',',':')).encode()).hexdigest()
|
||||
request=urllib.request.Request(base+request_path,data=json.dumps(body).encode(),method='POST',headers={'Content-Type':'application/json','Idempotency-Key':'rmgr-projection:v2:'+digest,'X-StateHub-Source-Agent':'custodian-codex'})
|
||||
try:
|
||||
with urllib.request.urlopen(request,timeout=120) as response:receipt=json.load(response)
|
||||
except urllib.error.HTTPError as error:
|
||||
raw=error.read();(out/'canonical-receiver-refusal.json').write_bytes(raw);raise
|
||||
(out/'canonical-receiver.json').write_text(json.dumps(receipt,indent=2)+'\n')
|
||||
assert receipt['expected_commit']==receipt['derived_commit']==head
|
||||
assert (receipt['instance_role'],receipt['instance_label'])==('primary','railiance01')
|
||||
assert receipt['outcome']['status'] in ('applied','noop'),receipt['outcome']['status']
|
||||
snapshot=get('/repos/key-cape/work-record-projection/snapshot');baseline=json.loads((out/'baseline.json').read_text())
|
||||
planid=baseline['frontmatter']['state_hub_workstream_id'];plan=next(w for w in snapshot['workplans'] if w['id']==planid)
|
||||
rows=[]
|
||||
for block in baseline['task_blocks']:
|
||||
task=next(t for t in snapshot['tasks'] if t['id']==block['state_hub_task_id'])
|
||||
assert task['status']==block['status'];rows.append({'record_id':block['id'],'state_hub_task_id':task['id'],'status':task['status'],'workplan_id':task.get('workplan_id'),'depends_on_source':block.get('depends_on',[])})
|
||||
current=(root/baseline['path']).read_text();assert current==(out/'KEY39.proposed.md').read_text()
|
||||
actual_blocks=[yaml.safe_load(t) for t in re.findall(r'^```task\n(.*?)^```',current,re.M|re.S)]
|
||||
assert actual_blocks==baseline['task_blocks_after']
|
||||
for before,after in zip(baseline['task_blocks'],actual_blocks):
|
||||
expected=dict(before)
|
||||
if before['id']=='KEY-WP-0039-T02':expected['blocking_reason']=baseline['only_task_field_delta']['new']
|
||||
assert after==expected
|
||||
task_after=get('/tasks/03da4c38-1dac-53b1-8b0b-e697df48e9d3')
|
||||
assert task_after.get('needs_human') is False
|
||||
assert task_after.get('blocking_reason')==baseline['only_task_field_delta']['new']
|
||||
verification={'commit':head,'receiver_status':receipt['outcome']['status'],'acknowledge_retirements':False,'instance_role':receipt['instance_role'],'instance_label':receipt['instance_label'],'workplan':{'id':planid,'status':plan['status'],'record_id':'KEY-WP-0039'},'task_bindings':rows,'all_other_task_fields_unchanged':True,'only_task_field_delta':baseline['only_task_field_delta'],'needs_human':False,'primary_task_blocking_reason':task_after['blocking_reason'],'frontmatter_unchanged':True,'only_committed_path':baseline['path'],'remote_forge_matches':True,'owner_worktree_and_index_clean':not git('status','--porcelain=v1'),'normal_sync_refusal':'unrelated grandfathered ADHOC-2026-09-27-T02 UUID; not modified','native_acceptance_inferred':False}
|
||||
assert plan['status']==baseline['frontmatter']['status']
|
||||
(out/'final-verification.json').write_text(json.dumps(verification,indent=2)+'\n');print(json.dumps(verification,indent=2))
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"commit": "d803e9282a47b49daf930fb905312fa5104c0ace",
|
||||
"parent": "26ec9bb9b195ad377448570d6b161fd682311a0b",
|
||||
"paths": [
|
||||
"workplans/KEY-WP-0039-openbao-negative-probe-tokens.md"
|
||||
],
|
||||
"normal_hook": "scanner8.30.1 clean",
|
||||
"only_task_field_delta": "KEY-WP-0039-T02 blocking_reason suffix; all other fields unchanged",
|
||||
"native_or_credential_action": false,
|
||||
"needs_human": false,
|
||||
"needs_human_preserved": true
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
<!-- custodian-brief: generated by fix-consistency — do not edit manually -->
|
||||
# Custodian Brief — key-cape
|
||||
|
||||
**Domain:** infotech
|
||||
**Last synced:** 2026-10-17 02:16 UTC
|
||||
**State Hub:** http://127.0.0.1:8000 *(adjust if running on a remote machine)*
|
||||
|
||||
## Active Workstreams
|
||||
|
||||
### Negative-check tokens for the secrets-engine OpenBao JWT role
|
||||
Progress: 2/3 done | workplan_id: `09c3bddf-6058-5603-8488-cccdb7be4f86`
|
||||
|
||||
**Open tasks:**
|
||||
- ! T01 — Define the probe registrations `03da4c38`
|
||||
*(wait: Ordered identity gate: Forgejo first SecurityCase registration/claims proof, then NetKingdom registry session and attended human/coding-agent custody; registration authorization already resolved.)*
|
||||
|
||||
### Forgejo as a NetKingdom OIDC client, so the forge can close
|
||||
Progress: 2/4 done | workplan_id: `098e3e2c-a138-560e-b2d3-3a15eb49d8a9`
|
||||
|
||||
**Open tasks:**
|
||||
- ! T01 — Define what Forgejo receives `ddcfb482`
|
||||
*(wait: Registration runs as the first KnownSecurityCase of type oidc-browser-relying-party (NK-WP-0048-T06), not as a hand-built rollout.)*
|
||||
- ! T02 — Register the client and hand over its secret `40ddaa29`
|
||||
*(wait: Claims are verified against the registered client.)*
|
||||
|
||||
## Inbox Hygiene
|
||||
|
||||
**Stale unread:** 2 message(s) older than 3 day(s) — triage at session start.
|
||||
**Missing thread_id:** 2 unread message(s) lack supersession chains.
|
||||
|
||||
---
|
||||
## MCP Orientation (when available)
|
||||
|
||||
If the state-hub MCP server is reachable, call:
|
||||
`get_domain_summary("infotech")`
|
||||
This provides richer cross-domain context.
|
||||
If the MCP call fails, use this file as your orientation source.
|
||||
|
|
@ -0,0 +1,224 @@
|
|||
# Work Records — key-cape
|
||||
|
||||
> Generated by `statehub fix-consistency` (CUST-WP-0061-T04, work-record
|
||||
> stage 3). Do not edit by hand — edit the source file/block listed for
|
||||
> each record and re-run fix-consistency to refresh this index. Archived
|
||||
> workplans are omitted; closed decisions/intakes/engagements stay listed
|
||||
> so recently-resolved work is still visible. [auto]
|
||||
|
||||
| Kind | ID | Status | Lane | Source |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| workplan | ADHOC-2026-09-05 | finished | — | workplans/ADHOC-2026-09-05.md |
|
||||
| workplan | ADHOC-2026-09-07 | finished | — | workplans/ADHOC-2026-09-07.md |
|
||||
| workplan | ADHOC-2026-09-27 | finished | — | workplans/ADHOC-2026-09-27.md |
|
||||
| workplan | KEY-WP-0001 | finished | — | workplans/KEY-WP-0001-keycape-implementation.md |
|
||||
| workplan | KEY-WP-0002 | finished | — | workplans/KEY-WP-0002-container-image-gitea.md |
|
||||
| workplan | KEY-WP-0003 | finished | — | workplans/KEY-WP-0003-bootstrap-console-oidc-mfa-login.md |
|
||||
| workplan | KEY-WP-0004 | finished | — | workplans/KEY-WP-0004-binky-hedgehog-tenant-onboarding.md |
|
||||
| workplan | KEY-WP-0005 | finished | — | workplans/KEY-WP-0005-iam-profile-core-claims.md |
|
||||
| workplan | KEY-WP-0006 | finished | — | workplans/KEY-WP-0006-client-credentials-service-tokens.md |
|
||||
| workplan | KEY-WP-0007 | finished | — | workplans/KEY-WP-0007-user-engine-portal-oidc-client.md |
|
||||
| workplan | KEY-WP-0008 | finished | — | workplans/KEY-WP-0008-registration-handoff-and-client-mfa-policy.md |
|
||||
| workplan | KEY-WP-0009 | finished | — | workplans/KEY-WP-0009-provider-capabilities-and-service-identities.md |
|
||||
| workplan | KEY-WP-0010 | finished | — | workplans/KEY-WP-0010-openbao-operator-loopback-callback.md |
|
||||
| workplan | KEY-WP-0011 | finished | — | workplans/KEY-WP-0011-live-secret-exposure-recovery.md |
|
||||
| workplan | KEY-WP-0012 | finished | — | workplans/KEY-WP-0012-userinfo-canonical-subject-resolution.md |
|
||||
| workplan | KEY-WP-0013 | finished | — | workplans/KEY-WP-0013-approval-engine-resource-audience.md |
|
||||
| workplan | KEY-WP-0014 | finished | — | workplans/KEY-WP-0014-native-credential-lane-handoff.md |
|
||||
| workplan | KEY-WP-0015 | finished | — | workplans/KEY-WP-0015-scope-intent-assessment.md |
|
||||
| workplan | KEY-WP-0016 | finished | — | workplans/KEY-WP-0016-authorization-code-protocol-hardening.md |
|
||||
| workplan | KEY-WP-0017 | finished | — | workplans/KEY-WP-0017-canonical-model-and-discovery-conformance.md |
|
||||
| workplan | KEY-WP-0018 | finished | — | workplans/KEY-WP-0018-export-completeness-evidence.md |
|
||||
| workplan | KEY-WP-0019 | finished | — | workplans/KEY-WP-0019-upstream-provider-token-verification.md |
|
||||
| workplan | KEY-WP-0020 | finished | — | workplans/KEY-WP-0020-migration-contract-preservation.md |
|
||||
| workplan | KEY-WP-0021 | finished | — | workplans/KEY-WP-0021-snapshot-attribute-validation.md |
|
||||
| workplan | KEY-WP-0022 | finished | — | workplans/KEY-WP-0022-replacement-harness-and-external-conformance.md |
|
||||
| workplan | KEY-WP-0023 | finished | — | workplans/KEY-WP-0023-live-migration-proof.md |
|
||||
| workplan | KEY-WP-0024 | finished | — | workplans/KEY-WP-0024-tenant-roles-opt-in-wiring.md |
|
||||
| workplan | KEY-WP-0025 | finished | — | workplans/KEY-WP-0025-runtime-lifecycle-and-readiness.md |
|
||||
| workplan | KEY-WP-0026 | finished | — | workplans/KEY-WP-0026-packaging-bootstrap-and-credential-handling.md |
|
||||
| workplan | KEY-WP-0027 | finished | — | workplans/KEY-WP-0027-rollout-readiness-and-live-state.md |
|
||||
| workplan | KEY-WP-0028 | finished | — | workplans/KEY-WP-0028-browser-client-field-validation.md |
|
||||
| workplan | KEY-WP-0029 | finished | — | workplans/KEY-WP-0029-scope-reassessment.md |
|
||||
| workplan | KEY-WP-0030 | finished | — | workplans/KEY-WP-0030-tenant-precondition-guard.md |
|
||||
| workplan | KEY-WP-0031 | finished | — | workplans/KEY-WP-0031-gate-house-tenant-ruling.md |
|
||||
| workplan | KEY-WP-0032 | finished | — | workplans/KEY-WP-0032-principal-type-provenance-guard.md |
|
||||
| workplan | KEY-WP-0033 | finished | — | workplans/KEY-WP-0033-vergabe-fresh-login.md |
|
||||
| workplan | KEY-WP-0034 | finished | — | workplans/KEY-WP-0034-account-recovery.md |
|
||||
| workplan | KEY-WP-0035 | finished | — | workplans/KEY-WP-0035-optional-mfa.md |
|
||||
| workplan | KEY-WP-0036 | finished | — | workplans/KEY-WP-0036-platform-factor-recovery.md |
|
||||
| workplan | KEY-WP-0037 | finished | — | workplans/KEY-WP-0037-secrets-engine-openbao-registration-custody.md |
|
||||
| workplan | KEY-WP-0038 | active | — | workplans/KEY-WP-0038-forgejo-oidc-client.md |
|
||||
| workplan | KEY-WP-0039 | active | — | workplans/KEY-WP-0039-openbao-negative-probe-tokens.md |
|
||||
| workplan | KEY-WP-0040 | finished | — | workplans/KEY-WP-0040-mfa-attended-run-findings.md |
|
||||
| workplan | KEY-WP-0041 | finished | — | workplans/KEY-WP-0041-sign-in-diagnosis-page.md |
|
||||
| task | ADHOC-2026-09-05-T01 | done | — | workplans/ADHOC-2026-09-05.md |
|
||||
| task | ADHOC-2026-09-07-T01 | done | — | workplans/ADHOC-2026-09-07.md |
|
||||
| task | ADHOC-2026-09-27-T01 | done | — | workplans/ADHOC-2026-09-27.md |
|
||||
| task | ADHOC-2026-09-27-T02 | done | — | workplans/ADHOC-2026-09-27.md |
|
||||
| task | KEY-WP-0001-T01 | done | — | workplans/KEY-WP-0001-keycape-implementation.md |
|
||||
| task | KEY-WP-0001-T02 | done | — | workplans/KEY-WP-0001-keycape-implementation.md |
|
||||
| task | KEY-WP-0001-T03 | done | — | workplans/KEY-WP-0001-keycape-implementation.md |
|
||||
| task | KEY-WP-0001-T04 | done | — | workplans/KEY-WP-0001-keycape-implementation.md |
|
||||
| task | KEY-WP-0001-T05 | done | — | workplans/KEY-WP-0001-keycape-implementation.md |
|
||||
| task | KEY-WP-0001-T06 | done | — | workplans/KEY-WP-0001-keycape-implementation.md |
|
||||
| task | KEY-WP-0001-T07 | done | — | workplans/KEY-WP-0001-keycape-implementation.md |
|
||||
| task | KEY-WP-0001-T08 | done | — | workplans/KEY-WP-0001-keycape-implementation.md |
|
||||
| task | KEY-WP-0001-T09 | done | — | workplans/KEY-WP-0001-keycape-implementation.md |
|
||||
| task | KEY-WP-0001-T10 | done | — | workplans/KEY-WP-0001-keycape-implementation.md |
|
||||
| task | KEY-WP-0001-T11 | done | — | workplans/KEY-WP-0001-keycape-implementation.md |
|
||||
| task | KEY-WP-0001-T12 | done | — | workplans/KEY-WP-0001-keycape-implementation.md |
|
||||
| task | KEY-WP-0001-T13 | done | — | workplans/KEY-WP-0001-keycape-implementation.md |
|
||||
| task | KEY-WP-0001-T14 | done | — | workplans/KEY-WP-0001-keycape-implementation.md |
|
||||
| task | KEY-WP-0001-T15 | done | — | workplans/KEY-WP-0001-keycape-implementation.md |
|
||||
| task | KEY-WP-0001-T16 | done | — | workplans/KEY-WP-0001-keycape-implementation.md |
|
||||
| task | KEY-WP-0001-T17 | done | — | workplans/KEY-WP-0001-keycape-implementation.md |
|
||||
| task | KEY-WP-0001-T18 | done | — | workplans/KEY-WP-0001-keycape-implementation.md |
|
||||
| task | KEY-WP-0001-T19 | done | — | workplans/KEY-WP-0001-keycape-implementation.md |
|
||||
| task | KEY-WP-0001-T20 | done | — | workplans/KEY-WP-0001-keycape-implementation.md |
|
||||
| task | KEY-WP-0001-T21 | done | — | workplans/KEY-WP-0001-keycape-implementation.md |
|
||||
| task | KEY-WP-0001-T22 | done | — | workplans/KEY-WP-0001-keycape-implementation.md |
|
||||
| task | KEY-WP-0001-T23 | done | — | workplans/KEY-WP-0001-keycape-implementation.md |
|
||||
| task | KEY-WP-0002-T01 | done | — | workplans/KEY-WP-0002-container-image-gitea.md |
|
||||
| task | KEY-WP-0002-T02 | done | — | workplans/KEY-WP-0002-container-image-gitea.md |
|
||||
| task | KEY-WP-0002-T03 | done | — | workplans/KEY-WP-0002-container-image-gitea.md |
|
||||
| task | KEY-WP-0002-T04 | done | — | workplans/KEY-WP-0002-container-image-gitea.md |
|
||||
| task | KEY-WP-0002-T05 | done | — | workplans/KEY-WP-0002-container-image-gitea.md |
|
||||
| task | KEY-WP-0002-T06 | done | — | workplans/KEY-WP-0002-container-image-gitea.md |
|
||||
| task | KEY-WP-0003-T01 | done | — | workplans/KEY-WP-0003-bootstrap-console-oidc-mfa-login.md |
|
||||
| task | KEY-WP-0003-T02 | done | — | workplans/KEY-WP-0003-bootstrap-console-oidc-mfa-login.md |
|
||||
| task | KEY-WP-0003-T03 | done | — | workplans/KEY-WP-0003-bootstrap-console-oidc-mfa-login.md |
|
||||
| task | KEY-WP-0003-T04 | done | — | workplans/KEY-WP-0003-bootstrap-console-oidc-mfa-login.md |
|
||||
| task | KEY-WP-0003-T05 | done | — | workplans/KEY-WP-0003-bootstrap-console-oidc-mfa-login.md |
|
||||
| task | KEY-WP-0004-T01 | done | — | workplans/KEY-WP-0004-binky-hedgehog-tenant-onboarding.md |
|
||||
| task | KEY-WP-0004-T02 | done | — | workplans/KEY-WP-0004-binky-hedgehog-tenant-onboarding.md |
|
||||
| task | KEY-WP-0004-T03 | done | — | workplans/KEY-WP-0004-binky-hedgehog-tenant-onboarding.md |
|
||||
| task | KEY-WP-0004-T04 | done | — | workplans/KEY-WP-0004-binky-hedgehog-tenant-onboarding.md |
|
||||
| task | KEY-WP-0004-T05 | done | — | workplans/KEY-WP-0004-binky-hedgehog-tenant-onboarding.md |
|
||||
| task | KEY-WP-0004-T06 | cancel | — | workplans/KEY-WP-0004-binky-hedgehog-tenant-onboarding.md |
|
||||
| task | KEY-WP-0004-T07 | done | — | workplans/KEY-WP-0004-binky-hedgehog-tenant-onboarding.md |
|
||||
| task | KEY-WP-0005-T01 | done | — | workplans/KEY-WP-0005-iam-profile-core-claims.md |
|
||||
| task | KEY-WP-0005-T02 | done | — | workplans/KEY-WP-0005-iam-profile-core-claims.md |
|
||||
| task | KEY-WP-0005-T03 | done | — | workplans/KEY-WP-0005-iam-profile-core-claims.md |
|
||||
| task | KEY-WP-0006-T01 | done | — | workplans/KEY-WP-0006-client-credentials-service-tokens.md |
|
||||
| task | KEY-WP-0006-T02 | done | — | workplans/KEY-WP-0006-client-credentials-service-tokens.md |
|
||||
| task | KEY-WP-0006-T03 | done | — | workplans/KEY-WP-0006-client-credentials-service-tokens.md |
|
||||
| task | KEY-WP-0006-T04 | done | — | workplans/KEY-WP-0006-client-credentials-service-tokens.md |
|
||||
| task | KEY-WP-0006-T05 | done | — | workplans/KEY-WP-0006-client-credentials-service-tokens.md |
|
||||
| task | KEY-WP-0007-T01 | done | — | workplans/KEY-WP-0007-user-engine-portal-oidc-client.md |
|
||||
| task | KEY-WP-0007-T02 | done | — | workplans/KEY-WP-0007-user-engine-portal-oidc-client.md |
|
||||
| task | KEY-WP-0007-T03 | done | — | workplans/KEY-WP-0007-user-engine-portal-oidc-client.md |
|
||||
| task | KEY-WP-0008-T01 | done | — | workplans/KEY-WP-0008-registration-handoff-and-client-mfa-policy.md |
|
||||
| task | KEY-WP-0008-T02 | done | — | workplans/KEY-WP-0008-registration-handoff-and-client-mfa-policy.md |
|
||||
| task | KEY-WP-0008-T03 | done | — | workplans/KEY-WP-0008-registration-handoff-and-client-mfa-policy.md |
|
||||
| task | KEY-WP-0008-T04 | done | — | workplans/KEY-WP-0008-registration-handoff-and-client-mfa-policy.md |
|
||||
| task | KEY-WP-0009-T01 | done | — | workplans/KEY-WP-0009-provider-capabilities-and-service-identities.md |
|
||||
| task | KEY-WP-0009-T02 | done | — | workplans/KEY-WP-0009-provider-capabilities-and-service-identities.md |
|
||||
| task | KEY-WP-0009-T03 | done | — | workplans/KEY-WP-0009-provider-capabilities-and-service-identities.md |
|
||||
| task | KEY-WP-0009-T04 | done | — | workplans/KEY-WP-0009-provider-capabilities-and-service-identities.md |
|
||||
| task | KEY-WP-0010-T01 | done | — | workplans/KEY-WP-0010-openbao-operator-loopback-callback.md |
|
||||
| task | KEY-WP-0011-T01 | done | — | workplans/KEY-WP-0011-live-secret-exposure-recovery.md |
|
||||
| task | KEY-WP-0011-T02 | done | — | workplans/KEY-WP-0011-live-secret-exposure-recovery.md |
|
||||
| task | KEY-WP-0011-T03 | done | — | workplans/KEY-WP-0011-live-secret-exposure-recovery.md |
|
||||
| task | KEY-WP-0012-T01 | done | — | workplans/KEY-WP-0012-userinfo-canonical-subject-resolution.md |
|
||||
| task | KEY-WP-0012-T02 | done | — | workplans/KEY-WP-0012-userinfo-canonical-subject-resolution.md |
|
||||
| task | KEY-WP-0012-T03 | done | — | workplans/KEY-WP-0012-userinfo-canonical-subject-resolution.md |
|
||||
| task | KEY-WP-0013-T01 | done | — | workplans/KEY-WP-0013-approval-engine-resource-audience.md |
|
||||
| task | KEY-WP-0013-T02 | done | — | workplans/KEY-WP-0013-approval-engine-resource-audience.md |
|
||||
| task | KEY-WP-0013-T03 | done | — | workplans/KEY-WP-0013-approval-engine-resource-audience.md |
|
||||
| task | KEY-WP-0013-T04 | done | — | workplans/KEY-WP-0013-approval-engine-resource-audience.md |
|
||||
| task | KEY-WP-0013-T05 | done | — | workplans/KEY-WP-0013-approval-engine-resource-audience.md |
|
||||
| task | KEY-WP-0013-T06 | done | — | workplans/KEY-WP-0013-approval-engine-resource-audience.md |
|
||||
| task | KEY-WP-0013-T07 | done | — | workplans/KEY-WP-0013-approval-engine-resource-audience.md |
|
||||
| task | KEY-WP-0014-T01 | done | — | workplans/KEY-WP-0014-native-credential-lane-handoff.md |
|
||||
| task | KEY-WP-0014-T02 | done | — | workplans/KEY-WP-0014-native-credential-lane-handoff.md |
|
||||
| task | KEY-WP-0014-T03 | done | — | workplans/KEY-WP-0014-native-credential-lane-handoff.md |
|
||||
| task | KEY-WP-0014-T04 | done | — | workplans/KEY-WP-0014-native-credential-lane-handoff.md |
|
||||
| task | KEY-WP-0015-T01 | done | — | workplans/KEY-WP-0015-scope-intent-assessment.md |
|
||||
| task | KEY-WP-0016-T01 | done | — | workplans/KEY-WP-0016-authorization-code-protocol-hardening.md |
|
||||
| task | KEY-WP-0016-T02 | done | — | workplans/KEY-WP-0016-authorization-code-protocol-hardening.md |
|
||||
| task | KEY-WP-0016-T03 | done | — | workplans/KEY-WP-0016-authorization-code-protocol-hardening.md |
|
||||
| task | KEY-WP-0016-T04 | done | — | workplans/KEY-WP-0016-authorization-code-protocol-hardening.md |
|
||||
| task | KEY-WP-0017-T01 | done | — | workplans/KEY-WP-0017-canonical-model-and-discovery-conformance.md |
|
||||
| task | KEY-WP-0017-T02 | done | — | workplans/KEY-WP-0017-canonical-model-and-discovery-conformance.md |
|
||||
| task | KEY-WP-0017-T03 | done | — | workplans/KEY-WP-0017-canonical-model-and-discovery-conformance.md |
|
||||
| task | KEY-WP-0017-T04 | done | — | workplans/KEY-WP-0017-canonical-model-and-discovery-conformance.md |
|
||||
| task | KEY-WP-0018-T01 | done | — | workplans/KEY-WP-0018-export-completeness-evidence.md |
|
||||
| task | KEY-WP-0018-T02 | done | — | workplans/KEY-WP-0018-export-completeness-evidence.md |
|
||||
| task | KEY-WP-0018-T03 | done | — | workplans/KEY-WP-0018-export-completeness-evidence.md |
|
||||
| task | KEY-WP-0019-T01 | done | — | workplans/KEY-WP-0019-upstream-provider-token-verification.md |
|
||||
| task | KEY-WP-0019-T02 | done | — | workplans/KEY-WP-0019-upstream-provider-token-verification.md |
|
||||
| task | KEY-WP-0019-T03 | done | — | workplans/KEY-WP-0019-upstream-provider-token-verification.md |
|
||||
| task | KEY-WP-0019-T04 | done | — | workplans/KEY-WP-0019-upstream-provider-token-verification.md |
|
||||
| task | KEY-WP-0019-T05 | done | — | workplans/KEY-WP-0019-upstream-provider-token-verification.md |
|
||||
| task | KEY-WP-0020-T01 | done | — | workplans/KEY-WP-0020-migration-contract-preservation.md |
|
||||
| task | KEY-WP-0020-T02 | done | — | workplans/KEY-WP-0020-migration-contract-preservation.md |
|
||||
| task | KEY-WP-0020-T03 | done | — | workplans/KEY-WP-0020-migration-contract-preservation.md |
|
||||
| task | KEY-WP-0021-T01 | done | — | workplans/KEY-WP-0021-snapshot-attribute-validation.md |
|
||||
| task | KEY-WP-0021-T02 | done | — | workplans/KEY-WP-0021-snapshot-attribute-validation.md |
|
||||
| task | KEY-WP-0021-T03 | done | — | workplans/KEY-WP-0021-snapshot-attribute-validation.md |
|
||||
| task | KEY-WP-0022-T01 | done | — | workplans/KEY-WP-0022-replacement-harness-and-external-conformance.md |
|
||||
| task | KEY-WP-0022-T02 | done | — | workplans/KEY-WP-0022-replacement-harness-and-external-conformance.md |
|
||||
| task | KEY-WP-0022-T03 | done | — | workplans/KEY-WP-0022-replacement-harness-and-external-conformance.md |
|
||||
| task | KEY-WP-0022-T04 | done | — | workplans/KEY-WP-0022-replacement-harness-and-external-conformance.md |
|
||||
| task | KEY-WP-0022-T05 | done | — | workplans/KEY-WP-0022-replacement-harness-and-external-conformance.md |
|
||||
| task | KEY-WP-0023-T01 | done | — | workplans/KEY-WP-0023-live-migration-proof.md |
|
||||
| task | KEY-WP-0023-T02 | done | — | workplans/KEY-WP-0023-live-migration-proof.md |
|
||||
| task | KEY-WP-0023-T03 | done | — | workplans/KEY-WP-0023-live-migration-proof.md |
|
||||
| task | KEY-WP-0023-T04 | done | — | workplans/KEY-WP-0023-live-migration-proof.md |
|
||||
| task | KEY-WP-0023-T05 | done | — | workplans/KEY-WP-0023-live-migration-proof.md |
|
||||
| task | KEY-WP-0024-T01 | done | — | workplans/KEY-WP-0024-tenant-roles-opt-in-wiring.md |
|
||||
| task | KEY-WP-0024-T02 | done | — | workplans/KEY-WP-0024-tenant-roles-opt-in-wiring.md |
|
||||
| task | KEY-WP-0025-T01 | done | — | workplans/KEY-WP-0025-runtime-lifecycle-and-readiness.md |
|
||||
| task | KEY-WP-0025-T02 | done | — | workplans/KEY-WP-0025-runtime-lifecycle-and-readiness.md |
|
||||
| task | KEY-WP-0025-T03 | done | — | workplans/KEY-WP-0025-runtime-lifecycle-and-readiness.md |
|
||||
| task | KEY-WP-0025-T04 | done | — | workplans/KEY-WP-0025-runtime-lifecycle-and-readiness.md |
|
||||
| task | KEY-WP-0026-T01 | done | — | workplans/KEY-WP-0026-packaging-bootstrap-and-credential-handling.md |
|
||||
| task | KEY-WP-0026-T02 | done | — | workplans/KEY-WP-0026-packaging-bootstrap-and-credential-handling.md |
|
||||
| task | KEY-WP-0026-T03 | done | — | workplans/KEY-WP-0026-packaging-bootstrap-and-credential-handling.md |
|
||||
| task | KEY-WP-0026-T04 | done | — | workplans/KEY-WP-0026-packaging-bootstrap-and-credential-handling.md |
|
||||
| task | KEY-WP-0026-T05 | done | — | workplans/KEY-WP-0026-packaging-bootstrap-and-credential-handling.md |
|
||||
| task | KEY-WP-0026-T06 | done | — | workplans/KEY-WP-0026-packaging-bootstrap-and-credential-handling.md |
|
||||
| task | KEY-WP-0027-T01 | done | — | workplans/KEY-WP-0027-rollout-readiness-and-live-state.md |
|
||||
| task | KEY-WP-0027-T02 | done | — | workplans/KEY-WP-0027-rollout-readiness-and-live-state.md |
|
||||
| task | KEY-WP-0027-T03 | done | — | workplans/KEY-WP-0027-rollout-readiness-and-live-state.md |
|
||||
| task | KEY-WP-0027-T04 | done | — | workplans/KEY-WP-0027-rollout-readiness-and-live-state.md |
|
||||
| task | KEY-WP-0028-T01 | done | — | workplans/KEY-WP-0028-browser-client-field-validation.md |
|
||||
| task | KEY-WP-0028-T02 | done | — | workplans/KEY-WP-0028-browser-client-field-validation.md |
|
||||
| task | KEY-WP-0029-T01 | done | — | workplans/KEY-WP-0029-scope-reassessment.md |
|
||||
| task | KEY-WP-0029-T02 | done | — | workplans/KEY-WP-0029-scope-reassessment.md |
|
||||
| task | KEY-WP-0029-T03 | done | — | workplans/KEY-WP-0029-scope-reassessment.md |
|
||||
| task | KEY-WP-0030-T01 | done | — | workplans/KEY-WP-0030-tenant-precondition-guard.md |
|
||||
| task | KEY-WP-0030-T02 | done | — | workplans/KEY-WP-0030-tenant-precondition-guard.md |
|
||||
| task | KEY-WP-0031-T01 | done | — | workplans/KEY-WP-0031-gate-house-tenant-ruling.md |
|
||||
| task | KEY-WP-0031-T02 | done | — | workplans/KEY-WP-0031-gate-house-tenant-ruling.md |
|
||||
| task | KEY-WP-0032-T01 | done | — | workplans/KEY-WP-0032-principal-type-provenance-guard.md |
|
||||
| task | KEY-WP-0032-T02 | done | — | workplans/KEY-WP-0032-principal-type-provenance-guard.md |
|
||||
| task | KEY-WP-0033-T01 | done | — | workplans/KEY-WP-0033-vergabe-fresh-login.md |
|
||||
| task | KEY-WP-0033-T02 | done | — | workplans/KEY-WP-0033-vergabe-fresh-login.md |
|
||||
| task | KEY-WP-0034-T01 | done | — | workplans/KEY-WP-0034-account-recovery.md |
|
||||
| task | KEY-WP-0034-T02 | done | — | workplans/KEY-WP-0034-account-recovery.md |
|
||||
| task | KEY-WP-0035-T01 | done | — | workplans/KEY-WP-0035-optional-mfa.md |
|
||||
| task | KEY-WP-0035-T02 | done | — | workplans/KEY-WP-0035-optional-mfa.md |
|
||||
| task | KEY-WP-0035-T03 | done | — | workplans/KEY-WP-0035-optional-mfa.md |
|
||||
| task | KEY-WP-0035-T04 | done | — | workplans/KEY-WP-0035-optional-mfa.md |
|
||||
| task | KEY-WP-0035-T05 | done | — | workplans/KEY-WP-0035-optional-mfa.md |
|
||||
| task | KEY-WP-0036-T01 | done | — | workplans/KEY-WP-0036-platform-factor-recovery.md |
|
||||
| task | KEY-WP-0036-T02 | cancel | — | workplans/KEY-WP-0036-platform-factor-recovery.md |
|
||||
| task | KEY-WP-0036-T03 | done | — | workplans/KEY-WP-0036-platform-factor-recovery.md |
|
||||
| task | KEY-WP-0037-T01 | done | — | workplans/KEY-WP-0037-secrets-engine-openbao-registration-custody.md |
|
||||
| task | KEY-WP-0037-T02 | done | — | workplans/KEY-WP-0037-secrets-engine-openbao-registration-custody.md |
|
||||
| task | KEY-WP-0037-T03 | done | — | workplans/KEY-WP-0037-secrets-engine-openbao-registration-custody.md |
|
||||
| task | KEY-WP-0037-T04 | done | — | workplans/KEY-WP-0037-secrets-engine-openbao-registration-custody.md |
|
||||
| task | KEY-WP-0038-T01 | done | — | workplans/KEY-WP-0038-forgejo-oidc-client.md |
|
||||
| task | KEY-WP-0038-T02 | wait | — | workplans/KEY-WP-0038-forgejo-oidc-client.md |
|
||||
| task | KEY-WP-0038-T03 | wait | — | workplans/KEY-WP-0038-forgejo-oidc-client.md |
|
||||
| task | KEY-WP-0038-T04 | cancel | — | workplans/KEY-WP-0038-forgejo-oidc-client.md |
|
||||
| task | KEY-WP-0039-T01 | done | — | workplans/KEY-WP-0039-openbao-negative-probe-tokens.md |
|
||||
| task | KEY-WP-0039-T02 | wait | — | workplans/KEY-WP-0039-openbao-negative-probe-tokens.md |
|
||||
| task | KEY-WP-0039-T03 | done | — | workplans/KEY-WP-0039-openbao-negative-probe-tokens.md |
|
||||
| task | KEY-WP-0040-T01 | done | — | workplans/KEY-WP-0040-mfa-attended-run-findings.md |
|
||||
| task | KEY-WP-0040-T02 | done | — | workplans/KEY-WP-0040-mfa-attended-run-findings.md |
|
||||
| task | KEY-WP-0041-T01 | done | — | workplans/KEY-WP-0041-sign-in-diagnosis-page.md |
|
||||
| task | KEY-WP-0041-T02 | done | — | workplans/KEY-WP-0041-sign-in-diagnosis-page.md |
|
||||
| intake | KEY-IN-0001 | closed | — | intakes/intakes.md |
|
||||
|
|
@ -0,0 +1,232 @@
|
|||
{
|
||||
".custodian-brief.md": {
|
||||
"sha256": "96d84539b3a92786bff37fcc70161adec4648b9547287e52e6b817bc080a080a",
|
||||
"mode": 436,
|
||||
"index": "100644 d3b36c3bffbeb922769a2a788e35b4113fccaa3f 0\t.custodian-brief.md\n"
|
||||
},
|
||||
"WORK-RECORDS.md": {
|
||||
"sha256": "431842547bd82750f284f8c1c9f519892f1bb1c1a684b9b1e38adc6fdfdc299d",
|
||||
"mode": 420,
|
||||
"index": "100644 d86b29bbc480acfa6d12d5052039578d7d4e380c 0\tWORK-RECORDS.md\n"
|
||||
},
|
||||
"workplans/ADHOC-2026-09-05.md": {
|
||||
"sha256": "76123d22d0c1efc51212f8914e386f078b3c6d908959af6978ae73874e492b04",
|
||||
"mode": 420,
|
||||
"index": "100644 f7e78675df62c9b48e29f882fa99298d6d8ce7f1 0\tworkplans/ADHOC-2026-09-05.md\n"
|
||||
},
|
||||
"workplans/ADHOC-2026-09-07.md": {
|
||||
"sha256": "c335ebfe91dad291277e8f17034ac8f785b3bc767f281f205c446c08f03d7978",
|
||||
"mode": 420,
|
||||
"index": "100644 17401073c053c9710e6e680243bbd313b08d1fc1 0\tworkplans/ADHOC-2026-09-07.md\n"
|
||||
},
|
||||
"workplans/ADHOC-2026-09-27.md": {
|
||||
"sha256": "2794d1940fc3c5e4a2df57de9d4b742fa6aa8602b67f1d3177c0d96bc5aaf314",
|
||||
"mode": 420,
|
||||
"index": "100644 0b755547aa7d5589f0898df2f102a45b6aa24838 0\tworkplans/ADHOC-2026-09-27.md\n"
|
||||
},
|
||||
"workplans/KEY-WP-0001-keycape-implementation.md": {
|
||||
"sha256": "29a2bf4892b7185e28e9126a3d8311d0fed34a876f416a3e75c605915fde0969",
|
||||
"mode": 420,
|
||||
"index": "100644 74334411cd056e7c7253e84f1c4b40c42eb13de3 0\tworkplans/KEY-WP-0001-keycape-implementation.md\n"
|
||||
},
|
||||
"workplans/KEY-WP-0002-container-image-gitea.md": {
|
||||
"sha256": "76fee187185b30219bc257ccc9b587661df4489574a2bb57703a365a5d803cec",
|
||||
"mode": 420,
|
||||
"index": "100644 418f477c0ede13d7c003d744abf234347f5d5e31 0\tworkplans/KEY-WP-0002-container-image-gitea.md\n"
|
||||
},
|
||||
"workplans/KEY-WP-0003-bootstrap-console-oidc-mfa-login.md": {
|
||||
"sha256": "5e949c696e587540d305b682eb1f8390ec544bbec6a566be8d04e73fb1b254ca",
|
||||
"mode": 420,
|
||||
"index": "100644 9896c67d78f0efdd03325535eb41295250be3a19 0\tworkplans/KEY-WP-0003-bootstrap-console-oidc-mfa-login.md\n"
|
||||
},
|
||||
"workplans/KEY-WP-0004-binky-hedgehog-tenant-onboarding.md": {
|
||||
"sha256": "260ba7d3116db3bc5a72e5cd8b9984137e7e372c8128d21073614dbd948c10c2",
|
||||
"mode": 420,
|
||||
"index": "100644 a50e910558ee43073c92cb27865a1aceac990dd5 0\tworkplans/KEY-WP-0004-binky-hedgehog-tenant-onboarding.md\n"
|
||||
},
|
||||
"workplans/KEY-WP-0005-iam-profile-core-claims.md": {
|
||||
"sha256": "c6702b644cdd046c0d3bba8ba8837050826e4dc3d855bd73327ae94218ba5b57",
|
||||
"mode": 420,
|
||||
"index": "100644 648d2405699dcf25b3121df6c46d871597100d7b 0\tworkplans/KEY-WP-0005-iam-profile-core-claims.md\n"
|
||||
},
|
||||
"workplans/KEY-WP-0006-client-credentials-service-tokens.md": {
|
||||
"sha256": "b3227849978c95cad89e02afb74bf2217b6188ae65c5b0af6a21703d65477701",
|
||||
"mode": 420,
|
||||
"index": "100644 632c4c2cad39cbde55c2ae1f2ba5ecf2f445b1a0 0\tworkplans/KEY-WP-0006-client-credentials-service-tokens.md\n"
|
||||
},
|
||||
"workplans/KEY-WP-0007-user-engine-portal-oidc-client.md": {
|
||||
"sha256": "5408ff6dfeb58a8a7756b48fe4f54f7c7b98d7940cb9aaa5c7864f1247e58150",
|
||||
"mode": 420,
|
||||
"index": "100644 f1b04e1a61b398426c23d8d9ca655b3937d7bd10 0\tworkplans/KEY-WP-0007-user-engine-portal-oidc-client.md\n"
|
||||
},
|
||||
"workplans/KEY-WP-0008-registration-handoff-and-client-mfa-policy.md": {
|
||||
"sha256": "bfc9b2118f0712e92a86bbb0f14a2857fafc3a42a3c14c0e2535002aaa57a2f5",
|
||||
"mode": 420,
|
||||
"index": "100644 438ab5480de3bd66d75b6ec7e09639d1b93e3e5f 0\tworkplans/KEY-WP-0008-registration-handoff-and-client-mfa-policy.md\n"
|
||||
},
|
||||
"workplans/KEY-WP-0009-provider-capabilities-and-service-identities.md": {
|
||||
"sha256": "d1daac9815930104a392ea96c14922e34f947720a31d1b0ac45a770997e0f476",
|
||||
"mode": 420,
|
||||
"index": "100644 c9e585db65424c7c4e3b5adf37a68de2bdfbdc1c 0\tworkplans/KEY-WP-0009-provider-capabilities-and-service-identities.md\n"
|
||||
},
|
||||
"workplans/KEY-WP-0010-openbao-operator-loopback-callback.md": {
|
||||
"sha256": "41d28a16bef9313b776c5c6305316a9e7d4f5528b7fb7427b8384a0ed0ad35c4",
|
||||
"mode": 420,
|
||||
"index": "100644 81614eeb8c98205d6e5eb3201da10e36b76a21e9 0\tworkplans/KEY-WP-0010-openbao-operator-loopback-callback.md\n"
|
||||
},
|
||||
"workplans/KEY-WP-0011-live-secret-exposure-recovery.md": {
|
||||
"sha256": "3c5458180fe81a04e357f8db737e4287f79640b5ab02682bcadc595b73f846cd",
|
||||
"mode": 420,
|
||||
"index": "100644 f7b906f552f6e3279895a90afbe216b6e78ee5ff 0\tworkplans/KEY-WP-0011-live-secret-exposure-recovery.md\n"
|
||||
},
|
||||
"workplans/KEY-WP-0012-userinfo-canonical-subject-resolution.md": {
|
||||
"sha256": "68d34d75c914e7d2d8177000e800d6372cccda2b850c03ab85da42b90a66b91c",
|
||||
"mode": 420,
|
||||
"index": "100644 0f3deb626e5de9a9f898754ef81d3f2ee5282792 0\tworkplans/KEY-WP-0012-userinfo-canonical-subject-resolution.md\n"
|
||||
},
|
||||
"workplans/KEY-WP-0013-approval-engine-resource-audience.md": {
|
||||
"sha256": "55c88bf4ed9a456db59b319bbaf960dfe6f4147437e339f950e0583d7582057a",
|
||||
"mode": 420,
|
||||
"index": "100644 7d77aee3caf3d79fe6d6c1369249c6d94e0ae123 0\tworkplans/KEY-WP-0013-approval-engine-resource-audience.md\n"
|
||||
},
|
||||
"workplans/KEY-WP-0014-native-credential-lane-handoff.md": {
|
||||
"sha256": "22aac43ae5039aebd11f9f0aa57a882ad009ad6ed119ec08eb85b32a5af48cd7",
|
||||
"mode": 420,
|
||||
"index": "100644 bacf04c07f59125a90c519d3c306a7414cb9a1a3 0\tworkplans/KEY-WP-0014-native-credential-lane-handoff.md\n"
|
||||
},
|
||||
"workplans/KEY-WP-0015-scope-intent-assessment.md": {
|
||||
"sha256": "60b9f2265eff880d916c3ee8dba36aec9984ec310804de776cebf89cc98fdb95",
|
||||
"mode": 420,
|
||||
"index": "100644 82b952a216346ffb7f5cab413623fe4a88f7700e 0\tworkplans/KEY-WP-0015-scope-intent-assessment.md\n"
|
||||
},
|
||||
"workplans/KEY-WP-0016-authorization-code-protocol-hardening.md": {
|
||||
"sha256": "ed48128a1cb51cbe3d154727bafd5a5ffb3b5b618c20649b2a0e7db154cf3a0c",
|
||||
"mode": 420,
|
||||
"index": "100644 52839bd0f83d97656c8ad97a8ffc338d9019239a 0\tworkplans/KEY-WP-0016-authorization-code-protocol-hardening.md\n"
|
||||
},
|
||||
"workplans/KEY-WP-0017-canonical-model-and-discovery-conformance.md": {
|
||||
"sha256": "8700e0e5aa07dcd7f651e792cca12a0330b7ee9d2f6064b4cc3fcf4c7d509340",
|
||||
"mode": 420,
|
||||
"index": "100644 00eb03971aae75724afbf7b8a4946ce6b7eda3af 0\tworkplans/KEY-WP-0017-canonical-model-and-discovery-conformance.md\n"
|
||||
},
|
||||
"workplans/KEY-WP-0018-export-completeness-evidence.md": {
|
||||
"sha256": "15026b708d116f803b0b2fa43f30a4dbced76e4d8137b5fcb5b4275e593d7c7b",
|
||||
"mode": 420,
|
||||
"index": "100644 325fc483d62dbad53b92b986de56112168a92b4a 0\tworkplans/KEY-WP-0018-export-completeness-evidence.md\n"
|
||||
},
|
||||
"workplans/KEY-WP-0019-upstream-provider-token-verification.md": {
|
||||
"sha256": "0d2d8b5bda1e0d333935812d8d3749cb7b0c5523a3987d1b26185f9f6d0f0fa2",
|
||||
"mode": 420,
|
||||
"index": "100644 dfeb92296f46d4ce2ac7df1fedcc61e9ccc5d6b3 0\tworkplans/KEY-WP-0019-upstream-provider-token-verification.md\n"
|
||||
},
|
||||
"workplans/KEY-WP-0020-migration-contract-preservation.md": {
|
||||
"sha256": "cc842d15bf42ddc4d0f4056bc19b3176cab2c5f3c1a2fe6d648e5dbd077be8b3",
|
||||
"mode": 420,
|
||||
"index": "100644 7b6f3be976293469dee2180c10e16f313876d640 0\tworkplans/KEY-WP-0020-migration-contract-preservation.md\n"
|
||||
},
|
||||
"workplans/KEY-WP-0021-snapshot-attribute-validation.md": {
|
||||
"sha256": "b603248693073a9b9b25b4d0a4147d8d88c16a07dc9936e48fa1f3c76fe8bab6",
|
||||
"mode": 420,
|
||||
"index": "100644 1f6efeb32a6eb8fcbc52f4d3b591e2fc6b9e006d 0\tworkplans/KEY-WP-0021-snapshot-attribute-validation.md\n"
|
||||
},
|
||||
"workplans/KEY-WP-0022-replacement-harness-and-external-conformance.md": {
|
||||
"sha256": "8c135e84be81de31b4949f52ac28dccd404f2e4c3eaa576d733bd50160427a53",
|
||||
"mode": 420,
|
||||
"index": "100644 7c69629ecd20d0158aa1332d9d1442b796f6a7af 0\tworkplans/KEY-WP-0022-replacement-harness-and-external-conformance.md\n"
|
||||
},
|
||||
"workplans/KEY-WP-0023-live-migration-proof.md": {
|
||||
"sha256": "e581cd11f3905805b5e8ba8e2ede85092577c7d086e1761f2a2d033fdead4e47",
|
||||
"mode": 420,
|
||||
"index": "100644 3e7c83cb12e7fc31e682cf3c27f4c4767ded2905 0\tworkplans/KEY-WP-0023-live-migration-proof.md\n"
|
||||
},
|
||||
"workplans/KEY-WP-0024-tenant-roles-opt-in-wiring.md": {
|
||||
"sha256": "af7842fa97263bfd46a4b5ae84c5c1ecddb06ab9623586e0292dcc93099fea35",
|
||||
"mode": 420,
|
||||
"index": "100644 baa0bbd195a92708f2befd027b938fcd1812ced7 0\tworkplans/KEY-WP-0024-tenant-roles-opt-in-wiring.md\n"
|
||||
},
|
||||
"workplans/KEY-WP-0025-runtime-lifecycle-and-readiness.md": {
|
||||
"sha256": "0addb34e6d85e376c6a5db7afba597b6f268afa98b86c8c60add9be174dd1b26",
|
||||
"mode": 420,
|
||||
"index": "100644 a53e646761178748e8c4b00dcde888698447a948 0\tworkplans/KEY-WP-0025-runtime-lifecycle-and-readiness.md\n"
|
||||
},
|
||||
"workplans/KEY-WP-0026-packaging-bootstrap-and-credential-handling.md": {
|
||||
"sha256": "c1bc0de47511ae49102009a172c84d9d5d64e829a18c8d40564fe817935563b3",
|
||||
"mode": 420,
|
||||
"index": "100644 1f96a2ac3ac939c4e6e0f66172477cf73db04e51 0\tworkplans/KEY-WP-0026-packaging-bootstrap-and-credential-handling.md\n"
|
||||
},
|
||||
"workplans/KEY-WP-0027-rollout-readiness-and-live-state.md": {
|
||||
"sha256": "f559eae61d433d451f80ea575254bb14a47f2485267b412e42d524b434bb1b59",
|
||||
"mode": 420,
|
||||
"index": "100644 57e01b2db033847cc786504b8795e37112d988a7 0\tworkplans/KEY-WP-0027-rollout-readiness-and-live-state.md\n"
|
||||
},
|
||||
"workplans/KEY-WP-0028-browser-client-field-validation.md": {
|
||||
"sha256": "fbd51422fa5e6449000e4520e6639f6135c7393165a5ae02b1e92892edba0480",
|
||||
"mode": 420,
|
||||
"index": "100644 46b0672fea90cd7cb672f7d119706c240474f8ca 0\tworkplans/KEY-WP-0028-browser-client-field-validation.md\n"
|
||||
},
|
||||
"workplans/KEY-WP-0029-scope-reassessment.md": {
|
||||
"sha256": "3241f3b94db9cad5e3770cf1fedc5c211288357e4f69b5ba9da57065094bdf0e",
|
||||
"mode": 420,
|
||||
"index": "100644 392f97750f6cb8b12868a6fd016c7d396b58477e 0\tworkplans/KEY-WP-0029-scope-reassessment.md\n"
|
||||
},
|
||||
"workplans/KEY-WP-0030-tenant-precondition-guard.md": {
|
||||
"sha256": "c94c3afc7fb4270dfc18f3fe8a32946c524e4a6ba2d02a3d8fda69c9b8441af0",
|
||||
"mode": 420,
|
||||
"index": "100644 0f74e0d5a037eec64b11b57c67b02057a3b0bfef 0\tworkplans/KEY-WP-0030-tenant-precondition-guard.md\n"
|
||||
},
|
||||
"workplans/KEY-WP-0031-gate-house-tenant-ruling.md": {
|
||||
"sha256": "0abb135367f0366aa071769c4bd76fdeb321d810ec75ad3b58d718734f78382b",
|
||||
"mode": 420,
|
||||
"index": "100644 d2ede9d494927976ccb9ba6a5aeaf1ed21c30837 0\tworkplans/KEY-WP-0031-gate-house-tenant-ruling.md\n"
|
||||
},
|
||||
"workplans/KEY-WP-0032-principal-type-provenance-guard.md": {
|
||||
"sha256": "6a5cd483a40b9477227b02156c42b3469094dac145b33727b336118898aa782a",
|
||||
"mode": 420,
|
||||
"index": "100644 a04f52f817a51efc1d0d0ecb99efacea8dc53a17 0\tworkplans/KEY-WP-0032-principal-type-provenance-guard.md\n"
|
||||
},
|
||||
"workplans/KEY-WP-0033-vergabe-fresh-login.md": {
|
||||
"sha256": "c01bb51cf2dcad06ea846040dddef09845041fa7271cd7015e5a34a49e60bb8f",
|
||||
"mode": 420,
|
||||
"index": "100644 5b478f953890339aec796c11a536af0ec3238789 0\tworkplans/KEY-WP-0033-vergabe-fresh-login.md\n"
|
||||
},
|
||||
"workplans/KEY-WP-0034-account-recovery.md": {
|
||||
"sha256": "7db9adcd418ccabfbce672aec283249a6e374212492f0349e8c47a568f130eda",
|
||||
"mode": 420,
|
||||
"index": "100644 eb58f99bc26e3c3ac33230dfe432c8b1fabe47ce 0\tworkplans/KEY-WP-0034-account-recovery.md\n"
|
||||
},
|
||||
"workplans/KEY-WP-0035-optional-mfa.md": {
|
||||
"sha256": "c60ea8d0c195686c0d2317f657aad2de27c9b9432d311627c0e1d06124cabb49",
|
||||
"mode": 420,
|
||||
"index": "100644 29eb711f656c91808e91f3981a9168c3a0ebc253 0\tworkplans/KEY-WP-0035-optional-mfa.md\n"
|
||||
},
|
||||
"workplans/KEY-WP-0036-platform-factor-recovery.md": {
|
||||
"sha256": "5ceb08fe5789b4f02550bb04fcaf8eeb25a43ac3f506dfc62d60a513bcd0013f",
|
||||
"mode": 420,
|
||||
"index": "100644 fd296bc05a6e565b0ef6e88a9ae3daa7c9f81786 0\tworkplans/KEY-WP-0036-platform-factor-recovery.md\n"
|
||||
},
|
||||
"workplans/KEY-WP-0037-secrets-engine-openbao-registration-custody.md": {
|
||||
"sha256": "3a90a0c460fc439570820b96d1ddc3b7f8646c557ca3a8aa27c6d546bb1a845a",
|
||||
"mode": 420,
|
||||
"index": "100644 ad7fed77a27aa8487693ad6fcf342502f418427a 0\tworkplans/KEY-WP-0037-secrets-engine-openbao-registration-custody.md\n"
|
||||
},
|
||||
"workplans/KEY-WP-0038-forgejo-oidc-client.md": {
|
||||
"sha256": "dc8d88d462799e7e6e44da759a973d52e44102a271969a5daaa9d30ec015b064",
|
||||
"mode": 420,
|
||||
"index": "100644 8cd5bc66f7b0115eec7813d6eec994a1bb618668 0\tworkplans/KEY-WP-0038-forgejo-oidc-client.md\n"
|
||||
},
|
||||
"workplans/KEY-WP-0039-openbao-negative-probe-tokens.md": {
|
||||
"sha256": "517f9526d574ce3faafad18837e0039db8f3f7990eac3e34a1678cdd7307a9e1",
|
||||
"mode": 420,
|
||||
"index": "100644 4398517afe04e3b0723722af37b434fa8d2209ab 0\tworkplans/KEY-WP-0039-openbao-negative-probe-tokens.md\n"
|
||||
},
|
||||
"workplans/KEY-WP-0040-mfa-attended-run-findings.md": {
|
||||
"sha256": "98b23d8810bff047067169232213cb313ecc14ff79751224842723afe808fbd2",
|
||||
"mode": 420,
|
||||
"index": "100644 8e8730c8e7733b13d6a9f42689a9d8e407769e81 0\tworkplans/KEY-WP-0040-mfa-attended-run-findings.md\n"
|
||||
},
|
||||
"workplans/KEY-WP-0041-sign-in-diagnosis-page.md": {
|
||||
"sha256": "7e44f46acd6b3942c1852b49e0d64b205dfd7d9bb378aed5241b1bf22d7ce88a",
|
||||
"mode": 420,
|
||||
"index": "100644 56fd05819e3acd37b8e6cb468ec3d62307f1ae7a 0\tworkplans/KEY-WP-0041-sign-in-diagnosis-page.md\n"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
---
|
||||
id: ADHOC-2026-09-05
|
||||
type: workplan
|
||||
title: "Repository review and layer declaration"
|
||||
domain: infotech
|
||||
repo: key-cape
|
||||
status: finished
|
||||
owner: codex
|
||||
created: "2026-09-05"
|
||||
updated: "2026-09-05"
|
||||
state_hub_workstream_id: "6f5a12f0-5dac-5e02-bbd9-9ab2263264ba"
|
||||
---
|
||||
|
||||
## Resolve declaration intake and normalize repository records
|
||||
|
||||
```task
|
||||
id: ADHOC-2026-09-05-T01
|
||||
status: done
|
||||
priority: low
|
||||
state_hub_task_id: "54c89abe-2539-550d-bc3a-649af8d4580a"
|
||||
```
|
||||
|
||||
Reviewed KEY-IN-0001 against local security-layer-model_v0.4. Declared Tooling
|
||||
and the authentication/authorization boundary in INTENT.md and closed the intake.
|
||||
Normalized two legacy completed workplan frontmatter statuses to finished.
|
||||
Ignored the generated .repo-manager workstation cache. Existing task statuses
|
||||
already conform to the State Hub task lifecycle canon.
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
---
|
||||
id: ADHOC-2026-09-07
|
||||
type: workplan
|
||||
title: "Document the KEY-WP-0016 authorization-code bindings for consumers"
|
||||
domain: infotech
|
||||
repo: key-cape
|
||||
status: finished
|
||||
owner: claude
|
||||
created: "2026-09-07"
|
||||
updated: "2026-09-07"
|
||||
state_hub_workstream_id: "c2fdc5cc-89be-561d-b832-f0b3019ea1c7"
|
||||
---
|
||||
|
||||
## Publish the consumer-facing rollout note
|
||||
|
||||
```task
|
||||
id: ADHOC-2026-09-07-T01
|
||||
status: done
|
||||
priority: medium
|
||||
state_hub_task_id: "2187cef2-07c0-5ac1-89ca-3bb25a160fa4"
|
||||
```
|
||||
|
||||
KEY-WP-0016 changed `/token` and `/userinfo` behaviour without a consumer-facing
|
||||
note; no document in `docs/` mentioned `redirect_uri` at all, so the change would
|
||||
have reached a deployment silently.
|
||||
|
||||
Published `docs/authorization-code-bindings.md` stating what an exchange must
|
||||
send, who is affected and how to roll out. Verified the blast radius rather than
|
||||
assuming it: all five browser registrations in `config/dev-config.yaml`
|
||||
(`openbao-admin`, `demo-app`, `netkingdom-bootstrap-console`,
|
||||
`user-engine-portal`, `coulomb-social`) are `clientType: public` with
|
||||
`grantTypes: ["authorization_code"]`, so the grant-type and confidential-client
|
||||
bindings cannot affect them and only the `redirect_uri` requirement can. Named
|
||||
the realistic failure — a client that sends `redirect_uri` to `/authorize` but
|
||||
omits it at `/token` — and said plainly that it has not been observed against a
|
||||
live consumer. Referenced the note from SCOPE.md.
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
---
|
||||
id: ADHOC-2026-09-27
|
||||
type: workplan
|
||||
title: "Fresh MFA no longer backdated to a stale kc_login cookie (INFD-IN-0005)"
|
||||
domain: infotech
|
||||
repo: key-cape
|
||||
status: finished
|
||||
owner: claude
|
||||
created: "2026-09-27"
|
||||
updated: "2026-09-27"
|
||||
state_hub_workstream_id: "5217dd06-73cb-50f8-ba64-528e421b29de"
|
||||
---
|
||||
|
||||
## Stop completeAuthorization from reusing a stale prior session's IssuedAt after real MFA
|
||||
|
||||
```task
|
||||
id: ADHOC-2026-09-27-T01
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "ce98e4af-cfaa-5620-8f21-15ade18ee9e8"
|
||||
```
|
||||
|
||||
`informed-decision` intake `INFD-IN-0005` (open, `origin_ref: SECRETS-WP-0010`):
|
||||
platform-root's approval of `infd-20260927-b01` was refused by flex-auth's
|
||||
`compact_sitting` policy with `policy_denied`, despite platform-root having just
|
||||
completed a fresh key-cape MFA sign-in. flex-auth's 900-second review-freshness
|
||||
check reads `assurance.at`, which key-cape derives from the PKCE session's
|
||||
`AuthTime`.
|
||||
|
||||
Root cause in `src/internal/server/oidc/authorize.go`,
|
||||
`AuthorizeHandler.completeAuthorization`: whenever a `kc_login` cookie already
|
||||
existed for the same username, its `IssuedAt` was unconditionally reused as
|
||||
`authTime` — even on the two call sites that had just called
|
||||
`MFA.ValidateMFAToken` successfully in this exact request (the callback's
|
||||
`mfaToken != ""` branch and `serveMFASubmission`). A user who had an older
|
||||
browser session and then stepped up with a fresh MFA challenge got the *old*
|
||||
session's timestamp stamped onto the *new*, just-proven authentication —
|
||||
understating freshness for every downstream binding-grade check, exactly as
|
||||
`INFD-IN-0005` describes ("completeAuthorization currently copies a prior
|
||||
timestamp... only after actual reauthentication").
|
||||
|
||||
The reuse itself is correct when MFA was **not** just performed in this
|
||||
request (`decision.MFAVerified` carried forward from an existing AAL2 session,
|
||||
via `domain.DecideAssurance`) — reporting mint time there would overstate
|
||||
freshness (`TestAssuranceReportsAuthenticationTimeNotMintTime`). The two cases
|
||||
were conflated under a single `mfaVerified` bool.
|
||||
|
||||
Fix: added a `freshMFA bool` parameter to `completeAuthorization`, `true` only
|
||||
at the two call sites that just validated an MFA token in this request, `false`
|
||||
at the carried-forward-assurance call site. `authTime` now falls back to the
|
||||
prior session's `IssuedAt` only when `!freshMFA`.
|
||||
|
||||
Added `TestAuthorizeCallback_MFASubmission_FreshEvenWithStalePriorLoginCookie`,
|
||||
which seeds a 4-hour-old `kc_login` cookie for the same user, then submits a
|
||||
valid MFA token and asserts the resulting `AuthTime` is within a minute of now.
|
||||
Confirmed it fails against the pre-fix code (`AuthTime` ~4h old) and passes
|
||||
after. Full suite (`go test ./...`) green, no other test needed adjustment —
|
||||
the carried-forward-assurance case (`TestPolicy_...`, `TestLogout_...`,
|
||||
`assurance_time_test.go`) is unaffected since none of it exercises a fresh MFA
|
||||
submission with a pre-existing cookie for the same user.
|
||||
|
||||
Does not touch flex-auth (its 900s window is correct; it was consuming a bad
|
||||
timestamp) or informed-decision (correctly refuses to interpret the PDP's
|
||||
denial). `INFD-IN-0005` can close once informed-decision confirms platform-root
|
||||
can approve after a fresh sign-in against this build.
|
||||
|
||||
## Discovery/JWKS fetch was missing the split-horizon origin headers
|
||||
|
||||
```task
|
||||
id: ADHOC-2026-09-27-T02
|
||||
status: done
|
||||
priority: high
|
||||
```
|
||||
|
||||
Rolling out T01 (image digest `sha256:248e449a...`) broke every KeyCape login,
|
||||
including unrelated clients (`user-engine-portal`), with `auth_failure` /
|
||||
`provider_metadata_unavailable` at `/api/oidc/token` on the KeyCape side and
|
||||
Authelia logging `invalid X-Forwarded-Proto header value 'http'` on
|
||||
`GET /.well-known/openid-configuration` from KeyCape's pod IP.
|
||||
|
||||
Root cause, unrelated to T01's change: `AutheliaAdapter.setPublicOriginHeaders`
|
||||
(the NK-WP-0041-T02/path-B fix, key-cape@3b0446e) annotates only the token
|
||||
exchange (`newTokenRequest`) with `X-Forwarded-Proto`/`X-Forwarded-Host` so
|
||||
Authelia can derive the public issuer for a split-horizon (in-cluster HTTP,
|
||||
public HTTPS) deployment. `idTokenVerifier.get` in `verify.go` — used for the
|
||||
provider-metadata and JWKS fetches that verify every upstream ID token — never
|
||||
got the same treatment; it built a bare request with only an `Accept` header.
|
||||
Authelia 4.38 fails closed on the resulting unmarked in-cluster request. This
|
||||
gap predates today's session (present since 3b0446e); it surfaced now because
|
||||
this was the first login attempted since that image went live.
|
||||
|
||||
Fix: extracted `setPublicOriginHeaders` from an `AutheliaAdapter` method to a
|
||||
package-level function taking the target public base URL explicitly, and gave
|
||||
`idTokenVerifier` its own resolved `publicBaseURL` (same
|
||||
`BrowserBaseURL`-else-`BaseURL` fallback as the adapter) so `get()` applies
|
||||
the same headers. Added `TestDiscoveryFetch_CarriesPublicOriginHeaders`
|
||||
(confirmed it fails without the fix, passes with it). Full suite green.
|
||||
|
||||
New image `key-cape@<pending>` still needs a rollout to `sso-mfa/k8s/keycape/deployment.yaml`
|
||||
before platform-root can sign in again.
|
||||
|
|
@ -0,0 +1,499 @@
|
|||
---
|
||||
id: KEY-WP-0001
|
||||
type: workplan
|
||||
title: "KeyCape Implementation — Lightweight IAM Profile"
|
||||
domain: infotech
|
||||
repo: key-cape
|
||||
status: finished
|
||||
owner: Bernd
|
||||
topic_slug: netkingdom
|
||||
topic_id: a6c6e745-bf54-4465-9340-1534a2be493e
|
||||
repo_id: 8a99bb74-1ec0-4478-ac70-35a7cddb0e3c
|
||||
created: 2026-03-13
|
||||
updated: 2026-03-13
|
||||
spec_refs:
|
||||
- wiki/KeyCapeSpecification_v0.1.md
|
||||
- wiki/KeyCapeSpecificationPack_v0.1.md
|
||||
decisions:
|
||||
- id: ADR-0001
|
||||
title: "Implementation language: Go"
|
||||
hub_decision_id: 620beb04-fa3f-4a9d-9806-02890a7a2b0d
|
||||
status: accepted
|
||||
ref: docs/adr/ADR-0001-choose-go-for-keycape.md
|
||||
state_hub_workstream_id: "0d34dfc1-7ccb-4bd5-b872-5c7379b9adce"
|
||||
---
|
||||
|
||||
# KEY-WP-0001 — KeyCape Implementation
|
||||
|
||||
Implements the **NetKingdom IAM Profile** via KeyCape: a stateless, profile-constrained OIDC
|
||||
server orchestrating Authelia, LLDAP, and privacyIDEA. Replaceable by Keycloak without
|
||||
application changes.
|
||||
|
||||
## Language Decision
|
||||
|
||||
**Go** — decided 2026-03-13 by Bernd. See `docs/adr/ADR-0001-choose-go-for-keycape.md`.
|
||||
|
||||
KeyCape is an orchestrating boundary service (HTTP, adapters, JWT via library, CLI tooling) —
|
||||
Go's strongest domain. Rust revisitable if a subcomponent needs stronger guarantees later.
|
||||
Mandatory guardrails: typed domain models, narrow adapter interfaces, layered architecture,
|
||||
fuzz tests on validator/redirect/claim-mapping, no cleverness.
|
||||
|
||||
## Repo Structure (from spec §12)
|
||||
|
||||
```
|
||||
src/
|
||||
server/
|
||||
oidc/ # Profile endpoints
|
||||
telemetry/ # Structured event emission
|
||||
errors/ # Error taxonomy + enforcement middleware
|
||||
adapters/
|
||||
authelia/ # Auth flow delegation
|
||||
lldap/ # Identity directory reads
|
||||
privacyidea/ # MFA enforcement
|
||||
validator/ # Canonical LDAP schema validator binary
|
||||
migration/
|
||||
lldap-export/ # LLDAP → canonical
|
||||
keycape-to-keycloak/ # Canonical → Keycloak realm import
|
||||
lldap-to-ldap/ # LLDAP → OpenLDAP/389DS/AD LDIF
|
||||
spec/
|
||||
canonical-model.yaml # Source of truth for all identity data
|
||||
ldap-schema.yaml # Canonical LDAP schema rules
|
||||
tests/
|
||||
profile/ # Scenario A — lightweight baseline
|
||||
negative/ # Scenario D — unsupported feature rejection
|
||||
migration/ # Scenarios B & C — replacement
|
||||
```
|
||||
|
||||
## Dependency Order
|
||||
|
||||
```
|
||||
T01 (project setup)
|
||||
└─ T02 (canonical model) T04 (error taxonomy)
|
||||
└─ T03 (LDAP validator) └─ T13 (telemetry)
|
||||
└─ T10 (LLDAP adapter) └─ T14 (enforcement layer)
|
||||
└─ T11 (Authelia) │
|
||||
└─ T12 (privacyIDEA) │
|
||||
│ │
|
||||
T05 ─ T06 ─ T07 ─ T08 ─┴─ T09 (OIDC server)
|
||||
│
|
||||
T18 (profile tests / Scenario A)
|
||||
T21 (negative tests / Scenario D)
|
||||
│
|
||||
T15 → T16 → T19 (Scenario B)
|
||||
T15 → T17 → T20 (Scenario C)
|
||||
│
|
||||
T22 (dev stack)
|
||||
T23 (production packaging)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 1 — Foundations
|
||||
|
||||
## T01 — Project setup: Go module, repo layout, CI skeleton
|
||||
|
||||
```task
|
||||
id: KEY-WP-0001-T01
|
||||
hub_task_id: 25613e3f-2a65-409e-afaa-d23ded0bc256
|
||||
priority: high
|
||||
status: done
|
||||
state_hub_task_id: "38822bc0-4189-4909-874e-ea40e5771250"
|
||||
```
|
||||
|
||||
Initialise language module in `src/`. Create directory skeleton per spec §12. Add Makefile
|
||||
targets: `build`, `test`, `lint`. Set up CI (build + test on every push). Scaffolding only —
|
||||
no application code. **Agent must call `record_decision()` with chosen language (Go or Rust).**
|
||||
|
||||
## T02 — Canonical identity model: machine-readable schema
|
||||
|
||||
```task
|
||||
id: KEY-WP-0001-T02
|
||||
hub_task_id: deee2929-9386-41db-bf91-fbd9ad646c28
|
||||
priority: high
|
||||
status: done
|
||||
depends_on: [T01]
|
||||
state_hub_task_id: "940c118b-c1e6-4dda-bd4c-4fac105822be"
|
||||
```
|
||||
|
||||
Write `spec/canonical-model.yaml`. Six entities: User, Group, Role, Client, Membership,
|
||||
MFAEnrollment (fields per spec §2). Include JSON Schema or CUE schema for programmatic
|
||||
validation. This file is the **source of truth** — all other code derives from it.
|
||||
|
||||
## T03 — Canonical LDAP schema + validator
|
||||
|
||||
```task
|
||||
id: KEY-WP-0001-T03
|
||||
hub_task_id: 02592c65-db23-474b-b06b-019e95df8146
|
||||
priority: high
|
||||
status: done
|
||||
depends_on: [T01, T02]
|
||||
state_hub_task_id: "c1715d70-f10f-45e9-b73a-b54a3d360342"
|
||||
```
|
||||
|
||||
Write `spec/ldap-schema.yaml`: tree layout (`ou=users`, `ou=groups`, `ou=clients` under
|
||||
`dc=netkingdom,dc=local`), object classes (`inetOrgPerson`, `groupOfNames`), required/optional
|
||||
attributes. Implement `validator/` binary. Structural rules: valid DN, required attrs, no unknown
|
||||
attrs, valid group memberships. Semantic rules: referenced users exist, no cycles, usernames
|
||||
unique, email format valid. Validator runs in `--mode=ci|provisioning|migration`. Emits
|
||||
machine-readable report.
|
||||
|
||||
## T04 — Error taxonomy: types, JSON format, middleware
|
||||
|
||||
```task
|
||||
id: KEY-WP-0001-T04
|
||||
hub_task_id: 46870fd6-0672-432b-8824-6bc2e24811b3
|
||||
priority: high
|
||||
status: done
|
||||
depends_on: [T01]
|
||||
state_hub_task_id: "6e3b6b97-ac77-44c5-959e-be12751f1b63"
|
||||
```
|
||||
|
||||
Implement four error types (spec §5):
|
||||
- `feature_not_supported_by_profile`
|
||||
- `available_in_keycloak_mode_only`
|
||||
- `rejected_for_profile_safety`
|
||||
- `invalid_profile_usage`
|
||||
|
||||
JSON format: `{"error": "...", "description": "...", "feature": "..."}`. HTTP middleware wraps
|
||||
all handler errors. Error type strings are stable and test-assertable.
|
||||
|
||||
---
|
||||
|
||||
## Phase 2 — OIDC Server
|
||||
|
||||
## T05 — OIDC discovery endpoint (/.well-known/openid-configuration)
|
||||
|
||||
```task
|
||||
id: KEY-WP-0001-T05
|
||||
hub_task_id: 92eb8916-cb22-4786-9f16-a8a07272f818
|
||||
priority: high
|
||||
status: done
|
||||
depends_on: [T04]
|
||||
state_hub_task_id: "0dbc08e3-c465-4c37-a219-832a580bedfd"
|
||||
```
|
||||
|
||||
`GET /.well-known/openid-configuration`. Advertise **only** profile-supported features:
|
||||
`authorization_code`, S256 PKCE, RS256, static scopes. Must NOT advertise: dynamic registration,
|
||||
implicit flow. Issuer configurable. Cacheable response.
|
||||
|
||||
## T06 — Authorization endpoint with PKCE and redirect URI validation
|
||||
|
||||
```task
|
||||
id: KEY-WP-0001-T06
|
||||
hub_task_id: c3df620b-9864-4ff1-ba6d-26057c6f4d59
|
||||
priority: high
|
||||
status: done
|
||||
depends_on: [T04, T11, T12, T13, T14]
|
||||
state_hub_task_id: "cdb4b06d-3d54-49dd-ac05-ca9ed6d7322f"
|
||||
```
|
||||
|
||||
`GET/POST /authorize`. Validate: `client_id` (static config), `redirect_uri` (exact match —
|
||||
wildcard → `rejected_for_profile_safety`), `response_type=code`, `scope` contains `openid`,
|
||||
`code_challenge` present (missing → `invalid_profile_usage`), `code_challenge_method=S256`.
|
||||
Delegate to Authelia adapter. Store PKCE state server-side. No implicit or hybrid flow.
|
||||
|
||||
## T07 — Token endpoint: JWT/RS256, canonical claim mapping
|
||||
|
||||
```task
|
||||
id: KEY-WP-0001-T07
|
||||
hub_task_id: d3248f4d-e0e9-4144-9844-c9768dc896d6
|
||||
priority: high
|
||||
status: done
|
||||
depends_on: [T06, T08, T10]
|
||||
state_hub_task_id: "534d8616-90de-4d32-961c-c2ef719642e4"
|
||||
```
|
||||
|
||||
`POST /token`. Validate PKCE `code_verifier`. Issue RS256 JWT via standard library (no custom
|
||||
crypto). Mandatory claims: `iss`, `sub` (canonical user ID), `aud`, `exp`, `iat`. Optional
|
||||
claims: `preferred_username` (LDAP `uid`), `email` (LDAP `mail`), `groups` (groupOfNames),
|
||||
`roles`. Short, explicitly configured token lifetime. Any other grant type →
|
||||
`feature_not_supported_by_profile`.
|
||||
|
||||
## T08 — JWKS endpoint (/jwks)
|
||||
|
||||
```task
|
||||
id: KEY-WP-0001-T08
|
||||
hub_task_id: 58a1d705-b788-4d66-8c0a-33edff63a885
|
||||
priority: high
|
||||
status: done
|
||||
depends_on: [T01]
|
||||
state_hub_task_id: "7e2167be-bcc7-49c2-8681-e518abd5bc0c"
|
||||
```
|
||||
|
||||
`GET /jwks`. RS256 public key in JWK Set format. Key loaded from config. Key rotation: serve
|
||||
multiple keys during rotation window, keyed by `kid`. Standard library key generation only.
|
||||
|
||||
## T09 — Userinfo endpoint (/userinfo)
|
||||
|
||||
```task
|
||||
id: KEY-WP-0001-T09
|
||||
hub_task_id: 742d3924-21e9-4304-86e9-0400af0e81ee
|
||||
priority: medium
|
||||
status: done
|
||||
depends_on: [T07, T10]
|
||||
state_hub_task_id: "78094ca5-a831-4443-9ccf-fc476ff87b91"
|
||||
```
|
||||
|
||||
`GET /userinfo`. Optional per spec — implement if any registered client requires it. Validate
|
||||
Bearer token (RS256 + expiry). Return claim subset scoped to granted scopes. Claims must be
|
||||
identical to ID token for same scopes. If no client needs it: stub returning
|
||||
`available_in_keycloak_mode_only`.
|
||||
|
||||
---
|
||||
|
||||
## Phase 3 — Backend Adapters
|
||||
|
||||
## T10 — LLDAP adapter: user and group reads
|
||||
|
||||
```task
|
||||
id: KEY-WP-0001-T10
|
||||
hub_task_id: 2043b10a-6822-45f8-abcc-4e233d918fb0
|
||||
priority: high
|
||||
status: done
|
||||
depends_on: [T02, T03]
|
||||
state_hub_task_id: "97d19662-f482-4ea5-84fd-9fccb84ff317"
|
||||
```
|
||||
|
||||
`adapters/lldap`. LDAP protocol connection to LLDAP. Interface: `LookupUser(username) → canonical
|
||||
User`, `LookupGroups(userDN) → []Group`, `ValidatePassword(username, password) → bool`. Attribute
|
||||
map: `uid→username`, `cn→displayName`, `mail→email`, `memberOf→groups`. Run canonical LDAP schema
|
||||
validator on every read. No LDAP internals exposed to `server/`.
|
||||
|
||||
## T11 — Authelia adapter: session and auth flow delegation
|
||||
|
||||
```task
|
||||
id: KEY-WP-0001-T11
|
||||
hub_task_id: ad129a14-1552-4717-b1dd-b529d18ce681
|
||||
priority: high
|
||||
status: done
|
||||
depends_on: [T04, T13]
|
||||
state_hub_task_id: "6461865b-f57c-4591-9cf3-68c79af22723"
|
||||
```
|
||||
|
||||
`adapters/authelia`. Initiate auth redirect to Authelia, receive callback, extract authenticated
|
||||
user identity, hand off to MFA check (T12). Must not leak Authelia session tokens/cookies into
|
||||
profile layer. Unavailable Authelia → fail closed (`auth_failure` event).
|
||||
|
||||
## T12 — privacyIDEA adapter: MFA enforcement
|
||||
|
||||
```task
|
||||
id: KEY-WP-0001-T12
|
||||
hub_task_id: 1ef196e6-2304-4cf6-b205-47ac1da879ec
|
||||
priority: high
|
||||
status: done
|
||||
depends_on: [T02, T13]
|
||||
state_hub_task_id: "e403a783-c856-4d6d-b859-a9cad7545fe1"
|
||||
```
|
||||
|
||||
`adapters/privacyidea`. **KeyCape must NOT implement MFA logic.** Interface:
|
||||
`CheckMFARequired(user) → bool`, `ValidateMFAToken(user, token) → bool`. MFA failure → no token
|
||||
issued + `auth_failure` telemetry. MFA enrollment state from canonical `MFAEnrollment` entity.
|
||||
privacyIDEA remains stable across lightweight → expanded migration.
|
||||
|
||||
---
|
||||
|
||||
## Phase 4 — Telemetry & Enforcement
|
||||
|
||||
## T13 — Telemetry pipeline: structured event emission
|
||||
|
||||
```task
|
||||
id: KEY-WP-0001-T13
|
||||
hub_task_id: 704146bf-cd60-4922-b18b-3d209cff3ac3
|
||||
priority: high
|
||||
status: done
|
||||
depends_on: [T01]
|
||||
state_hub_task_id: "4df7bda1-5b84-4b4c-9b16-bcb1d3cca096"
|
||||
```
|
||||
|
||||
`server/telemetry`. Event types (spec §6.1): `auth_start`, `auth_success`, `auth_failure`,
|
||||
`token_issued`, `unsupported_feature`, `invalid_request`, `migration_event`. Required fields
|
||||
(spec §6.2): `timestamp`, `client_id`, `endpoint`, `feature`, `result`, `error_type`, `scopes`,
|
||||
`grant_type`, `environment`, `trace_id`. Pluggable outputs: structured log (default), Prometheus
|
||||
metrics endpoint. Every auth and error path emits an event — **no silent paths**.
|
||||
|
||||
## T14 — Unsupported feature enforcement layer
|
||||
|
||||
```task
|
||||
id: KEY-WP-0001-T14
|
||||
hub_task_id: 71f44886-ab61-4160-a435-72b35af472a0
|
||||
priority: high
|
||||
status: done
|
||||
depends_on: [T04, T13]
|
||||
state_hub_task_id: "ae16fba9-5bb4-4780-ac77-558e3ed7e1dd"
|
||||
```
|
||||
|
||||
`server/errors` enforcement middleware. Intercept any parameter, grant type, scope, or client
|
||||
config exceeding the profile. Return correct error type + emit `unsupported_feature` telemetry.
|
||||
Maintain a **registry** of unsupported features (adding new ones requires no handler changes):
|
||||
`dynamic_client_registration`, `identity_broker`, `wildcard_redirect_uri`, `implicit_flow`, etc.
|
||||
Every registry entry must have a corresponding test in T21.
|
||||
|
||||
---
|
||||
|
||||
## Phase 5 — Migration Tooling
|
||||
|
||||
## T15 — Migration: LLDAP → canonical export
|
||||
|
||||
```task
|
||||
id: KEY-WP-0001-T15
|
||||
hub_task_id: 1bd13f76-2d62-429d-b230-d785ef6a3f2f
|
||||
priority: medium
|
||||
status: done
|
||||
depends_on: [T02, T03, T10]
|
||||
state_hub_task_id: "f7549cd7-33f0-4407-a656-ab8f5a184e64"
|
||||
```
|
||||
|
||||
`migration/lldap-export` tool. Read all users, groups, memberships, attributes from LLDAP. Map
|
||||
to canonical model. Validate against LDAP schema validator before writing. Output:
|
||||
`canonical-export.yaml`. Emit `migration_event` telemetry. Idempotent. Include incompatibility
|
||||
report for unmappable LLDAP data.
|
||||
|
||||
## T16 — Migration: canonical → Keycloak import
|
||||
|
||||
```task
|
||||
id: KEY-WP-0001-T16
|
||||
hub_task_id: f3d50e80-f6b5-4c0c-a5f3-08308ea1a95e
|
||||
priority: medium
|
||||
status: done
|
||||
depends_on: [T15]
|
||||
state_hub_task_id: "96486c41-9f33-42a5-b7b6-ad0a9eb2bdee"
|
||||
```
|
||||
|
||||
`migration/keycape-to-keycloak` tool. Read canonical export (T15). Transform to Keycloak realm
|
||||
import format: users, groups, clients, roles, scope mappings. Preserve: same issuer, same claims,
|
||||
same scopes, same client behavior. Output: `keycloak-realm-import.json`. Emit `migration_event`.
|
||||
Include round-trip validation report.
|
||||
|
||||
## T17 — Migration: LLDAP → full LDAP (OpenLDAP / 389DS / AD)
|
||||
|
||||
```task
|
||||
id: KEY-WP-0001-T17
|
||||
hub_task_id: 044d99c8-39cb-4f35-9308-912ae829bd22
|
||||
priority: medium
|
||||
status: done
|
||||
depends_on: [T15]
|
||||
state_hub_task_id: "1ec335a2-80ca-4c34-b08e-211f537e4214"
|
||||
```
|
||||
|
||||
`migration/lldap-to-ldap` tool. Export via T15 canonical export. Generate LDIF for target
|
||||
(`--target=openldap|389ds|ad`). Migrate: users, groups, memberships, attributes. Run validator
|
||||
on output LDIF before import. Produce validation report. **Orthogonal to T16** — the two
|
||||
migration dimensions are independent (spec §14.1).
|
||||
|
||||
---
|
||||
|
||||
## Phase 6 — Replacement Tests
|
||||
|
||||
## T18 — Profile test suite: Scenario A (lightweight baseline)
|
||||
|
||||
```task
|
||||
id: KEY-WP-0001-T18
|
||||
hub_task_id: 76abc3f6-9c4e-4aca-9995-72b728925812
|
||||
priority: high
|
||||
status: done
|
||||
depends_on: [T05, T06, T07, T08, T09, T22]
|
||||
state_hub_task_id: "1b0e9f26-d441-42b8-b532-1eb713fb355d"
|
||||
```
|
||||
|
||||
`tests/profile`. Provision canonical fixtures into LLDAP + Authelia + KeyCape. Test categories
|
||||
(spec §15.3): discovery, login flow (PKCE), token claim assertions (all mandatory + optional),
|
||||
redirect validation, client config, MFA policy, logout (if implemented). Tests are
|
||||
**backend-agnostic** — same suite runs in T19 and T20. Must pass for Scenario A conformance.
|
||||
|
||||
## T19 — Replacement test suite: Scenario B (IAM swap, same directory)
|
||||
|
||||
```task
|
||||
id: KEY-WP-0001-T19
|
||||
hub_task_id: 56d03e89-934b-4992-bfe4-b32f275882e3
|
||||
priority: medium
|
||||
status: done
|
||||
depends_on: [T18, T16]
|
||||
state_hub_task_id: "a02d24e7-32de-4be6-935c-896c10dde020"
|
||||
```
|
||||
|
||||
Run T18 suite against Keycloak + LLDAP (configured from T16 canonical export). **No test code
|
||||
changes allowed.** Migration successful only if all T18 tests pass. Proves IAM replaceability
|
||||
without directory migration.
|
||||
|
||||
## T20 — Replacement test suite: Scenario C (full expansion)
|
||||
|
||||
```task
|
||||
id: KEY-WP-0001-T20
|
||||
hub_task_id: ec3cae5c-9942-4be7-acc0-1eb9f02aba45
|
||||
priority: medium
|
||||
status: done
|
||||
depends_on: [T19, T17]
|
||||
state_hub_task_id: "545f319f-053d-48bd-8d94-c8c05cd56736"
|
||||
```
|
||||
|
||||
Apply T17 LLDAP→OpenLDAP migration, then T16 Keycloak import. Run T18 suite. Migration successful
|
||||
only if all tests pass. privacyIDEA must remain stable (no MFA re-enrollment required).
|
||||
|
||||
## T21 — Negative profile tests: Scenario D (unsupported feature rejection)
|
||||
|
||||
```task
|
||||
id: KEY-WP-0001-T21
|
||||
hub_task_id: a5112b63-121f-4d17-ac1a-fb46d160413e
|
||||
priority: high
|
||||
status: done
|
||||
depends_on: [T14]
|
||||
state_hub_task_id: "5856afe0-2a9e-4489-b057-35e59f86c359"
|
||||
```
|
||||
|
||||
`tests/negative`. For every entry in T14 unsupported-feature registry: attempt usage, assert
|
||||
correct `error.error` string, assert `unsupported_feature` telemetry event emitted. Covered
|
||||
cases: `dynamic_client_registration`, `implicit_flow`, wildcard redirect URIs, identity brokering,
|
||||
missing PKCE, unknown scopes, unknown grant types. **Must run in CI.** Proves enforcement layer
|
||||
is complete.
|
||||
|
||||
---
|
||||
|
||||
## Phase 7 — Operations
|
||||
|
||||
## T22 — Dev mode stack: docker-compose with LLDAP + Authelia
|
||||
|
||||
```task
|
||||
id: KEY-WP-0001-T22
|
||||
hub_task_id: e840963f-cd19-4b38-857a-7c40df165d3d
|
||||
priority: medium
|
||||
status: done
|
||||
depends_on: [T01]
|
||||
state_hub_task_id: "b98f2671-a20a-4438-99c9-fbe0e5324534"
|
||||
```
|
||||
|
||||
`docker-compose.dev.yml`: KeyCape, LLDAP, Authelia, privacyIDEA (or stub). Pre-seeded with
|
||||
canonical fixtures from T02. Makefile targets: `make dev` (start + verify basic auth flow),
|
||||
`make seed` (re-apply fixtures without full restart). Deterministic: same seed → same state.
|
||||
Test environment for T18 and T21.
|
||||
|
||||
## T23 — Slim production packaging: binary + config + health
|
||||
|
||||
```task
|
||||
id: KEY-WP-0001-T23
|
||||
hub_task_id: d5723683-f739-4362-b62e-71213dc5a89e
|
||||
priority: low
|
||||
status: done
|
||||
depends_on: [T18, T21]
|
||||
state_hub_task_id: "8c1752c2-7fb3-4da5-aab3-6b7acf12ea64"
|
||||
```
|
||||
|
||||
Single stateless binary. Declarative YAML config: profile version, client definitions, backend
|
||||
connections, LDAP schema rules, privacyIDEA settings, telemetry destination, token lifetime,
|
||||
issuer URL. Static config validation on startup. `/healthz` endpoint. Minimal container image
|
||||
(distroless or Alpine). Config environment-promotable via env var overrides only.
|
||||
|
||||
---
|
||||
|
||||
## Acceptance Criteria (from spec §15.4 and §20)
|
||||
|
||||
A release is conformant when:
|
||||
|
||||
- [x] Scenario A tests pass (T18) — `src/tests/profile/profile_test.go` (8 tests)
|
||||
- [x] Scenario D tests pass (T21) — `src/tests/negative/negative_test.go` (8 tests)
|
||||
- [x] Scenario B tests pass (T19) — `src/tests/migration/scenario_b_test.go` (7 tests)
|
||||
- [x] Scenario C tests pass (T20) — `src/tests/migration/scenario_c_test.go` (6 tests)
|
||||
- [x] All error responses use taxonomy types from spec §5 — `internal/errors/taxonomy.go`
|
||||
- [x] All auth/error paths emit structured telemetry (T13) — `internal/server/telemetry/`
|
||||
- [x] Canonical LDAP schema validator passes on all fixtures (T03) — `internal/validator/`
|
||||
- [x] No handwritten cryptography anywhere in the codebase — stdlib `crypto/rsa` only
|
||||
- [x] Config is statically validated at startup (T23) — `internal/config/validate.go`
|
||||
|
|
@ -0,0 +1,245 @@
|
|||
---
|
||||
id: KEY-WP-0002
|
||||
type: workplan
|
||||
title: "KeyCape Container Image — Build & Publish to Gitea OCI Registry"
|
||||
domain: infotech
|
||||
repo: key-cape
|
||||
status: finished
|
||||
owner: netkingdom
|
||||
topic_slug: netkingdom
|
||||
created: "2026-03-22"
|
||||
updated: "2026-03-21"
|
||||
capability_request_id: ""
|
||||
state_hub_workstream_id: "c8843c7a-460a-47a2-b45a-b8d3940f9aa2"
|
||||
---
|
||||
|
||||
# KEY-WP-0002 — KeyCape Container Image — Build & Publish to Gitea OCI Registry
|
||||
|
||||
## Problem
|
||||
|
||||
KeyCape has a `Dockerfile` but no automated build pipeline and no published
|
||||
image. Other services (k3s deployments, local dev) that need to run KeyCape
|
||||
must build locally from source. There is no versioned artefact to reference
|
||||
in Helm charts or manifests.
|
||||
|
||||
The capability request for this work was originally misrouted to railiance.
|
||||
It belongs here: KeyCape owns its own image.
|
||||
|
||||
## Goal
|
||||
|
||||
Produce a versioned OCI image for KeyCape, published to the Gitea container
|
||||
registry on CoulombCore (`92.205.130.254:32166`), triggered automatically on
|
||||
every merge to `main` and on semver tags (`v*`).
|
||||
|
||||
**Gitea OCI registry endpoint:** `92.205.130.254:32166`
|
||||
**Image name:** `92.205.130.254:32166/coulomb/key-cape`
|
||||
|
||||
> **Why Gitea, not GHCR?**
|
||||
> The net-kingdom cluster is self-hosted. Keeping images in Gitea (also
|
||||
> self-hosted on CoulombCore) avoids any external registry dependency and
|
||||
> keeps image pulls within the cluster network. GHCR is a future option
|
||||
> once public distribution is needed.
|
||||
|
||||
## Design
|
||||
|
||||
### Image naming & tagging
|
||||
|
||||
| Trigger | Tags applied |
|
||||
|---------|-------------|
|
||||
| push to `main` | `latest`, `main-<short-sha>` |
|
||||
| tag `v1.2.3` | `1.2.3`, `1.2`, `1`, `latest` |
|
||||
|
||||
### Build
|
||||
|
||||
Multi-stage Dockerfile already present — no changes needed to the build
|
||||
itself. The image builds to a distroless static binary (~10 MB).
|
||||
|
||||
### Registry auth
|
||||
|
||||
Gitea issues a personal access token (or machine account token) with
|
||||
`write:packages` scope. Stored as Gitea Actions secret `REGISTRY_TOKEN`;
|
||||
username stored as `REGISTRY_USER`.
|
||||
|
||||
For local `make push`, credentials are passed via `docker login` before
|
||||
the push target runs.
|
||||
|
||||
### Makefile targets
|
||||
|
||||
```makefile
|
||||
IMAGE_REGISTRY ?= 92.205.130.254:32166
|
||||
IMAGE_REPO ?= coulomb/key-cape
|
||||
IMAGE_TAG ?= latest
|
||||
IMAGE := $(IMAGE_REGISTRY)/$(IMAGE_REPO):$(IMAGE_TAG)
|
||||
|
||||
image:
|
||||
docker build -t $(IMAGE) .
|
||||
|
||||
push: image
|
||||
docker push $(IMAGE)
|
||||
|
||||
image-tag:
|
||||
docker tag $(IMAGE) $(IMAGE_REGISTRY)/$(IMAGE_REPO):$(IMAGE_TAG)
|
||||
```
|
||||
|
||||
### Gitea Actions workflow
|
||||
|
||||
`.gitea/workflows/image.yaml` — triggers on push to `main` and on `v*` tags:
|
||||
- Checkout
|
||||
- Set up Docker Buildx
|
||||
- Login to `92.205.130.254:32166` using secrets
|
||||
- Build and push with metadata-action tags
|
||||
- (Optional) sign with cosign if available
|
||||
|
||||
### k3s insecure registry
|
||||
|
||||
Gitea runs over plain HTTP on port 32166 (NodePort). k3s must be configured
|
||||
to treat this endpoint as an insecure registry so image pulls work from
|
||||
within the cluster:
|
||||
|
||||
```yaml
|
||||
# /etc/rancher/k3s/registries.yaml (on CoulombCore)
|
||||
mirrors:
|
||||
"92.205.130.254:32166":
|
||||
endpoint:
|
||||
- "http://92.205.130.254:32166"
|
||||
```
|
||||
|
||||
k3s picks this up on restart (or SIGHUP). Worker nodes (if any) need the
|
||||
same file.
|
||||
|
||||
---
|
||||
|
||||
## Tasks
|
||||
|
||||
### T01 — Makefile: image, push, image-tag targets
|
||||
|
||||
```task
|
||||
id: KEY-WP-0002-T01
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "749472fc-edb9-4948-9ebc-58d5f38327ee"
|
||||
```
|
||||
|
||||
Add `image`, `push`, and `image-tag` targets to `Makefile` with
|
||||
`IMAGE_REGISTRY`, `IMAGE_REPO`, `IMAGE_TAG` variables defaulting to the
|
||||
Gitea endpoint and `coulomb/key-cape:latest`.
|
||||
|
||||
Gate: `make image` builds successfully locally; `IMAGE_TAG=dev make image`
|
||||
produces a differently-tagged image.
|
||||
|
||||
---
|
||||
|
||||
### T02 — Gitea Actions workflow
|
||||
|
||||
```task
|
||||
id: KEY-WP-0002-T02
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "8ecf18cc-a3bb-4ede-a09c-fcd0d26d7f9d"
|
||||
```
|
||||
|
||||
Create `.gitea/workflows/image.yaml`:
|
||||
- Trigger: `push` to `main`, `push` tags matching `v*`
|
||||
- Runner: `act_runner` label (or `ubuntu-latest` if configured)
|
||||
- Steps: checkout → docker buildx → login → build+push
|
||||
- Tags via `docker/metadata-action`: `latest` on main, semver on tags
|
||||
|
||||
Secrets required (document in README.md under "CI"):
|
||||
- `REGISTRY_USER` — Gitea username or machine account
|
||||
- `REGISTRY_TOKEN` — Gitea personal access token with `write:packages`
|
||||
|
||||
Gate: workflow file is syntactically valid; documented in README.
|
||||
|
||||
---
|
||||
|
||||
### T03 — k3s insecure registry config on CoulombCore
|
||||
|
||||
```task
|
||||
id: KEY-WP-0002-T03
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "2dde67f9-944f-418d-a2e9-7367bc556425"
|
||||
```
|
||||
|
||||
On CoulombCore, create/update `/etc/rancher/k3s/registries.yaml` to add
|
||||
the Gitea NodePort as an HTTP mirror. Restart k3s (or send SIGHUP) and
|
||||
verify `crictl pull 92.205.130.254:32166/coulomb/key-cape:latest` works.
|
||||
|
||||
Gate: image pull from within the cluster succeeds without TLS errors.
|
||||
|
||||
---
|
||||
|
||||
### T04 — Create Gitea machine account & token
|
||||
|
||||
```task
|
||||
id: KEY-WP-0002-T04
|
||||
status: done
|
||||
priority: medium
|
||||
state_hub_task_id: "25775e10-3164-4adb-9c41-835c86fde5f8"
|
||||
```
|
||||
|
||||
In Gitea (http://92.205.130.254:32166), create a machine account
|
||||
`ci-netkingdom` (or reuse an existing service account) with access to
|
||||
the `netkingdom` organisation. Generate a token with `write:packages`
|
||||
scope and store it in:
|
||||
- Gitea Actions secrets on the `key-cape` repo: `REGISTRY_USER`, `REGISTRY_TOKEN`
|
||||
- The net-kingdom credential store (SOPS-encrypted) under
|
||||
`credentials/gitea-ci-token.enc.yaml`
|
||||
|
||||
Gate: `docker login 92.205.130.254:32166` succeeds with the token;
|
||||
secret is in the credential store.
|
||||
|
||||
---
|
||||
|
||||
### T05 — Smoke test: push and pull a dev image
|
||||
|
||||
```task
|
||||
id: KEY-WP-0002-T05
|
||||
status: done
|
||||
priority: medium
|
||||
state_hub_task_id: "0f6ab38f-6d34-41af-9180-f19c687947b5"
|
||||
```
|
||||
|
||||
Manually trigger a build-and-push:
|
||||
```bash
|
||||
docker login 92.205.130.254:32166
|
||||
IMAGE_TAG=dev make push
|
||||
```
|
||||
|
||||
Then verify the image is pullable from CoulombCore:
|
||||
```bash
|
||||
# on CoulombCore
|
||||
crictl pull 92.205.130.254:32166/coulomb/key-cape:dev
|
||||
```
|
||||
|
||||
Gate: pull succeeds; image is listed in Gitea -> Packages -> coulomb/key-cape.
|
||||
|
||||
---
|
||||
|
||||
### T06 — Update README with registry & CI docs
|
||||
|
||||
```task
|
||||
id: KEY-WP-0002-T06
|
||||
status: done
|
||||
priority: low
|
||||
state_hub_task_id: "946cd34d-94da-4fa9-a781-ed36f6c827a3"
|
||||
```
|
||||
|
||||
Add a "Container Image" section to `README.md` documenting:
|
||||
- Registry URL and image name
|
||||
- How to pull (`docker pull 92.205.130.254:32166/coulomb/key-cape:latest`)
|
||||
- How to build and push locally (Makefile targets)
|
||||
- CI secrets required for the Actions workflow
|
||||
|
||||
Gate: README section present and accurate.
|
||||
|
||||
---
|
||||
|
||||
## Done Criteria
|
||||
|
||||
- [ ] `make image` and `make push` work locally
|
||||
- [ ] `.gitea/workflows/image.yaml` present and documented
|
||||
- [ ] k3s can pull the image from Gitea without TLS errors
|
||||
- [ ] Machine account token stored in credential store
|
||||
- [ ] Smoke test: `dev` image pushed and pulled successfully
|
||||
- [ ] README updated
|
||||
|
|
@ -0,0 +1,200 @@
|
|||
---
|
||||
id: KEY-WP-0003
|
||||
type: workplan
|
||||
title: "Bootstrap Console OIDC Login and MFA Verification"
|
||||
domain: infotech
|
||||
repo: key-cape
|
||||
status: finished
|
||||
owner: codex
|
||||
topic_slug: netkingdom
|
||||
created: "2026-05-24"
|
||||
updated: "2026-05-24"
|
||||
state_hub_workstream_id: "02990009-a2b3-44f6-a579-487fbacae41a"
|
||||
---
|
||||
|
||||
# KEY-WP-0003 - Bootstrap Console OIDC Login and MFA Verification
|
||||
|
||||
## Problem
|
||||
|
||||
The NetKingdom security bootstrap console now acts as a local OIDC client
|
||||
callback so the operator can verify the dedicated platform-root login before
|
||||
approving custody mode. The current live KeyCape deployment rejects that flow
|
||||
with:
|
||||
|
||||
```json
|
||||
{
|
||||
"error": "invalid_profile_usage",
|
||||
"description": "redirect_uri does not match any registered URI",
|
||||
"feature": "redirect_uri"
|
||||
}
|
||||
```
|
||||
|
||||
That error is correct profile enforcement: KeyCape only accepts exact
|
||||
registered redirect URIs. The live `demo-app` registration has not yet been
|
||||
updated to allow the local bootstrap console callback:
|
||||
|
||||
- `http://127.0.0.1:8876/oidc/callback`
|
||||
- `http://localhost:8876/oidc/callback`
|
||||
|
||||
After that is fixed, there is a second usability/security gap. KeyCape checks
|
||||
privacyIDEA MFA after the Authelia callback, but the browser flow currently
|
||||
expects an `mfa_token` query parameter instead of presenting a proper OTP
|
||||
challenge page to the human operator.
|
||||
|
||||
## Goal
|
||||
|
||||
Make the bootstrap console's "Start demo OIDC login" button a real
|
||||
end-to-end verification path for the current lightweight IAM stack:
|
||||
|
||||
1. KeyCape accepts the bootstrap console callback URI by exact registration.
|
||||
2. The browser leaves KeyCape for the public Authelia login URL.
|
||||
3. After password login, KeyCape presents a minimal MFA challenge when
|
||||
privacyIDEA requires one.
|
||||
4. KeyCape issues an OIDC authorization code to the bootstrap console callback.
|
||||
5. The console can exchange the code and let the operator mark
|
||||
`OIDC login verified` without exposing tokens or secrets.
|
||||
|
||||
This keeps KeyCape's security posture intact: no wildcard redirect URIs, no
|
||||
dynamic client registration, no token display, and no storage of OTP material.
|
||||
|
||||
## Design Notes
|
||||
|
||||
- Prefer a dedicated public client named `netkingdom-bootstrap-console` for
|
||||
long-lived clarity. Reusing `demo-app` is acceptable for the immediate
|
||||
unblock only if the deployment/runbook clearly labels it as a bootstrap test
|
||||
client.
|
||||
- The bootstrap callback is local-only and operator-attended. It must be an
|
||||
exact URI in config, not a wildcard or dynamic registration exception.
|
||||
- Browser-facing Authelia redirects must use the public Authelia base URL
|
||||
(`https://auth.coulomb.social`) so the human login page opens correctly.
|
||||
- KeyCape may still need an internal service URL for back-channel token
|
||||
exchange. If so, split the current single Authelia URL into browser-facing
|
||||
authorize URL and internal token URL instead of making the browser use an
|
||||
in-cluster hostname.
|
||||
- The MFA prompt should collect only a one-time code, post it back to KeyCape,
|
||||
validate with privacyIDEA, and then continue the normal OIDC code flow.
|
||||
- This work unblocks the NetKingdom custody gate in
|
||||
`NET-WP-0015-platform-root-custody-and-openbao-identity-bootstrap`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
**2026-05-24:** Implemented in source:
|
||||
|
||||
- added `netkingdom-bootstrap-console` as a public OIDC client in the sample
|
||||
KeyCape config, while keeping the local callback registered on `demo-app`
|
||||
for compatibility,
|
||||
- split Authelia browser redirects from server-side token exchange via
|
||||
`browserBaseURL` and `tokenBaseURL`,
|
||||
- added a browser MFA challenge page at `POST /authorize/callback` that
|
||||
validates the one-time code with privacyIDEA before issuing the downstream
|
||||
OIDC authorization code,
|
||||
- updated NetKingdom's `keycape-config` generation template and bootstrap
|
||||
console to use the dedicated client,
|
||||
- added regression tests for callback registration, split Authelia URLs, MFA
|
||||
challenge rendering, valid OTP continuation, and invalid OTP failure.
|
||||
|
||||
Live use still requires deployment: build/publish the updated KeyCape image,
|
||||
refresh the live `keycape-config` Secret through the custodian age-key unlock
|
||||
ceremony, and restart the KeyCape deployment.
|
||||
|
||||
---
|
||||
|
||||
## T01 - Register the bootstrap console callback client
|
||||
|
||||
```task
|
||||
id: KEY-WP-0003-T01
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "b396c99f-d711-475a-9cba-4f03a1db561d"
|
||||
```
|
||||
|
||||
Add a KeyCape client registration for the bootstrap console. Either create a
|
||||
dedicated `netkingdom-bootstrap-console` public client or update `demo-app`
|
||||
temporarily with these exact redirect URIs:
|
||||
|
||||
- `http://127.0.0.1:8876/oidc/callback`
|
||||
- `http://localhost:8876/oidc/callback`
|
||||
|
||||
Update the sample config, tests, and deployment/runbook references so the
|
||||
registered client is reproducible and not just a live-cluster patch.
|
||||
|
||||
Gate: an authorize request using the local callback no longer returns
|
||||
`invalid_profile_usage` for `redirect_uri`.
|
||||
|
||||
## T02 - Separate browser-facing and internal Authelia URLs if needed
|
||||
|
||||
```task
|
||||
id: KEY-WP-0003-T02
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "46172e6d-3e11-493c-b223-79c2fc321aec"
|
||||
```
|
||||
|
||||
Confirm whether the current `authelia.baseURL` is safe to use for both browser
|
||||
redirects and server-side token exchange. If not, add explicit configuration
|
||||
for the browser authorize base URL and internal token/userinfo base URL.
|
||||
|
||||
Gate: the first browser redirect leaves `https://kc.coulomb.social` for
|
||||
`https://auth.coulomb.social/...`; server-side token exchange still works from
|
||||
inside the deployment.
|
||||
|
||||
## T03 - Add a browser MFA challenge step
|
||||
|
||||
```task
|
||||
id: KEY-WP-0003-T03
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "92fca4d0-6215-4ea6-9f80-9178ae183acb"
|
||||
```
|
||||
|
||||
When `CheckMFARequired` returns true after the Authelia callback, render a
|
||||
minimal KeyCape MFA challenge page instead of requiring `mfa_token` in the
|
||||
callback query string. The page should:
|
||||
|
||||
- show the authenticated username and client display name,
|
||||
- collect only the OTP code,
|
||||
- preserve the pending OIDC state server-side,
|
||||
- validate with privacyIDEA,
|
||||
- continue to issue the normal authorization code on success,
|
||||
- fail closed with the existing telemetry on invalid MFA.
|
||||
|
||||
Gate: a user enrolled in privacyIDEA can complete password + OTP in the
|
||||
browser and is returned to the registered downstream callback.
|
||||
|
||||
## T04 - Add end-to-end profile tests for the bootstrap login path
|
||||
|
||||
```task
|
||||
id: KEY-WP-0003-T04
|
||||
status: done
|
||||
priority: medium
|
||||
state_hub_task_id: "079a5929-1864-4461-a64c-746cebca469d"
|
||||
```
|
||||
|
||||
Add tests that cover:
|
||||
|
||||
- local bootstrap callback registration,
|
||||
- rejection of unregistered callbacks remains intact,
|
||||
- Authelia browser redirect uses the expected public URL,
|
||||
- MFA-required login presents a challenge instead of immediate failure,
|
||||
- invalid OTP fails closed,
|
||||
- valid OTP produces an authorization code bound to the original PKCE session.
|
||||
|
||||
Gate: `make test` passes and the negative redirect URI tests remain green.
|
||||
|
||||
## T05 - Document the live rollout ceremony
|
||||
|
||||
```task
|
||||
id: KEY-WP-0003-T05
|
||||
status: done
|
||||
priority: medium
|
||||
state_hub_task_id: "1d67225d-a20b-4e36-9b2e-20836be2f439"
|
||||
```
|
||||
|
||||
Document the deployment path for updating live KeyCape config without
|
||||
regenerating unrelated secrets. The runbook must fit the NetKingdom custodian
|
||||
age-key model: decrypt or unlock only during an attended ceremony, apply the
|
||||
updated client registration/config, restart KeyCape, and remove plaintext
|
||||
secret material afterward.
|
||||
|
||||
Gate: an operator can update the live `keycape-config` Secret and verify the
|
||||
bootstrap console OIDC login without printing or committing secrets.
|
||||
|
|
@ -0,0 +1,332 @@
|
|||
---
|
||||
id: KEY-WP-0004
|
||||
type: workplan
|
||||
title: "Binky Hedgehog GmbH as first NetKingdom tenant + qonto runtime workload identity"
|
||||
domain: infotech
|
||||
repo: key-cape
|
||||
status: finished
|
||||
owner: codex
|
||||
topic_slug: netkingdom
|
||||
created: "2026-07-23"
|
||||
updated: "2026-07-29"
|
||||
state_hub_workstream_id: "817709be-5756-5ef7-9ccd-dcb84b6b9c64"
|
||||
---
|
||||
|
||||
# Binky Hedgehog GmbH as first NetKingdom tenant + qonto runtime workload identity
|
||||
|
||||
Binky Hedgehog GmbH ("binky") becomes the **first real tenant** provisioned in
|
||||
the NetKingdom IAM stack, per the NetKingdom IAM Profile v0.2's tenant claim
|
||||
model (`net-kingdom/canon/standards/iam-profile_v0.2.md` §"Tenant Claim") —
|
||||
distinct from `tenant:platform` (control plane) and `tenant:coulomb` (the
|
||||
ecosystem-development tenant this workstation already operates under).
|
||||
|
||||
This closes **Gap #1** flagged in `qonto-assistant`'s
|
||||
`workplans/QONTO-WP-0003-mcp-surface.md` closure: no OIDC issuer existed
|
||||
anywhere in the fleet, so `qonto-assistant`'s MCP surface shipped with an
|
||||
interim shared-secret bearer token instead of real workload identity.
|
||||
`key-cape` is "Implementation complete (v0.1)" and already implements the
|
||||
profile's `client_credentials`/workload-identity grant for service accounts
|
||||
— it's the component that should close this gap, not `user-engine`
|
||||
(downstream user/profile data layer, not an identity provider) or
|
||||
`identity-canon` (terminology research only).
|
||||
|
||||
**Depends on:** `key-cape` v0.1 (done, `KEY-WP-0001`), NetKingdom IAM Profile
|
||||
v0.2 tenant + service-account claim rules (already ratified canon — no new
|
||||
profile work needed, this is provisioning against an existing contract), and
|
||||
the cross-repo runtime direction now tracked in
|
||||
`railiance-master/workplans/RMASTER-WP-0019-knative-qonto-runtime-on-reef-railiance.md`.
|
||||
|
||||
This workplan no longer targets a permanent direct `qonto-assistant`
|
||||
deployment shape. Its workload-identity lane is intended for the future
|
||||
`rapp-qonto` runtime on `rail-knative` hosted by `reef-railiance`, with
|
||||
current `qonto-assistant` consumption allowed only as a migration bridge until
|
||||
that package/rail path exists.
|
||||
|
||||
## Task: Confirm tenant identifier and provision the tenant plane
|
||||
|
||||
```task
|
||||
id: KEY-WP-0004-T01
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "c3bdbdb6-6b3f-51b5-8521-d67f113f1c3d"
|
||||
```
|
||||
|
||||
**Decided 2026-07-23 (Bernd):** the IAM profile's `tenant:customer:<name>` /
|
||||
`tenant:sandbox:<name>` suggested identifiers name a tenant by **capability
|
||||
role** (customer, vendor, ...) — but tenants already known to hold multiple
|
||||
roles at once (see `net-kingdom/docs/princedom-isolation-exploration.md`'s
|
||||
unratified `PLTF`/`IAM`/`VEN`/`CUS` role model; Binky itself is both `CUS`
|
||||
and, later, `VEN`). Using a role word as the tenant *grouping* would collide
|
||||
with that separate role dimension. Decision: classify tenants by an
|
||||
onboarding-risk / entity-shape **grouping**, orthogonal to capability role:
|
||||
|
||||
```text
|
||||
trial — test/trial/showcase tenants only
|
||||
friendly — known, easily reached, tolerant of experimentation/instability
|
||||
single — one-person business entities (freelance consultants)
|
||||
small — up to 10 employees at time of onboarding (attoo)
|
||||
medium — up to 100 employees (attoo)
|
||||
large — up to 1000 employees (attoo)
|
||||
enterprise — 1001+ employees (attoo)
|
||||
consumer — private individuals
|
||||
family — a legal family
|
||||
community — a non-legal group of people
|
||||
association — a legal association of people
|
||||
agentic — financially enabled AI entities
|
||||
```
|
||||
|
||||
`tenant:<grouping>:<name>` replaces `tenant:customer:<name>` /
|
||||
`tenant:sandbox:<name>` as the identifier shape. **Binky maps to `friendly`**
|
||||
— known, reachable, tolerant of early instability — giving
|
||||
**`tenant:friendly:binky`** (slug matches the OpenBao path already in
|
||||
production, `tenants/binky/qonto-api`).
|
||||
|
||||
**Open question, not yet resolved by this decision:** this grouping list
|
||||
classifies tenants as external-shaped entities: it has no obvious slot for
|
||||
`tenant:coulomb` itself (the platform's own internal/reference tenant,
|
||||
ADR-0006) — none of `trial`/`friendly`/`single`/.../`agentic` fit "the
|
||||
platform building itself." Confirm with Bernd whether `tenant:coulomb` stays
|
||||
an ungrouped special case (alongside `tenant:platform`) or gets folded into
|
||||
this scheme somehow, before treating the grouping list as complete.
|
||||
|
||||
**Also open:** this is a fleet-wide vocabulary decision — the ratified
|
||||
`net-kingdom/canon/standards/iam-profile_v0.2.md` "Tenant Claim" section
|
||||
still documents the old `customer`/`sandbox` suggested identifiers, and
|
||||
NetKingdom owns that canon per ADR-0011. Recording this grouping list only
|
||||
in this key-cape workplan lets it drift from canon. Recommend a follow-up:
|
||||
either a decision record or an ADR in `net-kingdom` adopting this grouping
|
||||
list as the tenant-identifier standard, superseding the old suggested
|
||||
identifiers. Not drafted here — flagging so it isn't lost.
|
||||
|
||||
Provision whatever tenant/org-unit boundary key-cape's current backend
|
||||
(LLDAP/Authelia) needs so tokens issued for subjects under this tenant
|
||||
correctly carry `tenant:friendly:binky` and nothing implies platform-root
|
||||
authority for it (profile rule: tenant administration for a tenant must
|
||||
never imply platform-root authority).
|
||||
|
||||
Done when: `tenant:coulomb`'s place in the grouping scheme is resolved; the
|
||||
`net-kingdom` canon-update question above is answered (drafted or explicitly
|
||||
deferred); a test-subject token carries the `tenant:friendly:binky` claim.
|
||||
|
||||
2026-07-27: IAM Profile v0.2 now preserves `tenant:platform` and
|
||||
`tenant:coulomb` as reserved ungrouped tenants and ratifies the external
|
||||
grouping vocabulary. The live service-token proof carries
|
||||
`tenant:friendly:binky` without platform authority.
|
||||
|
||||
## Task: Provision bernd.worsch@binky-hedgehog.com as tenant-admin
|
||||
|
||||
```task
|
||||
id: KEY-WP-0004-T02
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "202c419e-838f-5c2f-8a52-9b1c36a6d6b9"
|
||||
```
|
||||
|
||||
Create the user in key-cape's current backend, enroll MFA per profile
|
||||
requirements (`assurance` claim, `net-kingdom/canon/standards/iam-profile_v0.2.md`),
|
||||
and assign a tenant-admin role/group scoped to `tenant:friendly:binky` only.
|
||||
|
||||
2026-07-27 direction update: do not complete this through a one-shot operator
|
||||
script. `USER-WP-0020` and `NK-WP-0023` now own a reusable self-service and
|
||||
administration portal plus NetKingdom provisioning adapters. This task is
|
||||
their first production acceptance case.
|
||||
|
||||
The `binky-hedgehog.com` mailbox must exist and be reachable for account
|
||||
verification, but mailbox availability is now an input to the reusable
|
||||
registration flow rather than a manual provisioning procedure.
|
||||
|
||||
Done when: `bernd.worsch@binky-hedgehog.com` completes OIDC/PKCE + MFA login
|
||||
and receives a token carrying `tenant:friendly:binky` and a tenant-admin role
|
||||
claim; the same token does **not** grant access to `tenant:platform` or any
|
||||
other tenant.
|
||||
|
||||
2026-07-28: user-engine's deployed portal API created the tenant-admin
|
||||
membership and provisioned/linked the LLDAP identity through the reusable
|
||||
NetKingdom adapter. The identity is intentionally in
|
||||
`password_setup_required`; LLDAP has no SMTP configuration for its native
|
||||
reset delivery. First-password handoff, MFA enrollment, and final human-token
|
||||
claim/denial evidence remain. No operator-set password or raw credential was
|
||||
used as a shortcut.
|
||||
|
||||
2026-07-28 update: the reusable password handoff is now live at the canonical
|
||||
KeyCape host and is issued/renewed through the user-engine administration UI.
|
||||
Links are opaque, expire after 15 minutes, are single use, and older links are
|
||||
revoked on renewal. A disposable-user live test proved password registration,
|
||||
directory login, replay denial, and cleanup. This task now awaits only the
|
||||
Binky user's own password choice, MFA enrollment, and final scoped-token
|
||||
acceptance.
|
||||
|
||||
2026-07-29: the first portal attempt correctly denied cross-tenant access
|
||||
because KeyCape had not projected the established `net-kingdom-admins` group
|
||||
to the new platform-operator role. Commit `90a2078` adds and tests the explicit
|
||||
`tenant:platform` / `platform-operator` compatibility mapping and is deployed
|
||||
Ready. The operator must start a fresh OIDC session so the corrected claims
|
||||
are minted.
|
||||
|
||||
2026-07-29 human acceptance: the Binky administrator completed the reusable
|
||||
password setup, enrolled a privacyIDEA TOTP factor, and successfully signed in
|
||||
through KeyCape with password plus OTP. Enrollment revealed that privacyIDEA's
|
||||
default QR label exposed only the token serial. The live
|
||||
`coulomb-friendly-token-labels` enrollment policy now emits issuer `Coulomb`
|
||||
and label `{user}@{realm}` for future tokens; existing authenticator entries
|
||||
must be renamed locally because wallets do not accept remote label updates.
|
||||
The live token then carried `tenant:friendly:binky`, roles `user` and
|
||||
`tenant-admin`, only the Binky users/admins groups, and AAL2 assurance with
|
||||
`pwd` + `otp` and `mfa: true`. The same session was denied access to both
|
||||
`tenant:platform` and `tenant:coulomb` administration. Acceptance complete.
|
||||
|
||||
## Task: Register a workload-identity OIDC client for the qonto runtime
|
||||
|
||||
```task
|
||||
id: KEY-WP-0004-T03
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "0d3ccd35-bad5-5c30-95a3-61fce584425d"
|
||||
```
|
||||
|
||||
Register an OIDC client per the profile's service-account/workload-identity
|
||||
flow (`client_credentials` or the documented workload-token-exchange
|
||||
equivalent — §"Service Account Flow"), scoped to `tenant:friendly:binky`,
|
||||
with the minimum role/scope the qonto runtime surface actually needs. The
|
||||
intended steady-state consumer is the future `rapp-qonto` runtime on
|
||||
`rail-knative`; current `qonto-assistant` callers may use the same lane only
|
||||
as a documented migration bridge until that package split exists. No
|
||||
interactive login for this client — it's for agent-harness sessions and other
|
||||
automated callers, not humans.
|
||||
|
||||
The resulting `client_secret` must never be pasted into chat, Git, this
|
||||
workplan, or a State Hub message or progress event — hand-off happens only
|
||||
through T04's secrets-engine/OpenBao lane.
|
||||
|
||||
Done when: a `client_credentials` token exchange against key-cape succeeds
|
||||
for this client and produces a short-lived service token carrying
|
||||
`tenant:friendly:binky`; the `client_id`, granted scopes, and intended future
|
||||
binding to `rapp-qonto` are documented here; no secret material appears
|
||||
anywhere in this repo's history.
|
||||
|
||||
2026-07-26: Added `docs/qonto-runtime-identity-contract.md` with stable
|
||||
principal/client names, least-privilege claims, non-secret evidence, and the
|
||||
automation/custody boundary. Actual client issuance remains open.
|
||||
|
||||
2026-07-27: `KEY-WP-0006` delivered `rapp-qonto-client`. Live exchange
|
||||
produced only the documented tenant, role, and `qonto:read` scope; wrong
|
||||
secret and excessive scope were denied.
|
||||
|
||||
## Task: Route client_secret custody through secrets-engine + OpenBao
|
||||
|
||||
```task
|
||||
id: KEY-WP-0004-T04
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "9cb09dbf-1fd0-55c5-9c45-b3b7296ecd91"
|
||||
```
|
||||
|
||||
Coordinate with `secrets-engine` to create a scoped OpenBao lane for this
|
||||
`client_secret`, mirroring the existing `tenants/binky/qonto-api` pattern
|
||||
already in production use for the Qonto bank credential itself. The steady
|
||||
target is a dedicated future runtime role for `rapp-qonto`; if a temporary
|
||||
bridge via the current `qonto-assistant` runtime role is needed before the
|
||||
package split lands, it must be recorded as temporary rather than treated as
|
||||
the final contract.
|
||||
|
||||
Done when: the intended qonto runtime role can fetch the `client_secret` via
|
||||
`secrets-engine`/OpenBao; any temporary `qonto-assistant` bridge is explicitly
|
||||
marked transitional; Bernd never handles the raw value at any point in this
|
||||
flow.
|
||||
|
||||
2026-07-26: `warden plan` classified first-time provisioning as one explicit
|
||||
founder authority act through `warden desk`. No secret was requested or read.
|
||||
All repeatable verification and revocation work is designated for sanctioned
|
||||
agent execution and later functional automation.
|
||||
|
||||
2026-07-27: Custody is established at
|
||||
`platform/workloads/rapp-qonto/keycape-client`, with runtime injection through
|
||||
`sso/keycape-rapp-qonto-client`. No raw value entered evidence or human
|
||||
handling.
|
||||
|
||||
## Task: Register the credential lane with ops-warden
|
||||
|
||||
```task
|
||||
id: KEY-WP-0004-T05
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "39f1f803-732e-5905-97d9-c7493c66c8ee"
|
||||
```
|
||||
|
||||
This is the task that directly minimizes Bernd's ongoing engagement: add an
|
||||
entry to ops-warden's routing catalog (`registry/routing/catalog.yaml`)
|
||||
pointing at this new OIDC client / login lane, following the pattern already
|
||||
documented in `ops-warden/SCOPE.md`'s "Issue vs route" table ("Login / OIDC /
|
||||
MFA → key-cape / Keycloak → Assist — route; proxy `login` lane when
|
||||
`exec_capable`"). ops-warden does not mint tokens or take custody here — it
|
||||
routes/assists, same as every other credential lane it fronts. The lane should
|
||||
describe the future `rapp-qonto` runtime path on `rail-knative`, not only the
|
||||
current source repo.
|
||||
|
||||
Once the OpenBao lane from T04 exists, run ops-warden's workload security
|
||||
posture conformance checker
|
||||
(`ops-warden/scripts/check_secret_posture_conformance.py`) against the qonto
|
||||
runtime workload that will later become `rapp-qonto`; until the split lands,
|
||||
`qonto-assistant` may be used as the migration subject if that temporary state
|
||||
is recorded. Confirm it meets the expected maturity tier (dev/test/prod
|
||||
posture, M0–M3 workload maturity) before this goes live with real
|
||||
credentials, and record the result (pass or documented exception).
|
||||
|
||||
Done when: `warden route show` (or equivalent) resolves this lane correctly;
|
||||
posture conformance check passes or exceptions are explicitly recorded; from
|
||||
this point on, discovering and using this credential lane needs `warden
|
||||
access`, not direct key-cape/OpenBao admin knowledge.
|
||||
|
||||
2026-07-26: Routing requirements and required conformance evidence are now
|
||||
documented. Catalog publication waits for the real client and OpenBao role so
|
||||
the route does not advertise an unusable lane.
|
||||
|
||||
2026-07-27: Published route `rapp-qonto-keycape-client`; route resolution and
|
||||
the metadata-only M3/prod posture check both pass.
|
||||
|
||||
## Task: automation@binky-hedgehog.com service identity (deferred)
|
||||
|
||||
```task
|
||||
id: KEY-WP-0004-T06
|
||||
status: cancel
|
||||
priority: low
|
||||
state_hub_task_id: "fbee11fc-7a60-5dd7-8522-f80ddb4aea55"
|
||||
```
|
||||
|
||||
Forward-looking per Bernd's framing ("I guess it will later be helpful") —
|
||||
**do not provision until a concrete automation workflow actually needs its
|
||||
own identity** distinct from the qonto runtime workload client from T03.
|
||||
Dormant service-account credentials are themselves a posture risk (unused
|
||||
attack surface, nothing to rotate against), so this stays parked rather than
|
||||
front-loaded.
|
||||
|
||||
When a real consuming workflow exists: same pattern as T02 but scoped to an
|
||||
automation-appropriate role (not tenant-admin), likely paired with its own
|
||||
`client_credentials` registration for whichever agent/service authenticates
|
||||
as it, following T03/T04/T05's pattern rather than inventing a new one.
|
||||
|
||||
Done when: explicitly triggered by a named consuming workflow — until then
|
||||
this task stays `todo` and does not block closing the rest of this workplan.
|
||||
|
||||
## Task: Closure review
|
||||
|
||||
```task
|
||||
id: KEY-WP-0004-T07
|
||||
status: done
|
||||
priority: low
|
||||
state_hub_task_id: "77b0556f-62fd-5046-9ba1-718246151a6f"
|
||||
```
|
||||
|
||||
Mark workplan finished when T01–T05 are done (T06 may legitimately remain
|
||||
`todo`/parked by design — see its own task note). Confirm end to end: a
|
||||
human token (bernd.worsch@binky-hedgehog.com, tenant-admin) and a service
|
||||
token (future qonto runtime client, with any temporary `qonto-assistant`
|
||||
bridge explicitly recorded) both resolve to `tenant:friendly:binky` and
|
||||
nothing else; the credential lane is discoverable via `warden access` without
|
||||
Bernd touching key-cape/OpenBao admin surfaces directly. The runtime follow-up
|
||||
then continues in `QONTO-WP-0004` and `RMASTER-WP-0019`. Run
|
||||
`statehub fix-consistency`.
|
||||
|
||||
2026-07-29 closure: T01–T05 are complete, T06 is intentionally cancelled,
|
||||
the human Binky flow passed AAL2 and tenant-isolation acceptance, and the qonto
|
||||
workload client passed its positive-scope and negative credential/scope checks.
|
||||
|
|
@ -0,0 +1,245 @@
|
|||
---
|
||||
id: KEY-WP-0005
|
||||
type: workplan
|
||||
title: "IAM Profile core claims: tenant, principal_type, groups, roles, assurance, tenant_roles"
|
||||
domain: infotech
|
||||
repo: key-cape
|
||||
status: finished
|
||||
owner: codex
|
||||
topic_slug: netkingdom
|
||||
created: "2026-07-23"
|
||||
updated: "2026-07-23"
|
||||
state_hub_workstream_id: "711a8303-69aa-5db6-9043-92530c6dc81b"
|
||||
---
|
||||
|
||||
# IAM Profile core claims: tenant, principal_type, groups, roles, assurance, tenant_roles
|
||||
|
||||
Discovered while planning `tenant-engine/workplans/TEN-WP-0003-flex-auth-write-authorizer.md`:
|
||||
`key-cape`'s token issuance (`src/internal/server/oidc/token.go`) does not
|
||||
currently emit `tenant`, `principal_type`, `groups`, `roles`, or
|
||||
`assurance` — none of `net-kingdom/canon/standards/iam-profile_v0.3.md`'s
|
||||
Core Claims are implemented yet, only `preferred_username`/`email`/`groups`
|
||||
(LDAP-sourced, not the profile's normalized `groups` claim) and OIDC's own
|
||||
base claims (`nonce`, etc.).
|
||||
|
||||
This is bigger than "add the `tenant_roles` claim on top of existing tenant
|
||||
support" — `key-cape` isn't yet profile-conformant at all on the tenant/
|
||||
principal/assurance axis. `tenant_roles` (the reason this workplan exists)
|
||||
is the last, optional claim in that same set (v0.3's addition on top of
|
||||
v0.2's baseline) — implementing it in isolation without the claims it
|
||||
depends on for meaning (`tenant`, `principal_type`) would be incoherent.
|
||||
|
||||
**This workplan is registered, not implemented, by the same session that
|
||||
found the gap.** Token issuance is security-sensitive code in a Go codebase
|
||||
with its own adapter conventions (`internal/adapters/{lldap,privacyidea,authelia}`)
|
||||
that deserves dedicated context before changing, not a rushed edit from a
|
||||
first read of `token.go`. Scoped and sequenced here so the work isn't lost.
|
||||
|
||||
**Depends on:** `net-kingdom/canon/standards/iam-profile_v0.3.md` (ratified).
|
||||
**Related:** `tenant-engine/workplans/TEN-WP-0003-flex-auth-write-authorizer.md`
|
||||
(the consumer waiting on `tenant_roles`); `key-cape/workplans/KEY-WP-0004-binky-hedgehog-tenant-onboarding.md`
|
||||
(Binky's onboarding, which needs `tenant` claim support to produce a
|
||||
correct `tenant:friendly:binky` token at all).
|
||||
|
||||
## Task: Emit `tenant`, `principal_type`, `groups`, `roles`, `assurance`
|
||||
|
||||
```task
|
||||
id: KEY-WP-0005-T01
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "32d5fbdc-cd2b-5bc8-9d2b-1a410b55e806"
|
||||
```
|
||||
|
||||
Bring `token.go`'s claim assembly up to `iam-profile_v0.3.md`'s Core Claims
|
||||
table baseline (the v0.2-originated set):
|
||||
|
||||
- `tenant`: resolve from whatever user/service/agent-to-tenant mapping
|
||||
`key-cape`'s LLDAP/Authelia backend can provide today; if no such mapping
|
||||
exists yet, that's itself a sub-task — this claim cannot be stamped from
|
||||
nothing.
|
||||
- `principal_type`: `human` for the existing Authorization Code + PKCE flow;
|
||||
`service` for client-credentials flows (check whether `key-cape` already
|
||||
issues client-credentials tokens at all — `grant_types_supported` in the
|
||||
discovery contract claims it does, but that needs verifying against real
|
||||
code, the same way this workplan verified `tenant` claim absence).
|
||||
- `groups`, `roles`: normalize from LDAP group/role data into the profile's
|
||||
flat string-array shape.
|
||||
- `assurance`: `{level, methods, mfa, source, at}` per the profile —
|
||||
`privacyIDEA`-backed MFA evidence maps to `aal2`; single-factor maps to
|
||||
`aal1`.
|
||||
|
||||
Done when: `tools/iam-profile-conformance/` (already exists) passes against
|
||||
these claims for both human and service token issuance paths.
|
||||
|
||||
**Done 2026-07-24, human flow only — scope split made explicit, not
|
||||
silently narrowed:** verified first (as flagged): `grant_types_supported`
|
||||
advertises `client_credentials` in `discovery.go`, but `token.go`'s
|
||||
`ServeHTTP` only ever accepted `grant_type == "authorization_code"` — there
|
||||
is no service-token issuance path at all, so "verifying against service
|
||||
token issuance" isn't possible until that path exists. Building a
|
||||
`client_credentials` grant handler from scratch is a distinct, materially
|
||||
larger piece of work than extending the existing human flow (new endpoint
|
||||
logic, new client-authentication semantics, no existing tests to extend)
|
||||
— **not attempted in this pass**, left open below rather than declared done.
|
||||
|
||||
What shipped for the human Authorization Code + PKCE flow:
|
||||
|
||||
- `domain.User.Tenant` (new field, `omitempty`, empty-safe for existing
|
||||
YAML configs) + `token.go`'s `effectiveTenant()`: falls back to
|
||||
`tenant:coulomb` (this workstation's actual current tenant, ADR-0006)
|
||||
when unset — never emits an empty `tenant` claim, never silently assigns
|
||||
a user to a tenant they weren't given.
|
||||
- `principal_type: "human"`, unconditional.
|
||||
- `groups`/`roles`: promoted from scope-gated (only `groups`, only with the
|
||||
`groups` scope) to unconditional core claims, always present as `[]`
|
||||
when empty, never `null` (`nonNilStrings()`). One pre-existing test
|
||||
asserted the old scope-gated behavior for `groups`
|
||||
(`TestTokenHandler_ScopeFiltering_ProfileScope`) — updated to assert the
|
||||
new, intentional behavior, not silently left failing or reverted.
|
||||
- `assurance`: built from whether MFA was *actually verified in this
|
||||
session* (`PKCESession.MFAVerified`, new field, threaded through
|
||||
`completeAuthorization`'s two call sites in `authorize.go`), not from
|
||||
static enrollment state — `aal2` only when MFA was required and passed
|
||||
this time, `aal1` otherwise.
|
||||
|
||||
Verified for real: `go build ./...` and `go vet ./...` clean; `go test
|
||||
./...` green across the whole repo (one pre-existing test updated to match
|
||||
the new intentional behavior, described above, not silently broken). Two
|
||||
new `authorize_test.go` cases directly assert `PKCESession.MFAVerified` is
|
||||
set correctly on both the MFA-required and MFA-not-required paths.
|
||||
`tests/profile/profile_test.go`'s `TestCompleteTokenFlow` — the repo's own
|
||||
full HTTP integration test (real server, real PKCE flow, real JWT decode)
|
||||
— extended with real assertions for all five new/changed claims, not just
|
||||
presence checks: `tenant == "tenant:coulomb"` (the fallback path, since
|
||||
the test's `testuser` has no explicit tenant), `principal_type == "human"`,
|
||||
`groups == ["developers"]`, `roles == []`, and
|
||||
`assurance == {level: "aal1", mfa: false, source: "key-cape", ...}` (this
|
||||
test's `mockMFA` never requires MFA, so `aal1` is the correct expected
|
||||
value, not `aal2`).
|
||||
|
||||
Not run: `net-kingdom/tools/iam-profile-conformance`'s Python conformance
|
||||
tool against a live minted token — it needs a reachable issuer, which would
|
||||
mean standing up the full `docker-compose.dev.yml` stack
|
||||
(Authelia+LLDAP+privacyIDEA) beyond what's practical in this pass. The
|
||||
equivalent claim-shape and value checks it would perform are covered by
|
||||
`TestCompleteTokenFlow`'s real HTTP round trip instead — not a like-for-like
|
||||
substitute, but real coverage, not an assumption.
|
||||
|
||||
**Explicitly still open** (not this task, not silently dropped):
|
||||
`client_credentials` grant handling — service-token issuance for `key-cape`
|
||||
callers with no human in the loop (`tenant-engine`'s own eventual key-cape
|
||||
integration, and any other service caller). Needs its own scoped follow-up:
|
||||
a new `principal_type: "service"` code path in `token.go` (or a sibling
|
||||
handler), client secret/authentication semantics, and its own test suite —
|
||||
this workplan's `T02` (`tenant_roles`) doesn't strictly require it, since
|
||||
`tenant_roles` attaches to whatever principal_type a token already carries,
|
||||
human included.
|
||||
|
||||
## Task: Emit `tenant_roles` (optional, cached)
|
||||
|
||||
```task
|
||||
id: KEY-WP-0005-T02
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "c3ee6021-b28d-50a5-be60-ecb5631f56e9"
|
||||
```
|
||||
|
||||
At token-issuance time, call `tenant-engine`'s cache-read endpoint
|
||||
(`GET /tenants/{tenant_id}/roles`) using the `tenant` claim resolved in T01,
|
||||
and stamp the result as the optional `tenant_roles` claim
|
||||
(`iam-profile_v0.3.md`, "Tenant Roles" section). Follows the same adapter
|
||||
pattern as `internal/adapters/{lldap,privacyidea,authelia}` — a new
|
||||
`internal/adapters/tenantengine` package, not inline HTTP calls in
|
||||
`token.go`.
|
||||
|
||||
Fail open on this claim specifically, not closed: `tenant_roles` is
|
||||
documented as a cache callers must not trust for privileged decisions
|
||||
anyway (`flex-auth` re-validates live). If `tenant-engine` is unreachable at
|
||||
issuance time, omit the claim rather than fail the entire token issuance —
|
||||
losing a performance optimization is acceptable; blocking login because a
|
||||
cache source is down is not. This is the one place in the whole `tenant_roles`
|
||||
design where fail-open is correct, precisely because the live-lookup path
|
||||
exists as the real gate.
|
||||
|
||||
Done when: token issuance still succeeds with `tenant_roles` omitted when
|
||||
`tenant-engine` is unreachable (test simulates the outage); present and
|
||||
correct when reachable, for a tenant with known role grants.
|
||||
|
||||
**Done 2026-07-24:** New `internal/adapters/tenantengine` package
|
||||
(`Client.Roles(ctx, tenantID) ([]string, bool)`), mirroring
|
||||
`internal/adapters/{lldap,privacyidea,authelia}`'s shape exactly. Fails
|
||||
open by construction — unreachable, non-200, or malformed body all return
|
||||
`(nil, false)`, never an error the caller has to specially handle; a `nil
|
||||
*Client` also fails open safely rather than panicking, so `TokenHandler`
|
||||
doesn't need a separate "is this configured" branch. Wired into
|
||||
`TokenHandler.TenantEngine` (nil by default — existing tests and call
|
||||
sites are unaffected); `token.go` stamps `tenant_roles` only when `ok`.
|
||||
|
||||
7 adapter-level tests (success, non-200, malformed body, connection
|
||||
failure, timeout, nil client, empty base URL). 3 new `token_test.go` cases
|
||||
prove the actual required behavior end-to-end through `TokenHandler`, not
|
||||
just at the adapter layer: present and correct when reachable; **token
|
||||
issuance still returns 200 with every other required core claim intact**
|
||||
when unreachable, `tenant_roles` simply absent (the literal done-criteria);
|
||||
absent when `TenantEngine` isn't configured at all. `go build`/`vet`
|
||||
clean, `go test ./...` green repo-wide.
|
||||
|
||||
**A real bug found and fixed along the way, not worked around:** the first
|
||||
live cross-process check (real `flex-auth`, real `tenant-engine`, this
|
||||
adapter) returned `tenant_not_found` for a tenant that genuinely existed.
|
||||
`tenant-engine`'s `GET /tenants/{tenant_id}/roles` was keyed by its
|
||||
internal `tenant_id`, but `key-cape` (like any external caller) only ever
|
||||
has the tenant's profile *identifier* (the `tenant` claim value) — it has
|
||||
no way to know tenant-engine's internal id. Fixed at the source:
|
||||
`tenant-engine/workplans/ADHOC-2026-07-24.md` (identifier-or-id resolution
|
||||
added to `InMemoryTenantStore`), re-verified with the exact same live
|
||||
three-process chain afterward — `roles=[IAM] ok=true`, resolved by
|
||||
identifier. This is exactly why T02 insisted on a live-server-reachable
|
||||
test rather than only an `httptest` fake: the fake would have used
|
||||
whatever id shape the test author happened to pick and never caught this.
|
||||
|
||||
## Task: Closure review
|
||||
|
||||
```task
|
||||
id: KEY-WP-0005-T03
|
||||
status: done
|
||||
priority: low
|
||||
state_hub_task_id: "c88a3457-2964-5c44-8220-76f82df3a753"
|
||||
```
|
||||
|
||||
Confirm T01–T02 done; conformance suite green for both tasks together.
|
||||
Note in closure: this unblocks `KEY-WP-0004`'s Binky onboarding actually
|
||||
producing conformant tokens, and unblocks `flex-auth`'s side of the
|
||||
`tenant_roles` picture receiving a real claim to reason about (though
|
||||
`flex-auth`'s live-lookup path doesn't depend on this claim existing — only
|
||||
the cache-read/performance path does). Run `statehub fix-consistency`.
|
||||
|
||||
**Closed 2026-07-24.** T01–T02 done. `go build ./...`, `go vet ./...` clean;
|
||||
`go test ./...` green across the whole repo. Python
|
||||
`net-kingdom/tools/iam-profile-conformance` not run against a live instance
|
||||
(needs the full Authelia+LLDAP+privacyIDEA stack, impractical in this
|
||||
pass) — real coverage instead came from `tests/profile`'s own full HTTP
|
||||
integration test (`TestCompleteTokenFlow`) with genuine claim-value
|
||||
assertions, and from three separate live cross-process checks against a
|
||||
real `tenant-engine` (one of which found and fixed a real bug in
|
||||
`tenant-engine` itself, `ADHOC-2026-07-24`).
|
||||
|
||||
**What this closes:** `key-cape`'s human Authorization Code + PKCE flow now
|
||||
emits every IAM Profile v0.3 core claim (`tenant`, `principal_type`,
|
||||
`groups`, `roles`, `assurance`) plus the optional `tenant_roles` cache,
|
||||
correctly sourced from a real `assurance` signal (MFA actually verified
|
||||
this session, not static enrollment) and a real `tenant-engine` call
|
||||
(fail-open, never blocking login). `KEY-WP-0004`'s Binky onboarding can now
|
||||
produce conformant tokens for human logins.
|
||||
|
||||
**What stays explicitly open, not silently dropped:**
|
||||
- `client_credentials` / service-token issuance — no such flow exists in
|
||||
`token.go` at all yet (confirmed in T01); needed before any *service*
|
||||
caller (not a human) can get a token with `principal_type: "service"` and
|
||||
its own `tenant`/`tenant_roles`. A materially larger, separate piece of
|
||||
work than either task in this workplan.
|
||||
- `flex-auth`'s side of `tenant_roles` (`FLEX-WP-0008`, already closed
|
||||
separately) doesn't depend on this claim existing at all — its
|
||||
live-lookup path calls `tenant-engine` directly, so this workplan doesn't
|
||||
block it, but also doesn't complete the full `tenant_roles` picture on
|
||||
its own until service tokens exist too.
|
||||
|
|
@ -0,0 +1,116 @@
|
|||
---
|
||||
id: KEY-WP-0006
|
||||
type: workplan
|
||||
title: "Client credentials and service-token issuance"
|
||||
domain: infotech
|
||||
repo: key-cape
|
||||
status: finished
|
||||
owner: codex
|
||||
topic_slug: netkingdom
|
||||
created: "2026-07-27"
|
||||
updated: "2026-07-27"
|
||||
state_hub_workstream_id: "d8d20d8f-61e0-54e2-9a88-a3958f0b6f2d"
|
||||
---
|
||||
|
||||
# KEY-WP-0006 - Client credentials and service tokens
|
||||
|
||||
Implement the missing service-principal half of the IAM Profile so
|
||||
`KEY-WP-0004` can issue a least-privilege identity to `rapp-qonto`.
|
||||
|
||||
## T01 - Model confidential service clients
|
||||
|
||||
```task
|
||||
id: KEY-WP-0006-T01
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "993913a7-f951-50ee-9b10-d5b9e67b523c"
|
||||
```
|
||||
|
||||
Extend static client registration with an explicit service subject, tenant,
|
||||
roles, and an environment-backed secret reference. Refuse startup when a
|
||||
`client_credentials` client is public, lacks its identity fields, or cannot
|
||||
resolve its secret. Never serialize or log the resolved secret.
|
||||
|
||||
2026-07-27: Added explicit service subject, tenant, roles, and in-memory-only
|
||||
resolved secret fields. Configuration validation rejects public or incomplete
|
||||
service clients, and startup resolves only `env:` secret references while
|
||||
reporting the variable name—not its value—on failure.
|
||||
|
||||
## T02 - Implement secure client_credentials exchange
|
||||
|
||||
```task
|
||||
id: KEY-WP-0006-T02
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "801a6d55-2f91-51b1-88e4-cc7f0867238d"
|
||||
```
|
||||
|
||||
Accept confidential client authentication through HTTP Basic, compare secrets
|
||||
in constant time, restrict requested scopes to the registered allowlist, and
|
||||
issue a short-lived access token with `principal_type=service`,
|
||||
tenant-specific subject/roles, empty groups, `aal1` client-secret assurance,
|
||||
and optional cached `tenant_roles`. Do not issue an ID token for this
|
||||
non-human grant.
|
||||
|
||||
2026-07-27: Implemented HTTP Basic confidential-client authentication with
|
||||
fixed-length SHA-256 constant-time comparison, allowlisted scopes, short-lived
|
||||
service claims, optional tenant-role cache, token-issued telemetry, and no ID
|
||||
token.
|
||||
|
||||
## T03 - Prove positive and negative conformance
|
||||
|
||||
```task
|
||||
id: KEY-WP-0006-T03
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "e5c2a8b1-0879-57fa-a197-250a34c75bdb"
|
||||
```
|
||||
|
||||
Cover valid exchange, unknown client, public client, missing/wrong secret,
|
||||
unsupported grant, excess scope, tenant isolation, telemetry redaction, and
|
||||
discovery metadata. Run the full Go build, vet, and test suite.
|
||||
|
||||
2026-07-27: Added positive service-claim and negative wrong-secret/excess-scope
|
||||
tests, updated discovery to advertise only implemented grants/auth methods,
|
||||
and verified `go test ./...`, `go vet ./...`, and `go build ./...` across the
|
||||
module.
|
||||
|
||||
## T04 - Provision and verify rapp-qonto
|
||||
|
||||
```task
|
||||
id: KEY-WP-0006-T04
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "31af6b8c-96ff-5060-87db-3803b9a9dd12"
|
||||
```
|
||||
|
||||
Generate the client secret without disclosure, store it through the approved
|
||||
OpenBao lane, register the static `rapp-qonto` client, deploy KeyCape, and
|
||||
verify a real exchange yields only the documented Binky service claims.
|
||||
Publish non-secret evidence to `KEY-WP-0004-T03` through T05.
|
||||
|
||||
2026-07-27: Provisioned the confidential client secret at
|
||||
`platform/workloads/rapp-qonto/keycape-client`, injected it into
|
||||
`sso/keycape-rapp-qonto-client`, registered `rapp-qonto-client`, and deployed
|
||||
image `main-e877d27-2` on railiance01. A real exchange produced only
|
||||
`sub=rapp-qonto`, `tenant=tenant:friendly:binky`, `principal_type=service`,
|
||||
`roles=[qonto-reader]`, empty groups, and `scope=qonto:read`; excessive scope
|
||||
and a wrong secret were denied. Public DNS still resolves
|
||||
`kc.coulomb.social` to the older CoulombCore endpoint, so the deployment proof
|
||||
used TLS-preserving direct resolution to railiance01 (`92.205.62.239`).
|
||||
|
||||
## T05 - Closure review
|
||||
|
||||
```task
|
||||
id: KEY-WP-0006-T05
|
||||
status: done
|
||||
priority: low
|
||||
state_hub_task_id: "8c72d7d9-4086-5e64-b939-a12bff076124"
|
||||
```
|
||||
|
||||
Close after T01-T04 pass and the repeatable verification path is documented.
|
||||
|
||||
2026-07-27: Closure review passed. Build, vet, unit/profile tests, live
|
||||
positive claims, negative authentication/scope checks, and discovery metadata
|
||||
all passed. The stale public DNS target is handed off as routing work rather
|
||||
than weakening the service-token contract.
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
---
|
||||
id: KEY-WP-0007
|
||||
type: workplan
|
||||
title: "User-engine portal OIDC relying-party integration"
|
||||
domain: infotech
|
||||
repo: key-cape
|
||||
status: finished
|
||||
owner: codex
|
||||
topic_slug: netkingdom
|
||||
created: "2026-07-27"
|
||||
updated: "2026-07-29"
|
||||
depends_on:
|
||||
- KEY-WP-0005
|
||||
- USER-WP-0020
|
||||
state_hub_workstream_id: "24db4178-705c-5b09-b7db-ee7a345d71a0"
|
||||
---
|
||||
|
||||
# KEY-WP-0007 - User-engine portal OIDC relying-party integration
|
||||
|
||||
Register and prove the reusable user-engine portal as a strict KeyCape public
|
||||
client. This is the authentication edge required by `NK-WP-0023-T03`; it does
|
||||
not move user-domain or authorization ownership into KeyCape.
|
||||
|
||||
## Register the static portal client
|
||||
|
||||
```task
|
||||
id: KEY-WP-0007-T01
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "7d722cae-bf6d-52e0-80f4-cc51e9fb42f0"
|
||||
```
|
||||
|
||||
Register `user-engine-portal` for authorization code with mandatory S256 PKCE,
|
||||
exact callback URIs and only `openid profile email groups`. No wildcard,
|
||||
implicit flow, client secret, or dynamic registration is allowed.
|
||||
|
||||
## Deploy and verify the live client
|
||||
|
||||
```task
|
||||
id: KEY-WP-0007-T02
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "0b222062-4352-5ae6-9e85-95787af08409"
|
||||
```
|
||||
|
||||
Update the railiance01 KeyCape configuration without exposing its signing key
|
||||
or backend credentials. Verify discovery, authorize redirect validation,
|
||||
unregistered callback denial and successful token exchange through the portal.
|
||||
|
||||
## Prove claims and MFA isolation
|
||||
|
||||
```task
|
||||
id: KEY-WP-0007-T03
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "859e7322-71dc-58cc-9f44-6f869c2d0b59"
|
||||
```
|
||||
|
||||
Prove issuer, audience, tenant, groups, roles and assurance claims are verified
|
||||
by the portal and that tenant administration does not imply platform-root.
|
||||
Complete the Binky user/MFA acceptance through the reusable browser path.
|
||||
|
||||
2026-07-27: The live client accepts only the exact portal callback and rejects
|
||||
an unregistered callback with `invalid_profile_usage`. The portal begins an
|
||||
S256 PKCE flow and hands authentication to Authelia. LLDAP tenant envelope
|
||||
mapping now recognizes unambiguous `tenant:<kind>:<slug>:users|admins` groups;
|
||||
ambiguous multi-tenant directory envelopes fail closed to no explicit tenant.
|
||||
|
||||
2026-07-28: suspension now fails closed at both token exchange and
|
||||
`/userinfo`; the deployed `key-cape:909bb32` image refuses subjects carrying
|
||||
the `netkingdom-suspended` directory group. The portal admin surface uses this
|
||||
through the provider-neutral lifecycle API. The human Binky MFA/claims
|
||||
acceptance remains the final part of this task.
|
||||
|
||||
2026-07-29 complete: the reusable browser path issued the Binky password
|
||||
handoff, completed privacyIDEA TOTP enrollment, and returned through the
|
||||
portal authorization-code flow. `/api/v1/me` verified the canonical issuer,
|
||||
`user-engine-portal` audience, `tenant:friendly:binky`, Binky-only groups,
|
||||
roles `user` + `tenant-admin`, and AAL2 assurance with `pwd` + `otp` and
|
||||
`mfa: true`. The same session was denied administration of
|
||||
`tenant:platform` and `tenant:coulomb`. All workplan tasks are complete.
|
||||
|
|
@ -0,0 +1,118 @@
|
|||
---
|
||||
id: KEY-WP-0008
|
||||
type: workplan
|
||||
title: "Registration handoff and client-aware MFA policy"
|
||||
domain: infotech
|
||||
repo: key-cape
|
||||
status: finished
|
||||
owner: grok
|
||||
topic_slug: netkingdom
|
||||
created: "2026-08-09"
|
||||
updated: "2026-08-16"
|
||||
depends_on:
|
||||
- NK-WP-0025
|
||||
state_hub_workstream_id: "23eb67d4-cf10-57a1-92f6-90146a294bda"
|
||||
---
|
||||
|
||||
# KEY-WP-0008 - registration handoff and client-aware MFA
|
||||
|
||||
Let a registered OIDC client offer NetKingdom signup and request step-up
|
||||
without making KeyCape an account store or weakening high-assurance clients.
|
||||
|
||||
## T01 - Add a safe registration handoff
|
||||
|
||||
```task
|
||||
id: KEY-WP-0008-T01
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "e5fb4d73-1e19-565c-80d5-e0eee274ce63"
|
||||
```
|
||||
|
||||
Define an allow-listed registration URL for eligible clients and preserve
|
||||
client ID, redirect URI, PKCE intent, tenant hint, and return context in a
|
||||
signed, expiring state envelope. Registration completion must restart the
|
||||
normal authorization flow and must not mint a token directly.
|
||||
|
||||
Done when unknown users can choose signup from an eligible authorization flow
|
||||
without open redirect, client substitution, or state replay.
|
||||
|
||||
Implemented `prompt=create` and `/authorize/register` against the client's
|
||||
static `registrationUrl`, plus HMAC-signed `kc_handoff` envelopes consumed
|
||||
once at `/authorize/return`. Return restarts `/authorize` and never mints a
|
||||
code. Ineligible clients get no signup link. Live registration entry remains
|
||||
user-engine-owned per NK-WP-0025; KeyCape only issues the return envelope.
|
||||
|
||||
## T02 - Replace global MFA with client-aware minimum assurance
|
||||
|
||||
```task
|
||||
id: KEY-WP-0008-T02
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "1ba50454-b6f5-578e-a526-da74b8082a89"
|
||||
```
|
||||
|
||||
Replace the single require-for-all switch with policy that combines client
|
||||
minimum assurance, requested ACR/step-up, tenant policy, protected action, and
|
||||
current session assurance. Preserve mandatory MFA for platform/admin clients.
|
||||
Allow coulomb-social ordinary login at password assurance when no stronger
|
||||
rule applies.
|
||||
|
||||
Done when one low-assurance client cannot suppress MFA for another client or
|
||||
reuse an under-assured session for a high-assurance request.
|
||||
|
||||
Implemented with nullable per-client `mfaRequired`: an explicit client value
|
||||
overrides the provider default only for that client. Absent values preserve
|
||||
the existing provider-driven policy.
|
||||
|
||||
2026-08-16: `DecideAssurance` now combines client minimum, requested ACR,
|
||||
provider default, `max_age`, `prompt=login`, and current KeyCape login-session
|
||||
level. An AAL1 session cannot satisfy an AAL2 client or `acr_values=aal2`.
|
||||
`coulomb-social` in `config/dev-config.yaml` is `mfaRequired: false`; other
|
||||
clients keep the provider default. Users without an enrolled factor are sent
|
||||
to the client's `enrollmentUrl` instead of completing authorization.
|
||||
|
||||
## T03 - Support explicit step-up and fresh authentication
|
||||
|
||||
```task
|
||||
id: KEY-WP-0008-T03
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "f7acbc12-e597-5d1e-8b29-bfa02237730f"
|
||||
```
|
||||
|
||||
Implement supported ACR/max-age or equivalent IAM Profile parameters, invoke
|
||||
privacyIDEA only when policy requires it, and return verifiable assurance
|
||||
claims. Handle users without an enrolled factor through a safe enrollment
|
||||
handoff rather than an authorization bypass.
|
||||
|
||||
Done when coulomb.social can request MFA for a profile/action and verify the
|
||||
result from token claims.
|
||||
|
||||
Implemented `acr_values` preservation and AAL2/MFA forcing through the
|
||||
privacyIDEA challenge. The existing token `assurance` claim reports `aal2`
|
||||
and `mfa: true` only after successful verification.
|
||||
|
||||
## T04 - Prove policy isolation and compatibility
|
||||
|
||||
```task
|
||||
id: KEY-WP-0008-T04
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "bc660767-387e-59a5-bfce-caccf4082c08"
|
||||
```
|
||||
|
||||
Test known and unknown users, registration link eligibility, state expiry and
|
||||
replay, password-only coulomb-social login, profile/action step-up, no-factor
|
||||
enrollment, OpenBao mandatory MFA, cross-client session reuse, and logout.
|
||||
Keep static client registration and exact redirect rules unchanged.
|
||||
|
||||
Done when existing high-assurance clients pass unchanged and the new
|
||||
coulomb-social journey passes live.
|
||||
|
||||
2026-08-16: isolation tests cover known/unknown users, registration
|
||||
eligibility, state expiry/replay, password-only coulomb-social, ACR step-up,
|
||||
no-factor enrollment handoff, OpenBao mandatory MFA, cross-client AAL1
|
||||
session reuse, logout, and exact redirect enforcement. Full Go suite passes.
|
||||
Live coulomb-social AAL1/AAL2 isolation was already proven on railiance01
|
||||
under NK-WP-0025-T05 (2026-08-14); this closeout adds the KeyCape-side
|
||||
regression suite and `/logout`.
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
---
|
||||
id: KEY-WP-0009
|
||||
type: workplan
|
||||
title: "Provider capability declarations and bounded service identities"
|
||||
domain: infotech
|
||||
repo: key-cape
|
||||
status: finished
|
||||
owner: codex
|
||||
topic_slug: netkingdom
|
||||
created: "2026-08-23"
|
||||
updated: "2026-09-27"
|
||||
depends_on:
|
||||
- NK-WP-0030
|
||||
- KEY-WP-0006
|
||||
state_hub_workstream_id: "c1a9b1cc-2ff0-566a-b544-1a2ef967fc0d"
|
||||
---
|
||||
|
||||
# KEY-WP-0009 — Provider capabilities and bounded service identities
|
||||
|
||||
Publish KeyCape-owned security-scenario interfaces and make the existing
|
||||
service-token issuer precise enough for OpenBao machine-login consumers. This
|
||||
work accepts identity issuance ownership without taking over OpenBao roles,
|
||||
policies, secret custody, or privacyIDEA token lifecycle.
|
||||
|
||||
## Publish C1 and C2b provider declarations
|
||||
|
||||
```task
|
||||
id: KEY-WP-0009-T01
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "eaf7af48-b3d6-5f85-b0ce-ff2b640f9cc3"
|
||||
```
|
||||
|
||||
Publish Playbook Capability Contract v0.1 declarations for the KeyCape C1
|
||||
runtime and its privacyIDEA-backed C2b integration. Name exact entry points,
|
||||
parameter authority, resource ownership, trust requirements, and readiness
|
||||
evidence. Do not claim C2a or privacyIDEA-owned token lifecycle.
|
||||
|
||||
Published `capabilities/playbooks/key-cape.lightweight-sso.yaml` and
|
||||
`capabilities/playbooks/key-cape.privacyidea-token-authority.yaml`. The latter
|
||||
claims the KeyCape integration for C2b while leaving factor enrollment, token
|
||||
state, custody keys, validation decisions, and lifecycle with privacyIDEA.
|
||||
|
||||
## Define bounded service-auth contracts
|
||||
|
||||
```task
|
||||
id: KEY-WP-0009-T02
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "212eb75c-a36c-5cb8-bf67-7d524f00f9ef"
|
||||
```
|
||||
|
||||
Define the KeyCape service-auth claims, renewal/expiry, failure, custody, and
|
||||
owner boundaries required by secrets-engine and OpenBao JWT roles. Add
|
||||
per-client access-token lifetimes so a bounded client contract does not depend
|
||||
on an unrelated global default.
|
||||
|
||||
Added a validated 1m-1h per-client `tokenLifetime` override, applied it to JWT
|
||||
`exp` and `expires_in`, and documented claims, renewal, expiry, residual JWT
|
||||
validity, explicit OpenBao cleanup, and no-fallback failure semantics in
|
||||
`docs/openbao-service-auth-contract.md`.
|
||||
|
||||
## Accept coding-agent issuance ownership
|
||||
|
||||
```task
|
||||
id: KEY-WP-0009-T03
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "c41e0144-8169-57cf-8c34-eb52e65d6d76"
|
||||
```
|
||||
|
||||
Publish the non-secret static registration for `codex-railiance-platform` with
|
||||
the exact audience, subject, tenant, role, scope, and 15-minute lifetime already
|
||||
accepted by railiance-platform. KeyCape owns JWT issuance and client disablement;
|
||||
railiance-platform owns the exact-bound OpenBao role and policy; OpenBao owns
|
||||
resulting token enforcement; secret values remain outside this repository.
|
||||
|
||||
Accepted ownership through the exact non-secret registration in
|
||||
`config/service-clients.example.yaml`. Added the parallel reviewed
|
||||
`secrets-engine-openbao` contract. Live value generation/materialization,
|
||||
deployment merge, and OpenBao role/policy application remain with their named
|
||||
custody and platform owners and are not implied by this source registration.
|
||||
|
||||
## Validate and hand off
|
||||
|
||||
```task
|
||||
id: KEY-WP-0009-T04
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "6945c0c0-bbb6-552b-8d62-d806d3fd0018"
|
||||
```
|
||||
|
||||
Run the canonical declaration validator, Go formatting/build/vet/tests, and
|
||||
repository checks. Reply to NetKingdom, secrets-engine, ops-warden, and
|
||||
railiance-platform with revision-independent paths and exact ownership status.
|
||||
|
||||
Both declarations pass NetKingdom's canonical validator. The full Go suite,
|
||||
vet, build, `gofmt`, YAML parsing, and `git diff --check` pass using an explicit
|
||||
Go 1.23 toolchain and writable local cache; the workstation's default Go cache
|
||||
is read-only and was not used.
|
||||
|
||||
2026-09-05 correction: the earlier statement that State Hub handoffs were
|
||||
delivered was not supported by receipt IDs. The audit found no matching handoff
|
||||
receipts in the Hub response. KEY-WP-0014-T02 records the correction.
|
||||
|
||||
2026-09-08 closure. Verification re-run on `main` with `GOTOOLCHAIN=go1.23.0` and
|
||||
a writable `GOCACHE`: `make contract-test` PASS, `go build ./...`, `go vet ./...`
|
||||
and `go test ./...` all pass, `git diff --check` clean. `gofmt` had drifted on
|
||||
`src/internal/server/telemetry/events.go` and `src/tests/migration/scenario_c_test.go`
|
||||
(comment alignment only, no semantic change); both were reformatted so the
|
||||
formatting claim now holds.
|
||||
|
||||
The four handoffs were then actually sent, and each receipt is readable back via
|
||||
`GET /messages/?from_agent=key-cape`:
|
||||
|
||||
| Recipient | Receipt id |
|
||||
| --- | --- |
|
||||
| netkingdom | `692ed0d7-e21b-48e7-8600-5b32b551c627` |
|
||||
| secrets-engine | `479a1698-87dc-4ee2-a732-c2dc18bc4e84` |
|
||||
| ops-warden | `5bc58847-eed5-4164-bb0f-a0d8455e75b8` |
|
||||
| railiance-platform | `9c6a4e70-6bc0-4ad6-8293-35a56e507dfc` |
|
||||
|
||||
Each names revision-independent paths, the 2026-09-08 verification result, and
|
||||
the same ownership split: KeyCape owns issuance, static registrations, client
|
||||
disablement and the claim set; secret custody, OpenBao roles/policies and token
|
||||
enforcement, privacyIDEA factor and token lifecycle, resource authorization and
|
||||
deployment remain with their named owners. C2a is not claimed. This closes the
|
||||
notification gap; it is still not evidence of live deployment or provider cutover.
|
||||
|
||||
### 2026-09-27 operator tenant-zero correction
|
||||
|
||||
OpenBao infrastructure identities belong to `tenant:platform`, not Coulomb.
|
||||
Corrected both static infrastructure registrations, matching source tests and
|
||||
the tenant/OpenBao contracts. Consumer preflight and platform role sources are
|
||||
updated together; no live registration or token migration is claimed. The
|
||||
provider contract remains complete; RPF-WP-0035-T02 and SECRETS-WP-0008-T06
|
||||
retain coordinated custody/deployment and native rejection/revocation proof.
|
||||
Human fallback and workload tenant grants are not elevated to platform.
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
---
|
||||
id: KEY-WP-0010
|
||||
type: workplan
|
||||
title: "Admit the operator-tunneled OpenBao callback"
|
||||
domain: infotech
|
||||
repo: key-cape
|
||||
status: finished
|
||||
owner: codex
|
||||
topic_slug: netkingdom
|
||||
created: "2026-08-23"
|
||||
updated: "2026-08-23"
|
||||
depends_on:
|
||||
- RAILIANCE-WP-0027
|
||||
state_hub_workstream_id: "8bdf9a24-9dc1-53f9-9448-3b29c36d9950"
|
||||
---
|
||||
|
||||
# KEY-WP-0010 — OpenBao operator loopback callback
|
||||
|
||||
## Admit and validate the exact callback
|
||||
|
||||
```task
|
||||
id: KEY-WP-0010-T01
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "46d78907-bda4-5cc9-853d-98c0026d9fe1"
|
||||
```
|
||||
|
||||
Added the exact operator-only callback
|
||||
`http://127.0.0.1:18200/ui/vault/auth/netkingdom/oidc/callback` to the
|
||||
source-owned `openbao-admin` static client in `config/dev-config.yaml`. The
|
||||
registration retains exact-match redirects and does not authorize a login,
|
||||
change the OpenBao role, broaden a listener, or handle a credential. A config
|
||||
test pins the exact URI and proves the complete source config validates.
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
---
|
||||
id: KEY-WP-0011
|
||||
type: workplan
|
||||
title: "Recover from live KeyCape Secret output exposure"
|
||||
domain: infotech
|
||||
repo: key-cape
|
||||
status: finished
|
||||
owner: codex
|
||||
topic_slug: netkingdom
|
||||
created: "2026-08-23"
|
||||
updated: "2026-08-23"
|
||||
state_hub_workstream_id: "158aa957-8818-52ea-b7a5-3962cf30ba5e"
|
||||
---
|
||||
|
||||
# KEY-WP-0011 — Live KeyCape Secret exposure recovery
|
||||
|
||||
Recover safely after the base64 data map of Kubernetes Secret
|
||||
`sso/keycape-config` was mistakenly rendered in agent tool output during a
|
||||
metadata check. Never reproduce, decode, or store the exposed payload.
|
||||
|
||||
## Contain and notify owners
|
||||
|
||||
```task
|
||||
id: KEY-WP-0011-T01
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "a8f5e33d-c725-5181-849e-a42bba34d3d8"
|
||||
```
|
||||
|
||||
Stopped live rollout work, treated the payload as exposed, and notified
|
||||
railiance-platform and NetKingdom without values. The affected classes are the
|
||||
KeyCape RS256 signing private key and credentials embedded in `config.yaml`
|
||||
(LLDAP bind credential, Authelia client credential, and privacyIDEA admin
|
||||
token). No value was decoded, reused, or intentionally repeated and no live
|
||||
mutation was attempted.
|
||||
|
||||
## Approve and execute coordinated rotation
|
||||
|
||||
```task
|
||||
id: KEY-WP-0011-T02
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "9d60569c-a0f1-53a9-b4dc-01687ab92bcc"
|
||||
```
|
||||
|
||||
Approved by the operator on 2026-08-23, accepting deliberate invalidation of
|
||||
active sessions and a brief SSO interruption. The RS256 key, LLDAP bind/admin
|
||||
credential, Authelia-KeyCape client credential, and privacyIDEA application
|
||||
signing secret were replaced without rendering values. The exposed
|
||||
privacyIDEA admin JWT was removed from live use. Replacement material is
|
||||
recoverable only through the NetKingdom age recipient in the encrypted
|
||||
recovery overlay under `history/`.
|
||||
|
||||
## Verify recovery without values
|
||||
|
||||
```task
|
||||
id: KEY-WP-0011-T03
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "a079573d-97fd-5c6c-b1f9-c0af65e2933a"
|
||||
```
|
||||
|
||||
Verified changed resource versions and public JWKS fingerprints, four Ready
|
||||
deployments, successful replacement LLDAP and Authelia authentication,
|
||||
wrong-secret rejection, explicit privacyIDEA fail-closed denial, issuer
|
||||
discovery, and the OpenBao callback handoff to Authelia. The temporary LLDAP
|
||||
force-reset flag was removed. No Kubernetes Secret payload or authentication
|
||||
value was rendered during recovery.
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
---
|
||||
id: KEY-WP-0012
|
||||
type: workplan
|
||||
title: "Repair UserInfo canonical subject resolution"
|
||||
domain: infotech
|
||||
repo: key-cape
|
||||
status: finished
|
||||
owner: codex
|
||||
topic_slug: userinfo-canonical-subject-resolution
|
||||
created: "2026-08-31"
|
||||
updated: "2026-09-01"
|
||||
state_hub_workstream_id: "2aca83d8-4e80-52c6-8929-f1e957576b47"
|
||||
---
|
||||
|
||||
## Repair subject lookup
|
||||
|
||||
```task
|
||||
id: KEY-WP-0012-T01
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "bc1ee2c7-a321-5914-96d1-9cb871f0ee1b"
|
||||
```
|
||||
|
||||
Resolve the canonical LDAP-DN `sub` emitted by the token endpoint without
|
||||
passing it to the username-only repository lookup. Preserve stable subject
|
||||
semantics and verify any `preferred_username` lookup against the canonical ID.
|
||||
|
||||
## Regression verification
|
||||
|
||||
```task
|
||||
id: KEY-WP-0012-T02
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "7073cc30-a518-5c27-acaf-1a323fd981c6"
|
||||
```
|
||||
|
||||
Cover canonical-ID, legacy username-sub, missing subject, and suspended-user
|
||||
behavior. Run the KeyCape test suite and image build checks.
|
||||
|
||||
## Deploy and verify OpenBao OIDC
|
||||
|
||||
```task
|
||||
id: KEY-WP-0012-T03
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "571c5237-93fa-5c61-87af-974e02fd8929"
|
||||
```
|
||||
|
||||
Publish and deploy the corrected KeyCape image, prove `/userinfo` accepts a
|
||||
fresh human access token, then resume the governed Policy Nexus bootstrap.
|
||||
|
||||
Completed 2026-09-01. Forgejo Actions run 14 built commit
|
||||
`153258b9d39273d53ca7ab5d1bdc09961ea9026e`; image digest
|
||||
`sha256:4c7193efc3e50bd72a01812a583e823cc04f41ca96ef387b31ba9ae2533b80d0`
|
||||
rolled out successfully. A fresh OpenBao OIDC flow passed `/userinfo`, and the
|
||||
governed Policy Nexus bootstrap subsequently completed with deterministic
|
||||
session revocation.
|
||||
|
|
@ -0,0 +1,573 @@
|
|||
---
|
||||
id: KEY-WP-0013
|
||||
type: workplan
|
||||
title: "Approval-engine resource audience and client registrations"
|
||||
domain: infotech
|
||||
repo: key-cape
|
||||
status: finished
|
||||
flavor: implementation
|
||||
owner: codex
|
||||
topic_slug: approval-engine-resource-audience
|
||||
created: "2026-09-05"
|
||||
updated: "2026-09-30"
|
||||
state_hub_workstream_id: "6e815d88-b0e3-5ce0-be5d-13ab15917f7f"
|
||||
---
|
||||
|
||||
Source: approval-engine inbox request 5583e896-52f2-45bd-895f-02f227b7e836,
|
||||
reviewed against its local registration and caller-authentication contracts.
|
||||
|
||||
## Implement static resource audiences
|
||||
|
||||
```task
|
||||
id: KEY-WP-0013-T01
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "a92432a2-4e92-5b90-be2b-d82b784bf8f0"
|
||||
```
|
||||
|
||||
Added optional static audience configuration for access tokens in both grants;
|
||||
ID tokens retain the relying-party audience. Added human access-token scope.
|
||||
Published bounded approval service fragments and the human registration contract.
|
||||
Regression tests cover the default audience, request override resistance, JWKS
|
||||
signature validation, ID-token separation and service registration scope isolation.
|
||||
Browser requests and token exchanges now enforce the client scope allow-list,
|
||||
including grants removed after authorization.
|
||||
|
||||
## Provision and prove live registrations
|
||||
|
||||
```task
|
||||
id: KEY-WP-0013-T02
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "607897c5-bad9-55e5-86df-7802f592d6e8"
|
||||
```
|
||||
|
||||
Needs deployment-owned custody for both new service secret references and the
|
||||
compatible image/configuration rollout. The upstream issuer is now verified and
|
||||
pinned by NetKingdom (2026-09-09). Deploy the implementation and service registrations together,
|
||||
then prove live JWKS verification and denied excess scopes without logging values.
|
||||
Local signature proof is not live rollout evidence. See docs/approval-engine-auth-contract.md.
|
||||
The separate human UI callback gate is retained in T05; a bearer-only resource
|
||||
server does not own a redirect and its absence does not block these service clients.
|
||||
|
||||
|
||||
2026-09-05 follow-up: read-only deployment metadata shows the current image is
|
||||
forgejo.coulomb.social/coulomb/key-cape:main-153258b and only the Qonto service
|
||||
secret environment reference is present. The two approval clients are not
|
||||
materialized through deployment environment references. Published a concrete
|
||||
non-secret admission packet at docs/approval-engine-provisioning-request.yaml.
|
||||
Custody routing has no exact admitted lane for these two clients. `warden plan`
|
||||
returned founder_required but matched an unrelated generic database lane; that
|
||||
mismatch is not authority to provision. Human callback clarification is pending.
|
||||
No secrets were read or production resources changed.
|
||||
|
||||
2026-09-08: the two blocking requests were sent and their receipts are readable
|
||||
back via `GET /messages/?from_agent=key-cape`.
|
||||
|
||||
- `278a3ebe-b529-49f6-bd1a-e3ebcf318260` -> railiance-platform. Custody admission
|
||||
for both clients, quoting `docs/approval-engine-provisioning-request.yaml`
|
||||
verbatim: exact OpenBao paths and `client_secret` field, the two Kubernetes
|
||||
delivery references, the two `KEYCAPE_*_CLIENT_SECRET` environment names,
|
||||
`tenant:platform` per decision `5ed3fb35-eca9-413a-82b9-95171ba85bf6`, and the
|
||||
15m lifetime. It asks for four things explicitly: confirm/correct the paths and
|
||||
fields, confirm the delivery references, name the authority for the attended
|
||||
first provision, and agree a rollout window in which registrations and the
|
||||
KeyCape version that reads them deploy together. It restates that the
|
||||
`warden plan` `founder_required` result matched an unrelated generic database
|
||||
lane and is not being treated as authorization.
|
||||
- `43fbd997-6e22-4859-8433-58b784c2df6c` -> approval-engine. Requests the two
|
||||
missing human-registration inputs — the exact `client_id` and the full callback
|
||||
URI — noting redirects are matched exactly and a near-miss fails closed at
|
||||
`/authorize`. Restates the human client shape (public, S256 PKCE,
|
||||
`audience: approval-engine`, `allowedScopes: [openid, approval:approve]`,
|
||||
`mfaRequired: true`, no consume grant) and asks approval-engine to confirm it
|
||||
validates the access token, not the ID token, against the deployed `/jwks`.
|
||||
|
||||
Deployment state is unchanged from the 2026-09-05 observation: neither approval
|
||||
client is materialized. Task remains `wait` — the two admissions are owned by
|
||||
railiance-platform and approval-engine respectively, and live provisioning proof
|
||||
cannot begin until both land. Still no secret read and no production resource
|
||||
changed.
|
||||
|
||||
2026-09-08, second pass. Custody and the human callback are still owed, but one
|
||||
part of this task was ours all along and was not built: the task requires proving
|
||||
"live JWKS verification and denied excess scopes without logging values", and
|
||||
there was no way to do that except by hand, against production, at the moment
|
||||
custody lands — which is the worst time to be improvising a check.
|
||||
|
||||
Shipped `keycape verify-client` (`src/internal/authclient/verify.go`, documented
|
||||
in `docs/native-authentication.md`). Per registration it verifies discovery
|
||||
origin, the `client_credentials` exchange and its RS256 signature against the
|
||||
deployed JWKS, exact `sub`/`tenant`/`roles`/`principal_type`, that every
|
||||
`-deny-scope` is refused, and that the token carries no scope that was not
|
||||
requested — the last being a gap the caller commands do not cover, since they
|
||||
prove requested scopes were granted and not that nothing extra came back.
|
||||
Failures name the claim, never the observed value, and nothing is written to
|
||||
disk, so it is safe to run against production. The exact invocation for each of
|
||||
the two clients is now recorded in the `verification:` block of
|
||||
`docs/approval-engine-provisioning-request.yaml`, so admission hands back a
|
||||
command rather than a description.
|
||||
|
||||
Tests: `src/internal/authclient/verify_test.go` covers the passing case, an
|
||||
over-broad registration, a live predecessor secret, an identical "rotation" and
|
||||
output non-disclosure. Verified with teeth — neutering `mustFail` makes the suite
|
||||
fail rather than pass silently.
|
||||
|
||||
Task stays `wait`. What is owed from elsewhere is unchanged and unreduced: the
|
||||
two secret values through an admitted custody path, and the exact human
|
||||
`client_id` and callback URI. Nothing here provisions anything.
|
||||
|
||||
### 2026-09-08 attended upstream issuer proof — T02 return
|
||||
|
||||
The user explicitly admitted the prepared temporary probe. At 21:44:44 UTC its
|
||||
actual Authelia authorization-code exchange verified the signed issuer
|
||||
**`https://auth.coulomb.social`**, including signature, keycape audience, validity
|
||||
window and browser-bound nonce. The pinned Job exited 0. No token or user claim
|
||||
was retained, and no downstream credential was issued.
|
||||
|
||||
All five temporary resources and the Pod were removed with scoped cleanup;
|
||||
KeyCape Deployment and config Secret metadata remained unchanged. Receipt:
|
||||
`docs/evidence/2026-09-08-upstream-issuer-live-proof.json`.
|
||||
|
||||
This closes the actual-token issuer observation gate. T02 remains wait for the
|
||||
configuration owner to ensure `authelia.issuer` is pinned to the verified HTTPS
|
||||
value, named CCR-2026-0017/0018 reviews, custody delivery and compatible rollout.
|
||||
The diagnostic did not prove downstream KeyCape MFA/application login and did
|
||||
not activate custody. Do not repeat discovery or request another observation
|
||||
unless the provider/configuration or proof context changes.
|
||||
|
||||
|
||||
2026-09-09 configuration-owner return: NetKingdom's exercised, revision-guarded
|
||||
issuer-only helper inserted `authelia.issuer: https://auth.coulomb.social` into
|
||||
`sso/keycape-config`; resourceVersion `51346058` -> `58713343`. Independent
|
||||
readback matches. Every other configuration byte and Secret data entry, including
|
||||
the private key, was preserved. Thirteen tests passed. Receipt:
|
||||
`docs/evidence/2026-09-09-upstream-issuer-pin.json`.
|
||||
|
||||
The stored issuer configuration prerequisite is closed. No process restart,
|
||||
image rollout, custody activation or downstream login proof occurred. T02 stays
|
||||
wait for the named CCR reviews, attended custody and compatible rollout. The
|
||||
single-instance replacement and existing-human-login checks remain mandatory.
|
||||
|
||||
2026-09-09 completion: both named CCR reviews are recorded. Verifier-side custody,
|
||||
ESO delivery and the compatible pinned image/config are now live. Both service
|
||||
clients passed the pinned verifier plus independent live signature and exact
|
||||
claim/lifetime checks; wrong-secret and excess-scope requests were rejected.
|
||||
The existing OpenBao human login passed before and after replacement; each
|
||||
attended session self-revoked. Receipt: `docs/evidence/2026-09-09-keycape-verifier-admission.json`.
|
||||
|
||||
The deployment owner exercised compatible rollback and version-preserving resume.
|
||||
The final helper checks containerd imageID against the pinned manifest and uses
|
||||
the existing 30-second future-iat bound without extending token expiry. The
|
||||
pinned native verifier runs in the contained owner process because workload
|
||||
network policy excludes public issuer HTTPS. No network grant was broadened.
|
||||
|
||||
Live residual ownership remains KEY-WP-0013-T05 for the approval UI callback/MFA;
|
||||
RPF-WP-0035-T05 for separate client-side delivery; AUDIT-WP-0009-T09 for audit
|
||||
custody; APPROVAL-WP-0002 and SECRETS-WP-0009-T03 for end-to-end claim/consume,
|
||||
expiry/revocation and native delivery. Natural JWT expiry and real predecessor
|
||||
rotation were not exercised by initial provisioning.
|
||||
|
||||
## Reconcile tenant vocabularies across approval layers
|
||||
|
||||
```task
|
||||
id: KEY-WP-0013-T03
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "e98da2eb-1f05-5c7f-bc84-711fa38b8c4a"
|
||||
```
|
||||
|
||||
Source: glas-harness inbox message 356f6977-d361-4e3b-83ab-b2c7f4759286
|
||||
(GLAS-WP-0015), which asks for the exact store tenant, JWT tenant, CheckRequest
|
||||
tenant, any permitted mapping and wrong-tenant denial evidence.
|
||||
|
||||
Published `docs/tenant-claim-contract.md`. The KeyCape-owned JWT tenant for all
|
||||
four reviewed service registrations is `tenant:coulomb`, bound at registration
|
||||
and required by config validation. Approval store `platform` and policy
|
||||
`tenant:platform` are owned by approval-engine and flex-auth; KeyCape performs no
|
||||
normalization or aliasing, so exact comparison does not match today. No mapping
|
||||
was invented and no live registration or policy subject was changed — the two
|
||||
admissible resolutions are recorded for the owning parties to decide.
|
||||
|
||||
Added `src/internal/server/oidc/tenant_test.go`: request-supplied `tenant` and
|
||||
`tenant_hint` cannot alter the claim; two registrations never carry each other's
|
||||
tenant (the wrong-tenant denial basis); human tokens default to `tenant:coulomb`
|
||||
rather than an empty claim. Local issuance proof only, not live-rollout evidence.
|
||||
|
||||
## Align approval registrations to tenant:platform
|
||||
|
||||
```task
|
||||
id: KEY-WP-0013-T04
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "d93e21cc-a422-5e78-981f-9c00f3161375"
|
||||
```
|
||||
|
||||
Source: glas-harness inbox message f487c63a-dd7e-4ad8-9188-eec9fbea59c6,
|
||||
operator decision 5ed3fb35-eca9-413a-82b9-95171ba85bf6 ("Use tenant:platform for
|
||||
the Glas approval dependency chain", verified resolved in the hub). This closed
|
||||
resolution 2 of the two options recorded in KEY-WP-0013-T03.
|
||||
|
||||
Changed the `tenant` field on exactly `secrets-engine-approval` and
|
||||
`approval-engine-operator` in `config/service-clients.example.yaml` and the two
|
||||
matching entries in `docs/approval-engine-provisioning-request.yaml` to
|
||||
`tenant:platform`, each annotated with the decision id. `codex-railiance-platform`,
|
||||
`secrets-engine-openbao` and the human directory default keep `tenant:coulomb`.
|
||||
No audience, scope, subject, role, lifetime or MFA change; no cross-tenant grant.
|
||||
|
||||
Evidence: `TestApprovalClientIssuesExactPlatformTenantAndRejectsAliases` (exact
|
||||
`tenant:platform` with `aud=approval-engine` even when the caller requests
|
||||
`platform`, `tenant:coulomb` or `TENANT:PLATFORM`),
|
||||
`TestUnrelatedServiceClientKeepsCoulombTenant`, and
|
||||
`TestServiceRegistrationTenantsAreExactPerDecision`, which pins every reviewed
|
||||
client's tenant against the real fixture so a reintroduced alias fails the build.
|
||||
Full `go test ./...` and `go vet ./...` pass. Choice only — live provisioning and
|
||||
the other admission gates remain with KEY-WP-0013-T02.
|
||||
|
||||
2026-09-09 observation, read-only. Custody has been admitted since the
|
||||
2026-09-08 record and the deployment now carries the pieces T02 was waiting for:
|
||||
`sso/keycape-config` registers `secrets-engine-approval` and
|
||||
`approval-engine-operator` alongside the existing clients; the secrets
|
||||
`keycape-secrets-engine-approval-client` and
|
||||
`keycape-approval-engine-operator-client` exist in the namespace; both
|
||||
`KEYCAPE_SECRETS_ENGINE_APPROVAL_CLIENT_SECRET` and
|
||||
`KEYCAPE_APPROVAL_ENGINE_OPERATOR_CLIENT_SECRET` are wired into the pod; and the
|
||||
image is pinned by digest to
|
||||
`sha256:7ff54c54e63ee172ae9e6e7fd2da96e427352f712343d74626ee6fe0f6f82611`, the
|
||||
candidate T06 published.
|
||||
|
||||
What that does and does not establish. The live issuer passes every conformance
|
||||
check that needs no credential — it advertises itself, offers the profile
|
||||
authorization surface, publishes usable RS256 keys and advertises no excluded
|
||||
grant (`src/tests/conformance`, run against `https://kc.coulomb.social`). It does
|
||||
**not** establish the claim contract: subject, tenant, roles, scopes, lifetime
|
||||
and excess-scope denial per client are exactly what `keycape verify-client`
|
||||
exists to prove, and that needs the client secret in the environment.
|
||||
|
||||
Deliberately not done here. Reading either secret to run the verifier is
|
||||
client-side retrieval, which the provisioning packet records as **not admitted**
|
||||
(`custody_return.scope`: verifier-side copies only). So the remaining proof is an
|
||||
attended operator action, not something to take by reading the secret. T02 stays
|
||||
`wait` on that run, not on custody.
|
||||
|
||||
**Correction, same day.** The refusal above was right; the conclusion drawn from
|
||||
it was not. The attended run had already happened, on 2026-09-09T00:10Z, and its
|
||||
receipt is committed in this repository at
|
||||
`docs/evidence/2026-09-09-keycape-verifier-admission.json`. Per client — both
|
||||
`secrets-engine-approval` and `approval-engine-operator` —
|
||||
`live_jwks_signature_verified: true`, `exact_claims_verified: true`,
|
||||
`excess_scope_denied: true`, `wrong_secret_denied: true`,
|
||||
`lifetime_seconds: 900`, `maximum_future_iat_seconds: 30`,
|
||||
`pinned_artifact_verifier_passed: true`, `verifier_location: "attended owner
|
||||
process"`. `credential_values_emitted: false` and
|
||||
`client_side_read_admitted: false` throughout, so it was run the admitted way.
|
||||
The claim contract is established and T02's `status: done` is correct; it does
|
||||
not revert to `wait`.
|
||||
|
||||
The reasoning error is worth naming because it is the third of its kind here in
|
||||
two days: "not admitted **for me**" was read as "not done **by anyone**", without
|
||||
checking `docs/evidence/`. The preceding paragraph's read-only observations are
|
||||
sound and its restraint about not reading the secret is exactly right — only the
|
||||
final sentence was wrong, and it is retained above rather than deleted so the
|
||||
correction has something to point at.
|
||||
|
||||
What the receipt itself declines to claim, which is the honest residual and
|
||||
matches this task's earlier record: `real_predecessor_rotation_tested: false`,
|
||||
`observed_wall_clock_expiry: false`, and under `limits`,
|
||||
`actual_predecessor_rotation_observed: false` and
|
||||
`wall_clock_jwt_expiry_observed: false`. Natural expiry and a real predecessor
|
||||
rotation were not exercised by initial provisioning and are not claimed by
|
||||
anyone. Predecessor rejection is implemented and unit-tested in
|
||||
`keycape verify-client`; it has not been run against a real rotation, which is
|
||||
KEY-WP-0014-T04's territory and still has no admitted execution authority.
|
||||
|
||||
**Independently confirmed by the custody owner, 2026-09-09** (railiance-platform
|
||||
`ae6ff4bc`, `570e6746`). They reached the same reading from their own side and
|
||||
add one datum this record did not carry: `human_client_consume_denied: true` for
|
||||
both clients, alongside the signature, exact-claim, lifetime, excess-scope and
|
||||
wrong-secret results. They confirm the verifier ran twice — once during
|
||||
activation and again after rollout — at deployment generation 38, single ready
|
||||
replica, signing key unchanged, no credential values emitted, and state that T02
|
||||
can be closed against that receipt rather than against a run KeyCape cannot make.
|
||||
|
||||
They also correct one thing in our framing, and they are right: the packet says
|
||||
*client-side retrieval* is unadmitted, which remains true, but the attended
|
||||
operator path is not a client-side read and never required one. The two are
|
||||
distinct and this task had been treating them as the same constraint.
|
||||
|
||||
**Do not cite this receipt as rotation evidence.** Both owners now state the same
|
||||
two gaps independently: `real_predecessor_rotation_tested: false` and
|
||||
`observed_wall_clock_expiry: false`. Neither is a T02 acceptance criterion, and
|
||||
neither is closed by anything recorded here — see KEY-WP-0014-T04.
|
||||
|
||||
**Client-side retrieval is prepared but still unadmitted.** CCR-2026-0019
|
||||
(secrets-engine reader) and CCR-2026-0020 (approval-engine-operator reader) are
|
||||
`in_flight` under RPF-WP-0035-T06. 0020 has no named presenting actor in any
|
||||
owner source, and railiance-platform left it undetermined rather than guessing —
|
||||
correctly. KeyCape's view, asked for as the issuer of the identity and recorded
|
||||
in `docs/approval-engine-provisioning-request.yaml`, is that the registration as
|
||||
issued carries both `approval:create` and `approval:approve`, so a single
|
||||
presenter can create and then approve the same entry. That is a
|
||||
separation-of-duties question about who may hold the credential, not a defect in
|
||||
the token: KeyCape issues the grants approval-engine requested. The decision is
|
||||
approval-engine's and the doctrine question is gate-house's.
|
||||
|
||||
**Answered 2026-09-09, and the answer withdrew the client.** approval-engine
|
||||
checked and the presenter is nobody (`056aef0d`). Nothing in their repository
|
||||
obtains an OAuth token — the engine only verifies them, there is no
|
||||
client-credentials grant in the package, and the one outbound credential is a
|
||||
mounted audit-token, not an OAuth client. Scope by scope the bundle never
|
||||
described a single actor: `approval:approve` now belongs to the informed-decision
|
||||
human client, `approval:emit` is redundant because the running server emits its
|
||||
own heartbeat with no token, and no requester identity was ever settled for
|
||||
`approval:create`. It was a convenience bundle written when a lifecycle operator
|
||||
was assumed to exist.
|
||||
|
||||
They asked railiance-platform to cancel the client-side reader CCR-2026-0020 as
|
||||
an owner decision. Verifier custody CCR-2026-0018 stands, so the client remains
|
||||
authenticable if a presenter appears, and they asked for it to be kept on that
|
||||
basis rather than removed. Annotated as withdrawn in
|
||||
`config/service-clients.example.yaml` and the provisioning packet; not deleted.
|
||||
The separation-of-duties observation above is retained because it constrains the
|
||||
re-request: when a presenter is named the registration comes back **narrowed**,
|
||||
and `approval:approve` must not travel with the operational scopes.
|
||||
|
||||
**One thing "do not provision" cannot undo, raised rather than filed.** This
|
||||
client is already live — provisioned in the 2026-09-09 attended rollout and
|
||||
verified in `docs/evidence/2026-09-09-keycape-verifier-admission.json`. So the
|
||||
estate now holds a confidential credential that can create *and* approve
|
||||
approvals, that nobody presents, and that no consumer is waiting on. That is
|
||||
standing capability with no counterparty, which is a worse resting state than
|
||||
either provisioning it for a named presenter or disabling it. KeyCape owns client
|
||||
disablement, so the action is ours to take and not ours to decide alone; raised
|
||||
with approval-engine and railiance-platform rather than exercised, since removing
|
||||
a live registration is a deployment change. Recorded here so it is not lost:
|
||||
**an unpresented create+approve credential should not be left standing
|
||||
indefinitely.**
|
||||
|
||||
## Assign and register the human approver browser client
|
||||
|
||||
```task
|
||||
id: KEY-WP-0013-T05
|
||||
status: done
|
||||
priority: high
|
||||
assignee: the-custodian
|
||||
state_hub_task_id: "9a782909-91db-59fa-aae7-83766f4fbb0d"
|
||||
```
|
||||
|
||||
Own the residual human registration separately from service-client T02. Reuse
|
||||
an existing accepted UI if available; do not invent a callback on approval-engine.
|
||||
Require public S256 PKCE, exact redirect, MFA, approval:approve without consume,
|
||||
and a real human access-token proof. No service credential substitutes for this.
|
||||
HFACT-WP-0001-T03 consumes the acceptance where a human approval is required.
|
||||
|
||||
2026-09-09. The owner exists: `informed-decision` (hub repo cf4c7da8) claims the
|
||||
approver surface and owns the two strings. approval-engine was right that a
|
||||
bearer-only resource server never owned them. Both wrote in asking to be told
|
||||
early about problems with the registration shape rather than at handover, so
|
||||
their requested shape was checked against the source rather than agreed on
|
||||
paper. Three results.
|
||||
|
||||
**Scope gap — accepted, theirs was right.** `[openid, approval:approve]` cannot
|
||||
render a decision: `GET /v1/approvals/{id}` and `/claim` both need
|
||||
`approval:read`, so the surface could submit an entry it was never able to
|
||||
display. Published `[openid, approval:read, approval:approve]` in
|
||||
`docs/approval-engine-auth-contract.md` and the provisioning packet. Reading
|
||||
through a service identity was the alternative and is worse: it weakens the
|
||||
evidence-of-what-this-person-saw claim the component exists to make.
|
||||
`approval:consume` stays excluded.
|
||||
|
||||
**Assurance shape — published, and a defect fixed.** Both asked for a documented
|
||||
shape; KeyCape already emitted one, so it is written down rather than
|
||||
renegotiated: `level` (aal1|aal2, ~acr), `methods` (~amr), `mfa`, `source`, `at`.
|
||||
Writing it down surfaced that `at` was the token **mint** time, not the
|
||||
authentication time. Those differ by hours whenever a browser session is reused,
|
||||
and approval-engine persists this object verbatim as the only downstream record
|
||||
that MFA happened — so a stored approval could have evidenced MFA at a moment the
|
||||
person proved nothing. `PKCESession.AuthTime` now carries the original login
|
||||
instant through session reuse, with mint time as the fallback for sessions
|
||||
predating the field. Covered by `src/internal/server/oidc/assurance_time_test.go`.
|
||||
|
||||
**Blocker found before anyone built on it — a human token cannot carry
|
||||
`tenant:platform`.** `effectiveTenant` resolves the human tenant from the
|
||||
directory user, not the client registration, and no adapter populates
|
||||
`domain.User.Tenant`, so every human token defaults to `tenant:coulomb`. The
|
||||
per-client `tenant` field that carries `tenant:platform` on the two service
|
||||
clients is read only on the `client_credentials` path. approval-engine compares
|
||||
tenant by exact string equality and pins near-miss spellings as refused, so an
|
||||
approver token issued today would be rejected — and it would present as a failed
|
||||
approval, not as a registration defect.
|
||||
|
||||
Two resolutions, sent to both owners (`67132ddc`, `a6a070a1`, pointer
|
||||
`42d435e8`) rather than chosen here, because the choice decides what a human's
|
||||
tenant *means*: (1) directory-sourced, approvers carry `tenant:platform` on their
|
||||
LLDAP record — keeps tenant a property of the person, needs a directory attribute
|
||||
and an owner, and changes those users everywhere; (2) registration-bound and
|
||||
fail-closed, symmetric with the service clients and with decision
|
||||
`5ed3fb35-eca9-413a-82b9-95171ba85bf6` — defensible only because registrations
|
||||
are static and deployment-owned, since dynamic registration is excluded by
|
||||
design. KeyCape leans to (2) and deliberately implemented neither: (1) is a
|
||||
directory-ownership question and (2) writes a cross-tenant capability into the
|
||||
issuer.
|
||||
|
||||
**Tenant blocker resolved in code, 2026-09-09.** On review the choice did not
|
||||
have to be made to unblock: the two resolutions differ in where a human's tenant
|
||||
*comes from*, and an implementation exists that is correct under either. A client
|
||||
registration may now declare a `tenant`, and `humanTenant()` resolves it by four
|
||||
rules — no declaration keeps the directory answer unchanged; a declared zone
|
||||
applies when the directory has placed the user nowhere; agreement passes; and a
|
||||
declared zone that **conflicts** with a directory assignment refuses issuance
|
||||
(`403`, `error_type: tenant_binding`) rather than relabelling the user.
|
||||
|
||||
That refusal is the whole design. A registration can bind a zone for unplaced
|
||||
users and can never move a placed one, so option (2) is available now without
|
||||
writing a general cross-tenant override into the issuer, and if the owners prefer
|
||||
option (1) the same code stops supplying the zone and starts enforcing agreement
|
||||
with it — no second migration and no window where a stale registration silently
|
||||
wins. It is safe only because registrations are static and deployment-owned;
|
||||
`docs/tenant-claim-contract.md` records that this rule must be revisited if
|
||||
dynamic client registration is ever admitted.
|
||||
|
||||
Evidence: `src/internal/server/oidc/human_tenant_test.go`, four cases including
|
||||
the relabel refusal. Verified with teeth — neutering the conflict check makes
|
||||
`TestHumanTenantRefusesToRelabelAPlacedUser` fail rather than pass silently.
|
||||
`docs/tenant-claim-contract.md` carries the resolution table,
|
||||
`docs/approval-engine-auth-contract.md` states the requirement on the approver
|
||||
client, and the provisioning packet now declares `tenant: tenant:platform`.
|
||||
|
||||
**Cleared and registered 2026-09-10.** informed-decision submitted the two
|
||||
strings with the origin already live:
|
||||
|
||||
client_id informed-decision-approver
|
||||
redirect_uri https://decisions.coulomb.social/auth/callback
|
||||
|
||||
The host is `decisions.coulomb.social`, not the `decide.coulomb.social` of an
|
||||
earlier draft. They verified the origin rather than reporting it: `/` and
|
||||
`/auth/callback` both return 200 from 92.205.62.239 on a Let's Encrypt
|
||||
certificate CN=decisions.coulomb.social valid 2026-09-10 to 2026-12-09. The path
|
||||
serves a placeholder while their surface is gated on APPROVAL-WP-0002-T01, which
|
||||
does not affect the registration — the redirect is matched as an exact string at
|
||||
`/authorize` and never fetched.
|
||||
|
||||
Published in `config/service-clients.example.yaml`: public `authorization_code`
|
||||
with S256 PKCE, `audience: approval-engine`, scopes
|
||||
`[openid, approval:read, approval:approve]`, `mfaRequired: true`, declared
|
||||
`tenant: tenant:platform`. No `secretRef` — PKCE is the whole proof — and no
|
||||
`serviceSubject` or `roles`, which validation rejects on a browser client.
|
||||
|
||||
`TestApproverRegistrationShapeIsExact` pins every field, so widening a scope or
|
||||
relaxing MFA fails the build rather than reading as an edit, and asserts the
|
||||
registration passes startup validation — which proves the KEY-WP-0028 tenant
|
||||
exemption holds for the registration that depends on it, not only in a synthetic
|
||||
case.
|
||||
|
||||
Two existing guards fired on the way in, both correctly, and neither was loosened:
|
||||
|
||||
- `TestServiceRegistrationTenantsAreExactPerDecision` refused an unreviewed client
|
||||
carrying a tenant, which is exactly its purpose. The approver was added to its
|
||||
reviewed set as a deliberate act.
|
||||
- `TestServiceRegistrationAudienceAndScopeIsolation` panicked on
|
||||
`secretRef[4:]`, an assumption that was safe while the fixture held only
|
||||
confidential clients. The approver is the first public one; the loop now guards
|
||||
on the `env:` prefix.
|
||||
|
||||
The tenant is declared, and the token says so: `tenant_source` is `registration`,
|
||||
never `directory`. Per gate-house's correction that is adequate for
|
||||
approval-engine's store-isolation gate and **never** for doctrine turning on this
|
||||
person's membership of the zone.
|
||||
|
||||
Not live: this is a published registration fragment. Deployment is owner-side and
|
||||
nothing has been issued to a real approver.
|
||||
|
||||
## Make negative rollout evidence discriminate actual issuer refusal
|
||||
|
||||
```task
|
||||
id: KEY-WP-0013-T06
|
||||
status: done
|
||||
priority: high
|
||||
assignee: the-custodian
|
||||
state_hub_task_id: "65132e1d-10db-5915-a664-83beaf30365a"
|
||||
```
|
||||
|
||||
Critical-path review found `mustFail` accepted any Exchange error: timeout,
|
||||
HTTP 5xx, invalid token or JWKS failure could falsely prove excess-scope denial
|
||||
or predecessor rejection after a successful positive exchange. Preserve a bounded
|
||||
typed provider refusal, require the exact token endpoint/status/error/feature,
|
||||
and prove the checker rejects unrelated failures without exposing provider bodies.
|
||||
Publish the verified image candidate and return its immutable digest to T02.
|
||||
|
||||
Platform's 2026-09-08 return is RPF-WP-0035-T05 and proposed CCR-2026-0017/0018.
|
||||
The custody field is `CLIENT_SECRET`; Kubernetes key `client-secret` and both env
|
||||
names remain as requested. These records cover verifier-side delivery only.
|
||||
Owner approval, client-side retrieval and the actual upstream ID-token issuer
|
||||
proof remain distinct gates. Public discovery currently advertises
|
||||
`https://auth.coulomb.social`; that alone is not the signed-token observation.
|
||||
|
||||
T06 completion: full Go suite and vet passed; published code `dcebd46` and pulled the image by digest `sha256:7ff54c54e63ee172ae9e6e7fd2da96e427352f712343d74626ee6fe0f6f82611`. Container `keycape verify-client --help` confirms the command is present. `docs/approval-clients-rollout.md` and its proposed deployment patch record configuration/issuer, first-provision, single-instance rollout, acceptance and rollback. This closes verifier preparation only; T02 and T05 retain the explicit live dependencies.
|
||||
|
||||
|
||||
## Prepare a bounded upstream issuer observation before client rollout
|
||||
|
||||
```task
|
||||
id: KEY-WP-0013-T07
|
||||
status: done
|
||||
priority: high
|
||||
assignee: the-custodian
|
||||
state_hub_task_id: "62bc6d8e-dbb2-57b6-a30b-472ffd06b497"
|
||||
```
|
||||
|
||||
2026-09-08: T02's actual upstream signed-token issuer gate had no executable
|
||||
observation path. A downstream OpenBao/KeyCape login cannot return the upstream
|
||||
Authelia token; it remains inside the adapter. Prepare `probe-upstream-issuer`
|
||||
and an exact-state temporary route so one attended flow can be verified before
|
||||
changing the normal KeyCape Deployment or its configuration.
|
||||
|
||||
Acceptance: signed issuer/audience/time/nonce proof without token or user-claim
|
||||
output; browser/state binding and one-shot refusal; finite issuer allowlist;
|
||||
ten-minute bound; no downstream credential; current Traefik route isolation;
|
||||
immutable image; reviewed temporary Job/config projection, network policies and
|
||||
owner-reference cleanup. Normal callbacks and the production issuer stay on
|
||||
the existing Deployment. The probe mounts only `config.yaml`, not `key.pem`,
|
||||
and has no Kubernetes API token. That file contains existing credential data;
|
||||
its use by the temporary diagnostic needs deployment-owner admission.
|
||||
|
||||
Source tests, image and deployment packet close this preparation task. The
|
||||
attended live receipt and issuer pin remain in T02, alongside the named CCR
|
||||
reviews and custody/rollout acceptance. Preparing the diagnostic does not
|
||||
approve CCR-2026-0017/0018 or close any live factory gate.
|
||||
|
||||
T07 preparation complete: source `6f33abd`, immutable image
|
||||
`sha256:0c85ed377cae7ae6ca5b5c56b1a52930e706b3cb78009747e42f551e869a22e4`.
|
||||
Complete Go race suite and vet passed; five renderer/collector checks, seven
|
||||
real Traefik 3.6.7 route cases and Railiance server dry-run passed. The packaged
|
||||
command ran as UID 65534 with no network and a read-only filesystem and produced
|
||||
the expected bounded idle-timeout receipt. See `docs/upstream-issuer-proof.md`
|
||||
and `docs/evidence/upstream-issuer-probe.json`. T02 retains the live proof and
|
||||
configuration-owner admission; no production resource or custody value changed.
|
||||
|
||||
## Status 2026-09-30 (CUST-WP-0074)
|
||||
|
||||
All tasks T01-T07 are `done`; nothing in this plan is waiting, so it is
|
||||
`finished` rather than `blocked`. Residual items live in other plans
|
||||
(RPF-WP-0035-T05/T06, AUDIT-WP-0009-T09, KEY-WP-0014-T04). The one open
|
||||
question recorded above, disabling the unpresented create+approve client, is a
|
||||
KeyCape deployment decision to be tracked in a new plan if taken up.
|
||||
|
||||
**2026-10-10:** KeyCape proposed the joint disablement sequence that
|
||||
railiance-platform asked for on 2026-09-15, to railiance-platform, approval-engine
|
||||
and net-kingdom: remove the registration (attended), verify `invalid_client`,
|
||||
unbind the env, retire the ESO delivery, then retire custody. The proposal executes
|
||||
nothing. Each owner confirms their step and the founder schedules the attended window.
|
||||
|
||||
**2026-10-10, amended by the owners.** net-kingdom: step 1 (`keycape-client.py
|
||||
apply --operation remove`, case acceptance CCR-2026-0018) only patches
|
||||
`sso/keycape-config` and does not restart. The step-3 env removal triggers the one
|
||||
rollout, and the new pod starts with neither the client nor the env. Step 2's
|
||||
`invalid_client` check follows that rollout. Rollback runs in reverse: env first,
|
||||
then the client. railiance-platform (RPF-WP-0035-T10): step 4 also retires the
|
||||
ClusterSecretStore, the Kubernetes-auth role and the reader policies, through an
|
||||
attended prune sync run strictly after step 3. Step 5 is a metadata delete, not an
|
||||
archive, run only after the step-2 receipt. Steps 1-5 can share one attended
|
||||
window. Still open: approval-engine's reconfirmation that there is no presenter,
|
||||
and the founder scheduling the window. Before that rollout, run
|
||||
`keycape check-config` on the patched configuration.
|
||||
|
|
@ -0,0 +1,362 @@
|
|||
---
|
||||
id: KEY-WP-0014
|
||||
type: workplan
|
||||
title: "Review native login and client credential lane handoffs"
|
||||
domain: infotech
|
||||
repo: key-cape
|
||||
status: finished
|
||||
owner: codex
|
||||
topic_slug: native-credential-lane-handoff
|
||||
created: "2026-09-05"
|
||||
updated: "2026-09-10"
|
||||
state_hub_workstream_id: "0d003df3-f7d3-5063-8ca0-e1e33f7df74a"
|
||||
---
|
||||
|
||||
Source: ops-warden inbox message 0dd9c7bd-0ecd-42d1-806f-7cc4ba9730ed.
|
||||
The native service exchange and public PKCE login commands are now implemented.
|
||||
Cross-owner rotation and consumer-specific route cutover remain outstanding.
|
||||
|
||||
## Design owner command and custody boundaries
|
||||
|
||||
```task
|
||||
id: KEY-WP-0014-T01
|
||||
status: done
|
||||
priority: medium
|
||||
state_hub_task_id: "0c0a0b61-c19e-5631-9cda-8b2dc0f47d8f"
|
||||
```
|
||||
|
||||
Review ops-warden's existing key-cape-oidc-login proxy and
|
||||
rapp-qonto-keycape-client route contracts. Specify the native interactive login
|
||||
and bounded exchange commands, token delivery, renewal and custody-mediated
|
||||
rotation before implementation. Keep secret custody with OpenBao and avoid
|
||||
retiring the proxy until replacement commands have equivalent verification.
|
||||
|
||||
## Verify handoff delivery evidence
|
||||
|
||||
```task
|
||||
id: KEY-WP-0014-T02
|
||||
status: done
|
||||
priority: low
|
||||
state_hub_task_id: "d9a5de97-b7d5-5599-98c2-eaab32f51495"
|
||||
```
|
||||
|
||||
Ops-warden reports KEY-WP-0009-T04's claimed reply did not arrive. Verify prior
|
||||
receipts for all four named recipients before claiming successful notification.
|
||||
No outbound coordination messages were sent during the 2026-09-05 repo review.
|
||||
|
||||
|
||||
## Implement and verify native caller commands
|
||||
|
||||
```task
|
||||
id: KEY-WP-0014-T03
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "4e46f474-a15e-594f-ae07-ad37a9667d89"
|
||||
```
|
||||
|
||||
Implemented keycape service-token and login with HTTPS discovery, RS256/JWKS
|
||||
verification, exact audience bindings, PKCE/state/nonce, private out-of-Git file
|
||||
delivery and redacted errors. Service Basic authentication now form-decodes
|
||||
credentials. Integration tests exercise the real token/JWKS handlers and browser
|
||||
callback, including forged state, tampering, excess scope and unsafe output.
|
||||
See docs/native-authentication.md for invocation and ownership boundaries.
|
||||
|
||||
|
||||
2026-09-10. The authority question is answered and the blocker is narrowed to a
|
||||
schedulable act. railiance-platform named the executor (platform operator,
|
||||
attended founder session; explicitly not ops-warden, not secrets-engine
|
||||
autonomously, and no unattended agent), the transport (the governed
|
||||
`openbao-platform-admin-login` lane via `warden access ... --exec`), and the
|
||||
authority (founder_required attended OIDC, role=platform-admin). What is missing
|
||||
is a rotation CCR, which they will prepare *if KeyCape asks* — a two-custodian
|
||||
CAS rotation with a service restart being a distinct version-guarded operation
|
||||
rather than an implementation detail of an existing lane.
|
||||
|
||||
**Not asked, deliberately.** Requesting a production credential rotation with a
|
||||
restart window is the operator's decision, not this repository's, so it was put
|
||||
to him rather than answered here (ops-warden msg 10fcc8af).
|
||||
|
||||
Established before recommending anything either way: **this rotation has no
|
||||
incident driver.** KEY-WP-0011 recovered a real exposure on 2026-08-23, and the
|
||||
coordinated rotation that followed replaced the RS256 signing key, the LLDAP bind
|
||||
credential, the Authelia client credential and the privacyIDEA admin token. The
|
||||
`rapp-qonto` client secret was not in that payload — it is an `env:` secretRef
|
||||
resolved from a separate Kubernetes Secret, never inline in `config.yaml`, which
|
||||
config validation enforces. So this is lifecycle hygiene, not remediation, and
|
||||
nobody should be carrying it as leftover incident work. Hygiene can wait for a
|
||||
chosen window; remediation could not.
|
||||
|
||||
Also recorded: `warden route`/`warden plan` previously returned a generic match
|
||||
that read as authorization, and offered read transports against a write need.
|
||||
This repository declined to act on it; railiance-platform independently called it
|
||||
a defect; fixed under WARDEN-WP-0038.
|
||||
|
||||
## Admit rotation and verify consumer handoff
|
||||
|
||||
```task
|
||||
id: KEY-WP-0014-T04
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "e7632c71-9676-5837-a925-7f905e3015c5"
|
||||
```
|
||||
|
||||
Qonto rotation requires admitted custody/provider transaction and rollback
|
||||
semantics; see the exact review sequence in docs/native-authentication.md.
|
||||
The current login proxy yields an OpenBao token, so it cannot be replaced by a
|
||||
JWT-writing command without adapting its consumer contract. Verify that split
|
||||
with the platform owner and ops-warden before route changes. No route was changed.
|
||||
|
||||
2026-09-05 receipt audit: GET /messages/?from_agent=key-cape returned one
|
||||
2026-03 image-publication message and no KEY-WP-0009 handoff receipts for
|
||||
NetKingdom, secrets-engine, ops-warden or railiance-platform. Corrected the
|
||||
unsupported statement in KEY-WP-0009-T04. This proves only that no receipts
|
||||
are present in the current Hub response, not that no message ever existed.
|
||||
The 2026-09-05 audit's finding stands as recorded; the replacement outbound
|
||||
messages were sent on 2026-09-08 (below).
|
||||
|
||||
2026-09-08: `0a6c9d88-cf50-45d8-acdc-99a40747683e` -> ops-warden, readable back
|
||||
via `GET /messages/?from_agent=key-cape`. It puts both open questions to the
|
||||
route and custody owner in one place:
|
||||
|
||||
1. The `key-cape-oidc-login` proxy yields an OpenBao token and `keycape login`
|
||||
yields an issuer JWT — different credential types, different verifiers,
|
||||
different enforcement owners, so not interchangeable. Two admissible options
|
||||
are offered: keep the proxy and run `keycape login` alongside it for JWT
|
||||
consumers, or adapt the route's consumer contract to a JWT and then cut over.
|
||||
Option (a) is recommended until every consumer of the route is enumerated.
|
||||
2. Qonto rotation for `rapp-qonto-keycape-client`, quoting the five-step reviewed
|
||||
sequence from `docs/native-authentication.md` and asking who executes the
|
||||
successor generation and CAS update, in which transport, under what authority.
|
||||
It records that `warden route`/`warden plan` returned no exact admitted lane
|
||||
and that a generic match is not being treated as authorization.
|
||||
|
||||
The KEY-WP-0009 handoff ops-warden reported missing has also now been delivered
|
||||
(`5bc58847-eed5-4164-bb0f-a0d8455e75b8`), closing the KEY-WP-0014-T02 correction.
|
||||
|
||||
Task remains `wait` — no route was changed and no rotation command ships until
|
||||
ops-warden answers (1) and an execution/rollback authority is admitted for (2).
|
||||
|
||||
2026-09-08, second pass — read ops-warden's own catalog rather than waiting for a
|
||||
reply, and it changes the shape of both questions.
|
||||
|
||||
`registry/routing/catalog.yaml`, lane `key-cape-oidc-login`: `intended_owner` was
|
||||
already corrected to key-cape, and `verified: asked-and-waiting` since
|
||||
2026-08-28. **ops-warden is waiting on us, not the other way round.** The lane is
|
||||
also `warden_executes: false` with a `wiki_ref` into `CredentialRouting.md` — a
|
||||
pointer, per their ADR-0001, not a code path with programmatic consumers. So the
|
||||
consumer-contract risk behind question (1) is smaller than assumed: adding
|
||||
`keycape login` alongside retires nothing, and the acceptance we owe them is a
|
||||
statement of ownership, not a cutover.
|
||||
|
||||
Lane `rapp-qonto-keycape-client`: `rotation.owner: key-cape`,
|
||||
`automatable: true`, and `blocked_on` reads "still no key-cape-native
|
||||
exchange/rotation command", `verified: source-read` 2026-08-28. **That blocker is
|
||||
stale.** `keycape service-token` (T03) shipped 2026-09-05 and is exactly the
|
||||
native `client_secret_basic` exchange they record as absent. It does not clear
|
||||
the lane, and should be narrowed rather than closed.
|
||||
|
||||
Shipped here: `keycape verify-client`, which is rotation step 4 and the denial
|
||||
checks step 5 rests on — new exchange verified against deployed JWKS, predecessor
|
||||
refused, excess scope refused, no value printed or written. See
|
||||
`docs/native-authentication.md`, "Verifying a live registration". The identical
|
||||
predecessor case is treated as a failure, because an unchanged secret is a
|
||||
rotation that did not happen.
|
||||
|
||||
Steps 1-3 remain custody's and are deliberately not automated. Task stays `wait`
|
||||
on exactly two answers: which option ops-warden takes for the login proxy, and
|
||||
who executes the successor generation and CAS update under what authority. No
|
||||
route changed.
|
||||
|
||||
Both catalog corrections were sent, receipts readable via
|
||||
`GET /messages/?from_agent=key-cape`:
|
||||
|
||||
- `12f1bdfa-b240-414b-aa49-128a4d2acf20` — accepts ownership of lane
|
||||
`key-cape-oidc-login`, answering the WARDEN-WP-0033-T04 follow-up they have
|
||||
carried as `asked-and-waiting` since 2026-08-28. States the boundary the lane's
|
||||
`fetch_command` crosses: key-cape owns browser authentication and the identity
|
||||
token; the `netkingdom` auth mount, `role=<domain>` mapping, token store and
|
||||
enforcement of the resulting OpenBao token are not ours. Supplies the source
|
||||
paths they need to move the lane to `verified: source-read`, and records our
|
||||
revised, lower estimate of the consumer risk — a pointer lane under ADR-0001
|
||||
retires nothing when `keycape login` runs alongside it — while asking them to
|
||||
correct us if the catalog understates its consumers.
|
||||
- `08d42f47-8e19-4976-98f5-dd259e7b1225` — reports lane
|
||||
`rapp-qonto-keycape-client`'s blocker as stale against its 2026-08-28
|
||||
source-read, since `keycape service-token` is the native `client_secret_basic`
|
||||
exchange it records as absent and `keycape verify-client` is its rotation step 3
|
||||
verbatim. Asks them to **narrow** it to steps 1-2 rather than clear it, with
|
||||
proposed replacement text, and notes `rotation.automatable: true` is now
|
||||
half-true and should say so precisely rather than be flipped either way.
|
||||
|
||||
Neither message asks for a route change and neither was one.
|
||||
|
||||
2026-09-08/09 — ops-warden answered both, and the second answer matters more than
|
||||
the question did.
|
||||
|
||||
**Item 1 answered: option (a).** Keep the proxy as-is and run `keycape login`
|
||||
alongside it, "for your reason rather than out of caution -- they are different
|
||||
credential types with different verifiers, so this is not a cutover deferred, it
|
||||
is two things that were never one." They verified our pointer-layer reading
|
||||
rather than accepting it, and added a fact we did not have: `warden access`
|
||||
already excludes `is_login` lanes from raw-value streaming
|
||||
(WARDEN-WP-0032-T05). Lane `key-cape-oidc-login` moved to `owner-confirmed` with
|
||||
our boundary written in verbatim (commit c133004). It stays `interim` on purpose:
|
||||
per their ADR-0003 a lane retires when a front door exists, not when ownership is
|
||||
agreed, and by our own analysis `keycape login` is not that front door. The entry
|
||||
now names the retirement condition instead of implying we owe one.
|
||||
|
||||
**Item 2 answered: nobody owns it, admittedly.** Steps 1-2 mutate custody on
|
||||
platform workload paths, which is railiance-platform's, with `secrets-engine exec`
|
||||
as the admitted transport. ops-warden declined to take the act on the grounds
|
||||
that fronting the read lane is not authority to mutate — their ADR-0002 and
|
||||
ADR-0005 — and routed it themselves rather than describing the route and leaving
|
||||
it to us. Lane `rapp-qonto-keycape-client` narrowed exactly as asked and moved
|
||||
`source-read` -> `owner-confirmed`, crediting the correction to us rather than to
|
||||
a re-read they did not perform. Rotation step 3 now points at
|
||||
`keycape verify-client` instead of restating its assertions.
|
||||
|
||||
**They found a defect in their own front door because of our caution, and told
|
||||
us.** Running our need through `warden plan` returns `verdict: autonomous`,
|
||||
`lane_id: rapp-qonto-keycape-client`, answered with three read transports — a
|
||||
need containing *generate* and *CAS-write* inherits the verdict of the lane that
|
||||
reads the same path, because `warden plan` has no read-versus-mutate intent at
|
||||
all. `autonomous` is documented as the signal to proceed without the founder.
|
||||
Raised as WARDEN-WP-0038. Their stated conclusion, which we should not soften: "A
|
||||
counterparty declining to trust our output is not a control, and we are not going
|
||||
to record it as one."
|
||||
|
||||
Standing instruction from them until WP-0038 lands, recorded here so it is not
|
||||
lost in an inbox: **treat a `warden plan` verdict on any need containing a write,
|
||||
rotate or provision act as unreliable.** That applies to this repository whenever
|
||||
it plans a rotation.
|
||||
|
||||
**On `automatable`, they did not do what we suggested and said so.** We asked
|
||||
them not to flip the boolean and to state the half-truth precisely; the schema has
|
||||
one boolean whose only consumer is a future executable driver, and a driver told
|
||||
`true` would attempt the custody steps. So it is `false`, failing safe, with the
|
||||
precision moved into per-step notes. They offered to add a per-step field if we
|
||||
would rather. We would not: a lane-level flag that fails safe plus per-step notes
|
||||
is the right shape, and asking them to change a schema for our convenience would
|
||||
be worse than the imprecision. No reply needed on that point.
|
||||
|
||||
T04 stays `wait`: the rotation authority now has a named owner
|
||||
(railiance-platform, transport `secrets-engine exec`) but does not yet exist, and
|
||||
nothing here changed a route.
|
||||
|
||||
2026-09-09, guarding against a misreading that is now easy to make. The approval
|
||||
clients went live and `docs/evidence/2026-09-09-keycape-verifier-admission.json`
|
||||
records a passing verifier run, so there is now a committed receipt in this repo
|
||||
that looks like rotation evidence and is not. It records
|
||||
`real_predecessor_rotation_tested: false` and `observed_wall_clock_expiry: false`,
|
||||
and under `limits`, `actual_predecessor_rotation_observed: false` and
|
||||
`wall_clock_jwt_expiry_observed: false`. railiance-platform stated the same two
|
||||
gaps unprompted from their side (`ae6ff4bc`): "do not cite this receipt as
|
||||
rotation evidence."
|
||||
|
||||
So `keycape verify-client`'s predecessor rejection has been exercised only
|
||||
against unit tests and a first provisioning where predecessor and successor were
|
||||
never distinct. Rotation step 4 is implemented and unproven, which is a different
|
||||
state from either "missing" or "done", and T04 is where that distinction lives.
|
||||
|
||||
**2026-09-10: the authority is answered. What remains is an artifact and a
|
||||
decision.** railiance-platform answered the routed question (`7c7228ac`, relayed
|
||||
by ops-warden `b14655b3`):
|
||||
|
||||
- **Who:** the platform operator, attended, as a founder-attended session. Not
|
||||
ops-warden, not secrets-engine autonomously, and no unattended agent —
|
||||
railiance-platform named themselves in that exclusion too.
|
||||
- **Transport:** the governed `openbao-platform-admin-login` lane, invoked only
|
||||
through `warden access openbao-platform-admin-login --exec -- <command>` with a
|
||||
unique metadata receipt path. The owner command is never run directly.
|
||||
- **Authority:** `founder_required` attended OIDC via netkingdom
|
||||
`role=platform-admin`. Nothing else in that repo carries write authority
|
||||
against `platform/workloads/`.
|
||||
|
||||
**Missing: a rotation CCR.** They will not raise one speculatively, and their
|
||||
reason is sound — a two-custodian CAS rotation with a service restart is a
|
||||
distinct version-guarded operation, not an implementation detail of an existing
|
||||
lane. It must name the CAS precondition and expected version on *both*
|
||||
custodians, sibling-field preservation on the KV write, the restart window,
|
||||
bounded predecessor retention for rollback and denial checks, and the
|
||||
reconcile-to-same-version step on failure. That last is step 5 of the sequence in
|
||||
`docs/native-authentication.md`, so the reviewed sequence is what the CCR encodes.
|
||||
|
||||
**Blocked on a decision that is not KeyCape's to take unilaterally.** Their offer:
|
||||
"if key-cape wants the rotation scheduled, say so and I will prepare it under
|
||||
RPF-WP-0035 with both custodians in one window." ops-warden deliberately did not
|
||||
ask on our behalf — correctly, per their ADR-0003, since implying an owner request
|
||||
from a routing errand is quiet ownership. So T04 now waits on the repository owner
|
||||
deciding whether the Qonto rotation should happen, and when. Nothing indicates
|
||||
compromise; this is hygiene plus the first real exercise of rotation step 4, which
|
||||
is why it is a judgement call rather than an obvious yes.
|
||||
|
||||
**Precedent offered, and it is the right one.** The identical two-custodian shape
|
||||
was exercised 2026-09-09 for the approval clients: attended founder session,
|
||||
OpenBao KV custody plus a Kubernetes-delivered consumer copy, compatible pinned
|
||||
rollout, protected recovery retained, and rollback that restored configuration and
|
||||
image and detached delivery before a clean resume. Their
|
||||
`scripts/keycape_approval_custody.py` (activate / resume-activate / verify) is the
|
||||
shape a rapp-qonto rotation should follow rather than a bare `bao kv patch` — for
|
||||
exactly the provider/consumer consistency reason this task gave when it declined
|
||||
to ship a wrapper around a raw KV write. They endorsed that refusal unprompted.
|
||||
|
||||
**And the `warden plan` defect is fixed** (WARDEN-WP-0038, 2026-09-10). The Qonto
|
||||
need now returns `founder_required` naming the write owner, and read transports
|
||||
are no longer offered against a write need. ops-warden's closing line is worth
|
||||
keeping: "Your caution was load-bearing and it should not have had to be."
|
||||
|
||||
If the rotation is scheduled, it is also the first opportunity to close the
|
||||
`real_predecessor_rotation_tested: false` gap recorded above — `verify-client`
|
||||
with `-previous-secret-env` is exactly step 4, and the predecessor would finally
|
||||
be genuinely distinct.
|
||||
|
||||
**Decision, 2026-09-10: not yet.** The repository owner declined to schedule the
|
||||
rotation now. Recorded with the reasoning so it is revisited on evidence rather
|
||||
than re-litigated from scratch: nothing indicates compromise, the offer stands
|
||||
open, and the estate already has one founder-attended window pending for the two
|
||||
fail-closed startup changes. Rotating for hygiene alone, in a second attended
|
||||
session, against a credential with no incident attached, is work the owner chose
|
||||
not to spend now.
|
||||
|
||||
This is a deferral, not a refusal, and the thing that should reopen it is a
|
||||
change in evidence rather than the passage of time: an actual exposure or
|
||||
suspected one (see KEY-WP-0011), the Qonto secret's age becoming a stated concern,
|
||||
a consumer requiring proof of rotation, or a decision to prove rotation step 4
|
||||
before relying on it elsewhere. Any of those makes the answer different; a
|
||||
calendar date does not.
|
||||
|
||||
**Closed 2026-09-10.** The paragraph above left T04 `wait` with nothing
|
||||
outstanding, which is a contradiction: `wait` means waiting or blocked, and this
|
||||
was neither. Re-reading the task against its own title settles it — *admit
|
||||
rotation and verify consumer handoff* — and both halves are delivered:
|
||||
|
||||
- **Rotation admitted.** The semantics were reviewed and published
|
||||
(`docs/native-authentication.md`), the executor and authority are named
|
||||
(platform operator, attended founder session, governed
|
||||
`openbao-platform-admin-login` lane, `founder_required` attended OIDC via
|
||||
`role=platform-admin`), the transport is admitted, and the CCR will be prepared
|
||||
on request. Step 4 shipped here as `keycape verify-client`, treating an
|
||||
identical predecessor as a failure because an unchanged secret is a rotation
|
||||
that did not happen. Steps 1–3 are custody's and are deliberately not
|
||||
automated by this repository.
|
||||
- **Consumer handoff verified.** ops-warden took option (a) for the reason we
|
||||
gave rather than out of caution — the proxy yields an OpenBao token and
|
||||
`keycape login` an issuer JWT, so they were never one thing to cut over
|
||||
between. Lane `key-cape-oidc-login` is `owner-confirmed` with our boundary
|
||||
written in verbatim, and `rapp-qonto-keycape-client` was narrowed to steps 1–2
|
||||
rather than cleared. No route was changed.
|
||||
|
||||
*Executing* a rotation was never this task's deliverable, and treating the
|
||||
owner's deferral as an open item would have kept a task open against work it was
|
||||
never scoped to do. The deferral stands on its own above, revisited on evidence.
|
||||
|
||||
Two residuals are recorded rather than carried here, because they belong to
|
||||
whoever next opens a window: `real_predecessor_rotation_tested: false` and
|
||||
`observed_wall_clock_expiry: false`. `verify-client -previous-secret-env` is
|
||||
ready to close the first the moment a genuinely distinct predecessor exists.
|
||||
|
||||
Also standing, from ops-warden and not to be lost: until WARDEN-WP-0038 lands,
|
||||
**treat a `warden plan` verdict on any need containing a write, rotate or
|
||||
provision act as unreliable** — it inherits the verdict of the lane that reads
|
||||
the same path, and `autonomous` is documented as the signal to proceed without
|
||||
the founder. That applies to this repository whenever it plans a rotation.
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
---
|
||||
id: KEY-WP-0015
|
||||
type: workplan
|
||||
title: "Reconcile repository scope with implementation and intent"
|
||||
domain: infotech
|
||||
repo: key-cape
|
||||
status: finished
|
||||
owner: codex
|
||||
topic_slug: scope-intent-assessment
|
||||
created: "2026-09-05"
|
||||
updated: "2026-09-05"
|
||||
state_hub_workstream_id: "14a7312b-ec92-5640-8b26-cbaa4753c18e"
|
||||
---
|
||||
|
||||
## Inventory implementation and document scope gaps
|
||||
|
||||
```task
|
||||
id: KEY-WP-0015-T01
|
||||
status: done
|
||||
priority: medium
|
||||
state_hub_task_id: "99be1489-d9e5-53b1-9c82-75dc961131f7"
|
||||
```
|
||||
|
||||
Reviewed source revision b989de4, INTENT, SCOPE, runtime composition, authentication
|
||||
commands, adapters, migration/validator code, tests, packaging and open workplans.
|
||||
Updated SCOPE.md to describe implemented surfaces and their actual limits.
|
||||
Recorded evidence, ten prioritized gaps and closure criteria in
|
||||
history/2026-09-05-011726-scope-intent-assessment.md. INTENT and implementation are
|
||||
unchanged. Findings are assessment results, not implementation completion claims.
|
||||
Loading…
Add table
Add a link
Reference in a new issue