docs: preserve reviewed NetKingdom session artifacts
Some checks failed
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s
Reject generated-only commits / generated-commit-guard (push) Has been cancelled

This commit is contained in:
codex 2026-11-01 23:36:38 +01:00
parent 24e9be4f99
commit be0f891619
50 changed files with 7398 additions and 0 deletions

View file

@ -0,0 +1,198 @@
# T03: protect the earlier cmctl key and reconcile its exact public request
Source review only under existing RCLUSTER-WP-0018-T03 / NK-WP-0049-T35/T33.
No canonical source changed, cmctl installed/executed, key generated/read,
CertificateRequest created, cluster mutation or residual implementation occurred.
Frozen TLS disposition and all native-runtime R1–R3 receipts stay untouched.
The missing behavior is small but real: cmctl writes its local private key
before its CertificateRequest Create. The existing NetKingdom session registers
its own later archive/Docker workdir and reaper; it does not cover those earlier
key bytes or a Kubernetes request accepted after a lost client response.
## Existing source and exact reuse
- NetKingdom isolated-keycape-session.py: register_workdir and cleanup_workdir
already bind /dev/shm/nk49-keycape-{run_id}-{eight_random_chars}, UID/mode0700,
device/inode and refuse replacement, symlink, hardlink, cross-device/special
objects. Its dirfd-based recursive cleanup can remain unchanged.
- Its isolate_execution / process_facts / pidfd_dead / quiesce_execution show
the existing fresh-session and pidfd contract. Its start_reaper/reaper_main
are specifically Docker/session-bound and must not be called with an invented
empty Docker session for issuance. Group-only membership also does not prove
removal of session-escaped credential-plugin descendants.
- Its persist_receipt/checkpoint seam records obligations before archive
plaintext. T03 must similarly register *before cmctl*, not after output exists.
- NetKingdom capture-identity.delete_created and Platform
native_factory_acceptance use Kubernetes raw DELETE with DeleteOptions UID
preconditions (the latter also Foreground propagation). Reuse that exact API
idiom in the TLS owner helper; do not broaden identity-capture's Pod/NP whitelist.
- The locally tested native-runtime R3 supervisor demonstrates subreaper/pidfd
descendant cleanup, including dead-orphan reaping and live-leftover refusal.
That is relevant source evidence, not an admitted TLS utility or native cause
proof. Do not import a Custodian packet at runtime or silently copy its grant.
## Minimal owner source delta, before any key
Implement one bounded T03 orchestration helper through the existing Cluster /
NetKingdom ownership boundary; retain the six-argument original recovery launcher.
No new broker, CA provider, auth method, TLS policy enum or approval field.
The reviewable interfaces are:
1. `register_tls_staging(run_id, caller_binding, deadline)` creates only the
existing guard-compatible fresh0700 directory. Before creation/key access,
verify the actual selected /dev/shm mount is writable tmpfs, bind mount ID /
device, owner UID, no aliases, and available bounded space. Then call existing
register_workdir and checkpoint only public path/device/inode/run facts.
The existing register_workdir alone does NOT check tmpfs before use; current
cleanup_workdir checks it only at deletion. Move that check earlier in this
new caller; no disk /tmp fallback. This sandbox's /dev/shm is actually ro,
so no usable private staging or local tmpfs lifecycle proof is claimed here.
2. `start_tls_reaper(staging, execution, request_intent)` starts an independent
surviving observer and waits for its registration acknowledgment BEFORE
spawning cmctl. Bind controller pidfd/start/UID and a single monotonic deadline.
The observer owns/reaps the exact cmctl and subsequent original-launcher
process trees as a subreaper; pidfd-bound actual descendants cover setsid
escapes. Preserve existing fresh-session guard and avoid inherited/shared
process-group kills. On timeout/parent loss stop/wait those exact descendants
before cleanup. A missing mapping/observer or survivor means cleanup unverified,
never an inferred success. Reap already-dead adopted direct-child zombies
without stealing managed leader exit status; retain the R3 local counterexample.
3. `run_issuance(binding, staging, run_id)` uses the admitted exact cmctl binary,
unchanged R1 flags and original Certificate spec, but puts one unique run label
and source/proposal annotation in its *local template*. The pinned cmctl source
copies those labels/annotations into the CertificateRequest. Pre-register
exact target/ns/name, template/spec hash, expected issuer and an unresolved
submit obligation before spawn. Output key/cert paths must be absent, under
the held private directory, with umask077. Discard raw stdout/stderr; no private
path/content/diagnostic dump enters shared receipts. The binary source uses
os.WriteFile0600 without O_EXCL/O_NOFOLLOW, so it relies on this previously
verified private directory/absent-output boundary; it is not a safe arbitrary
output-path primitive by itself.
4. `observe_request(intent, private_custody)` uses only the bound owner API,
exact name and bounded public field projection. Require same cluster identity,
namespace/name, fresh run marker, fixed issuer/usages/SAN/CSR semantics and
the admitted API caller. Inside future approved private custody, derive only
the held key's public SPKI commitment and compare it with the actual signed
CSR; expose only public commitment/CSR hash and pairing boolean. Check the
CSR signature/SANs. Checkpoint the actual UID/resourceVersion and expected
public spec hash before any remote delete or private-directory cleanup.
The run marker alone is not proof of key/caller ownership.
5. `delete_request_uid(bound)` uses the exact contract in delete-request-contract.json,
then independently verifies that UID's absence and the actual owner-UID
descendant chain. Normal cert-manager garbage collection is preferred; explicit
cleanup of a stuck child needs its recorded UID/owner chain and scoped owner
review. Do not delete by label, prefix, wildcard or name alone.
6. `finish_or_abort(...)` persists an immutable bounded public checkpoint BEFORE
private deletion: tool/source/target/run, process exits/public facts,
certificate/trust hashes, request UID/spec/CSR commitments, descendant cleanup
dispositions, pending API/daemon duties. Reuse cleanup_workdir only after exact
local child quiescence and verified staging identity. If public remote cleanup
is uncertain, local key/plaintext cleanup can still proceed after quiescence,
PROVIDED the exact public request obligations/commitments have been durably
retained; report separate local and remote cleanup results, not global success.
If required commitments cannot be retained, fail closed and leave the exact
protected staging for attended custody recovery, rather than losing ownership
proof or broadly deleting unknown resources. No blind retry after uncertainty.
`staging-reaper-contract.json` fixes the proposed nonrenewable bounds: issuance
330 seconds including cmctl's inner300-second fetch wait; existing recovery
child at most1800 seconds; one total private execution deadline2130 seconds,
then at most60 seconds of clipped cleanup (2190 total). TERM0.5, KILL/reap3,
watchdog wait8; each API request at most30 seconds clipped to cleanup remainder.
Set umask077, disable core dumps, and bound each cmctl output to1MiB with the
same bounded child runner before generation. Tmpfs is
not claimed unswappable or isolated from host root/hypervisor/crash custody.
These exact proposed bounds need source lifecycle proof before use; they are
not a statement that today's helper already enforces them.
The cmctl child gets DEVNULL input/output/error and no custody fd. Do not copy
that IO policy onto the original six-argument launcher: it explicitly requires
an attended TTY and invokes the existing hidden age prompt / sealed-fd custody
helper. Preserve that original owner terminal path and its existing custody
contract, without a new PTY proxy, prompt capture or key passage to the observer.
The observer gets only pidfds/public metadata and must close every unrelated fd;
its deadline also bounds a stalled attended prompt. Prove this exact distinction
with a synthetic terminal fixture before integration; the runtime R3 runner's
DEVNULL default is not an admitted drop-in for the attended recovery launcher.
The independent observer must outlive the creator, not rely on a shell trap or
SIGTERM-only child wait. It must not receive a newly fetched kube credential or
private key in argv/environment/receipt. Exact use of the existing bound owner
API caller remains in that owner's attended execution scope. If that caller
uses an exec auth plugin, its actual descendants are covered by the same reaper;
there is no implicit admission for unrelated credential helpers or new routes.
## One custody host and explicit Kubernetes binding
R1's candidate /home/worsch cmctl path and --context default do not establish
where the private key and original local-Docker recovery run will live. R1's
observed default context was on railiance01. Current environment orientation says
workstation kubeconfig was repaired in September but still requires explicit
node-IP verification and node-local mutation practice; a context name alone is
not a cluster binding. This is an unverified command binding, not proof that
workstation/default presently targets a different cluster.
Keep cmctl, tmpfs/reaper and the original six-argument launcher on ONE actually
admitted custody host. Do not move only cmctl to railiance01 and invent a private
key SCP/download. The existing T33/T35 exact action review must bind that host,
account, cmctl absolute path/hash, API server/CA public identity and actual node
92.205.62.239 through the existing owner profile. If the actual launcher is
workstation-local, direct networked issuance needs that explicit owner/founder
execution disposition under the existing change gate; if all run node-local,
first demonstrate the existing launcher/Docker/independent-owner interfaces there.
No host choice, kube credential read or private transfer is assumed by this packet.
This is why the source delta is reviewable while a native argv cannot yet be
honestly called executable.
## Exact lifecycle/counterexamples to require from T03 implementation
- Observer/mount/target/source refusal: zero cmctl spawn and zero key output.
- Success: fake network child writes a synthetic PRIVATE_SENTINEL under actual
guarded tmpfs, public fixture API returns the exact UID, original launcher
consumer reads through the same custody boundary, UID-delete body matches,
held inode removed after positive process proof. No real admin canary/key.
- Failure before API, key-only failure, timeout, controller SIGKILL, setsid child,
dead adopted zombie, uninterruptible/unknown child and output bound. Verify
no leaked sentinel and no unrelated sibling signaled. Replaced inode,
symlink/hardlink or wrong mount must refuse deletion/use.
- API accepted but reply lost: preserve submit uncertainty, later exact-name
reconciliation accepts only held SPKI/run/spec/caller binding and actual UID.
A single NotFound after killed client does not establish no late Create.
- Wrong cluster/ns/name/run/SPKI/spec or swapped UID: no delete. UID-precondition
conflict leaves replacement untouched. GC/finalizer timeout and unreachable
API retain outstanding request/child UIDs, not successful cleanup.
- Independent immutable public receipt exists before private cleanup; receipt
write failure leaves protected staging. Real issuance and cluster cleanup
remain separate supervised native tests after source proof/review.
These are requested meaningful generated lifecycle tests, not tests run by this
source-only packet. Existing native session/daemon cleanup facts and R3 process
fixtures are reused with their limits; no broad dummy rerun was performed.
## Safe source-only owner publication now
The one-file owner-doc.patch is ready for source-owner review against its captured
preimage. It clarifies renewable transport versus archived signing identity,
retains per-attempt TLS/trust hashes and all existing T02–T04/native duties, records
the already-located guide, and names the concrete T03 delta above. It makes no
owner/founder assent or native compatibility claim. No task status/UUID change or
new T06 is needed for this clarification; do not publish the old R1 task addition
as completed native/staging work. Root may review/publish that doc alone using
normal owner publication while canonical code and frozen packets remain unchanged.
The original disaster guide is already located: publication commit
31f483966cdfca4df1d326dee3ec0b6d33ea0e5e, Backup account,
railiance-backups/net-kingdom-identity/net-kingdom-recovery-57d1940947aa25ff-START-HERE.md.
The recorded three-object readback is historical; this source work does not
re-read StorageShare or request another original TLS backup. The guide is
identity-only and does not establish ingress TLS-key coverage.
Renewed trusted leaf material remains compatible with the existing per-attempt
admission; no historical leaf-byte equality or extra TLS mode is introduced.
Archived original OIDC issuer/signing keys/JWKS and registration/policy checks
remain unchanged. HTTP-01 still depends on a surviving Kubernetes/cert-manager/
Traefik/DNS/domain-routing/port80/ACME path. It neither causes nor proves recovery
from cluster loss, and today's Ready/public-chain observations do not establish
original TLS custody, a future issuance result or an external-vantage HTTP01 test.
Independent cluster-loss recovery/provider/domain-control remains T02–T04 scope.

View file

@ -0,0 +1,340 @@
/*
Copyright 2020 The cert-manager Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package certificaterequest
import (
"context"
"encoding/pem"
"errors"
"fmt"
"os"
"time"
apiutil "github.com/cert-manager/cert-manager/pkg/api/util"
cmapi "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
cmmeta "github.com/cert-manager/cert-manager/pkg/apis/meta/v1"
"github.com/cert-manager/cert-manager/pkg/util/pki"
"github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/cli-runtime/pkg/resource"
"k8s.io/kubectl/pkg/util/templates"
"github.com/cert-manager/cmctl/v2/pkg/build"
"github.com/cert-manager/cmctl/v2/pkg/convert"
"github.com/cert-manager/cmctl/v2/pkg/factory"
)
var (
// Dedicated scheme used by the ctl tool that has the internal cert-manager types,
// and their conversion functions registered
scheme = convert.Scheme
)
// Options is a struct to support create certificaterequest command
type Options struct {
// Name of file that the generated private key will be stored in
// If not specified, the private key will be written to <NameOfCR>.key
KeyFilename string
// If true, will wait for CertificateRequest to be ready to store the x509 certificate in a file
// Command will block until CertificateRequest is ready or timeout as specified by Timeout happens
FetchCert bool
// Name of file that the generated x509 certificate will be stored in if --fetch-certificate flag is set
// If not specified, the private key will be written to <NameOfCR>.crt
CertFileName string
// Path to a file containing a Certificate resource used as a template
// when generating the CertificateRequest resource
// Required
InputFilename string
// Length of time the command blocks to wait on CertificateRequest to be ready if --fetch-certificate flag is set
// If not specified, default value is 5 minutes
Timeout time.Duration
genericclioptions.IOStreams
*factory.Factory
}
// NewOptions returns initialized Options
func NewOptions(ioStreams genericclioptions.IOStreams) *Options {
return &Options{
IOStreams: ioStreams,
}
}
// NewCmdCreateCR returns a cobra command for create CertificateRequest
func NewCmdCreateCR(setupCtx context.Context, ioStreams genericclioptions.IOStreams) *cobra.Command {
o := NewOptions(ioStreams)
cmd := &cobra.Command{
Use: "certificaterequest",
Aliases: []string{"cr"},
Short: "Create a cert-manager CertificateRequest resource, using a Certificate resource as a template",
Long: templates.LongDesc(`
Create a new CertificateRequest resource based on a Certificate resource, by generating a private key locally and create a 'certificate signing request' to be submitted to a cert-manager Issuer.`),
Example: templates.Examples(build.WithTemplate(setupCtx, `
# Create a CertificateRequest with the name 'my-cr', saving the private key in a file named 'my-cr.key'.
{{.BuildName}} create certificaterequest my-cr --from-certificate-file my-certificate.yaml
# Create a CertificateRequest in namespace default, provided no conflict with namespace defined in file.
{{.BuildName}} create certificaterequest my-cr --namespace default --from-certificate-file my-certificate.yaml
# Create a CertificateRequest and store private key in file 'new.key'.
{{.BuildName}} create certificaterequest my-cr --from-certificate-file my-certificate.yaml --output-key-file new.key
# Create a CertificateRequest, wait for it to be signed for up to 5 minutes (default) and store the x509 certificate in file 'new.crt'.
{{.BuildName}} create certificaterequest my-cr --from-certificate-file my-certificate.yaml --fetch-certificate --output-cert-file new.crt
# Create a CertificateRequest, wait for it to be signed for up to 20 minutes and store the x509 certificate in file 'my-cr.crt'.
{{.BuildName}} create certificaterequest my-cr --from-certificate-file my-certificate.yaml --fetch-certificate --timeout 20m
`)),
ValidArgsFunction: factory.ValidArgsListCertificateRequests(&o.Factory),
PreRunE: func(cmd *cobra.Command, args []string) error {
return o.Validate(args)
},
//nolint:contextcheck // False positive
RunE: func(cmd *cobra.Command, args []string) error {
return o.Run(cmd.Context(), args)
},
}
cmd.Flags().StringVar(&o.InputFilename, "from-certificate-file", o.InputFilename,
"Path to a file containing a Certificate resource used as a template when generating the CertificateRequest resource")
cmd.Flags().StringVar(&o.KeyFilename, "output-key-file", o.KeyFilename,
"Name of file that the generated private key will be written to")
cmd.Flags().StringVar(&o.CertFileName, "output-certificate-file", o.CertFileName,
"Name of the file the certificate is to be stored in")
cmd.Flags().BoolVar(&o.FetchCert, "fetch-certificate", o.FetchCert,
"If set to true, command will wait for CertificateRequest to be signed to store x509 certificate in a file")
cmd.Flags().DurationVar(&o.Timeout, "timeout", 5*time.Minute,
"Time before timeout when waiting for CertificateRequest to be signed, must include unit, e.g. 10m or 1h")
o.Factory = factory.New(cmd)
return cmd
}
// Validate validates the provided options
func (o *Options) Validate(args []string) error {
if len(args) < 1 {
return errors.New("the name of the CertificateRequest to be created has to be provided as argument")
}
if len(args) > 1 {
return errors.New("only one argument can be passed in: the name of the CertificateRequest")
}
if o.InputFilename == "" {
return errors.New("the path to a YAML manifest of a Certificate resource cannot be empty, please specify by using --from-certificate-file flag")
}
if o.KeyFilename != "" && o.CertFileName != "" && o.KeyFilename == o.CertFileName {
return errors.New("the file to store private key cannot be the same as the file to store certificate")
}
if !o.FetchCert && o.CertFileName != "" {
return errors.New("cannot specify file to store certificate if not waiting for and fetching certificate, please set --fetch-certificate flag")
}
return nil
}
// Run executes create certificaterequest command
func (o *Options) Run(ctx context.Context, args []string) error {
builder := new(resource.Builder)
// Read file as internal API version
r := builder.
WithScheme(scheme, schema.GroupVersion{Group: cmapi.SchemeGroupVersion.Group, Version: runtime.APIVersionInternal}).
LocalParam(true).ContinueOnError().
NamespaceParam(o.Namespace).DefaultNamespace().
FilenameParam(o.EnforceNamespace, &resource.FilenameOptions{Filenames: []string{o.InputFilename}}).Flatten().Do()
if err := r.Err(); err != nil {
return err
}
singleItemImplied := false
infos, err := r.IntoSingleItemImplied(&singleItemImplied).Infos()
if err != nil {
return err
}
// Ensure only one object per command
if len(infos) == 0 {
return fmt.Errorf("no objects found in manifest file %q. Expected one Certificate object", o.InputFilename)
}
if len(infos) > 1 {
return fmt.Errorf("multiple objects found in manifest file %q. Expected only one Certificate object", o.InputFilename)
}
info := infos[0]
// Convert to v1 because that version is needed for functions that follow
crtObj, err := scheme.ConvertToVersion(info.Object, cmapi.SchemeGroupVersion)
if err != nil {
return fmt.Errorf("failed to convert object into version v1: %w", err)
}
// Cast Object into Certificate
crt, ok := crtObj.(*cmapi.Certificate)
if !ok {
return errors.New("decoded object is not a v1 Certificate")
}
crt = crt.DeepCopy()
if crt.Spec.PrivateKey == nil {
crt.Spec.PrivateKey = &cmapi.CertificatePrivateKey{}
}
signer, err := pki.GeneratePrivateKeyForCertificate(crt)
if err != nil {
return fmt.Errorf("error when generating new private key for CertificateRequest: %w", err)
}
keyData, err := pki.EncodePrivateKey(signer, crt.Spec.PrivateKey.Encoding)
if err != nil {
return fmt.Errorf("failed to encode new private key for CertificateRequest: %w", err)
}
crName := args[0]
// Storing private key to file
keyFileName := crName + ".key"
if o.KeyFilename != "" {
keyFileName = o.KeyFilename
}
if err := os.WriteFile(keyFileName, keyData, 0600); err != nil {
return fmt.Errorf("error when writing private key to file: %w", err)
}
fmt.Fprintf(o.ErrOut, "Private key written to file %s\n", keyFileName)
// Build CertificateRequest with name as specified by argument
req, err := buildCertificateRequest(crt, keyData, crName)
if err != nil {
return fmt.Errorf("error when building CertificateRequest: %w", err)
}
ns := crt.Namespace
if ns == "" {
ns = o.Namespace
}
req, err = o.CMClient.CertmanagerV1().CertificateRequests(ns).Create(ctx, req, metav1.CreateOptions{})
if err != nil {
return fmt.Errorf("error creating CertificateRequest: %w", err)
}
fmt.Fprintf(o.ErrOut, "CertificateRequest %s has been created in namespace %s\n", req.Name, req.Namespace)
if o.FetchCert {
fmt.Fprintf(o.ErrOut, "CertificateRequest %v in namespace %v has not been signed yet. Wait until it is signed...\n",
req.Name, req.Namespace)
err = wait.PollUntilContextTimeout(ctx, time.Second, o.Timeout, false, func(ctx context.Context) (done bool, err error) {
req, err = o.CMClient.CertmanagerV1().CertificateRequests(req.Namespace).Get(ctx, req.Name, metav1.GetOptions{})
if err != nil {
return false, nil //nolint: nilerr // Retry and keep polling until context is cancelled
}
return apiutil.CertificateRequestHasCondition(req, cmapi.CertificateRequestCondition{
Type: cmapi.CertificateRequestConditionReady,
Status: cmmeta.ConditionTrue,
}) && len(req.Status.Certificate) > 0, nil
})
if err != nil {
return fmt.Errorf("error when waiting for CertificateRequest to be signed: %w", err)
}
fmt.Fprintf(o.ErrOut, "CertificateRequest %v in namespace %v has been signed\n", req.Name, req.Namespace)
// Fetch x509 certificate and store to file
actualCertFileName := req.Name + ".crt"
if o.CertFileName != "" {
actualCertFileName = o.CertFileName
}
err = fetchCertificateFromCR(req, actualCertFileName)
if err != nil {
return fmt.Errorf("error when writing certificate to file: %w", err)
}
fmt.Fprintf(o.ErrOut, "Certificate written to file %s\n", actualCertFileName)
}
return nil
}
// Builds a CertificateRequest
func buildCertificateRequest(crt *cmapi.Certificate, pk []byte, crName string) (*cmapi.CertificateRequest, error) {
csrPEM, err := generateCSR(crt, pk)
if err != nil {
return nil, err
}
cr := &cmapi.CertificateRequest{
ObjectMeta: metav1.ObjectMeta{
Name: crName,
Annotations: crt.Annotations,
Labels: crt.Labels,
},
Spec: cmapi.CertificateRequestSpec{
Request: csrPEM,
Duration: crt.Spec.Duration,
IssuerRef: crt.Spec.IssuerRef,
IsCA: crt.Spec.IsCA,
Usages: crt.Spec.Usages,
},
}
return cr, nil
}
func generateCSR(crt *cmapi.Certificate, pk []byte) ([]byte, error) {
csr, err := pki.GenerateCSR(crt)
if err != nil {
return nil, err
}
signer, err := pki.DecodePrivateKeyBytes(pk)
if err != nil {
return nil, err
}
csrDER, err := pki.EncodeCSR(csr, signer)
if err != nil {
return nil, err
}
csrPEM := pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE REQUEST", Bytes: csrDER,
})
return csrPEM, nil
}
// fetchCertificateFromCR fetches the x509 certificate from a CR and stores the
// certificate in file specified by certFilename. Assumes CR is ready,
// otherwise returns error.
func fetchCertificateFromCR(req *cmapi.CertificateRequest, certFileName string) error {
// If CR not ready yet, error
if !apiutil.CertificateRequestHasCondition(req, cmapi.CertificateRequestCondition{
Type: cmapi.CertificateRequestConditionReady,
Status: cmmeta.ConditionTrue,
}) || len(req.Status.Certificate) == 0 {
return errors.New("CertificateRequest is not ready yet, unable to fetch certificate")
}
// Store certificate to file
err := os.WriteFile(certFileName, req.Status.Certificate, 0600)
if err != nil {
return fmt.Errorf("error when writing certificate to file: %w", err)
}
return nil
}

View file

@ -0,0 +1,58 @@
{
"schema": "custodian.keycape-tls-request-cleanup-source-proposal.v1",
"status": "not executed; no actual request UID exists",
"target": {
"node_ip": "92.205.62.239",
"namespace": "sso",
"name": "nk49-kc-drill-20261017-r1",
"group_version": "cert-manager.io/v1",
"resource": "certificaterequests"
},
"create_binding": {
"run_label": "net-kingdom/recovery-run=<bound run_uuid before key output>",
"source_annotation": "net-kingdom/tls-reissue-source=<reviewed source sha256>",
"expected_issuer": {
"group": "cert-manager.io",
"kind": "ClusterIssuer",
"name": "letsencrypt-prod"
},
"public_spki_and_csr_hash": "derived/verified inside separately approved private custody; no key exposed",
"caller_and_template_spec_hash": "exact admitted owner caller plus frozen public input"
},
"delete_argv_template": [
"<same admitted owner kubectl>",
"--request-timeout=30s",
"delete",
"--raw",
"/apis/cert-manager.io/v1/namespaces/sso/certificaterequests/nk49-kc-drill-20261017-r1",
"-f",
"-"
],
"stdin_payload_template": {
"apiVersion": "v1",
"kind": "DeleteOptions",
"preconditions": {
"uid": "<actual observed and custody-bound CertificateRequest UID>"
},
"propagationPolicy": "Foreground"
},
"required_before_delete": [
"fresh cluster/account binding",
"exact observed UID plus held run/spec/SPKI/CSR/caller commitments",
"durable public request and descendant obligations; local writer quiescent if submission was uncertain"
],
"required_after_delete": [
"same recorded UID is absent or deletion remains uncertain",
"different UID at same name remains untouched; record collision",
"actual Order->CertificateRequest, Challenge->Order and solver->Challenge ownerReference UID chains are reconciled; do not infer from name/label",
"controller/API/finalizer timeout remains unresolved; no wildcard cleanup"
],
"forbidden": [
"kubectl delete certificaterequest NAME without UID preconditions",
"production Certificate/Secret/Ingress mutation",
"broad Order/Challenge/solver deletion",
"one post-timeout NotFound treated as proof no late Create"
],
"unknown_uid": "No delete. Preserve exact name/run/spec/SPKI intent and reconcile actual UID through owner API; unresolved if ownership cannot be established.",
"native_api_calls": 0
}

View file

@ -0,0 +1,8 @@
{
"scope": "this source-preparation sandbox only, not an admitted native custody host",
"mountinfo": [
"945 943 0:65 / /dev/shm ro,nosuid,nodev,noatime master:20 - tmpfs none rw"
],
"usable_private_staging_claimed": false,
"mount_or_tmpfs_write_attempts": 0
}

View file

@ -0,0 +1,17 @@
{
"schema": "custodian.tls-staging-cleanup-source.v1",
"status": "source prepared only",
"native_execution_authorized": false,
"files": {
"OWNER-REVIEW.md": "sha256:f2380217eef95148951f00d0a01b634fe9a2af1cda93bca5ed0652f92157e6e6",
"cmctl-public-source.go": "sha256:0ba0c2d3d4f8530514c8d864b2a9e056f14f763a0b314aa05998f3169ee5bc32",
"delete-request-contract.json": "sha256:8dae54b516d67c2069eeffbf6a3ab9ac391e4f386971cddb4dfbb587143afa86",
"local-tmpfs-readonly-observation.json": "sha256:e7cbc7bd1e1d3e4a4282c87da6c689f341a06a74840ee0ed770368ff0c809270",
"owner-boundary.preimage.md": "sha256:f4bcb1d1832c566f9c87793ed2a461c8cd8a00859dc4ae66cd3c160d7ceefa70",
"owner-boundary.proposed.md": "sha256:6daec76f0c220e433dcc0bfafbd4bb922a4a19606cbd74e76e50be9883ae796b",
"owner-doc.patch": "sha256:df565c58026150cd95833b4472610fc90db1ab5fccc90d2bca36494ca77f3c4f",
"review.json": "sha256:5e72c13f4284fe6c4a418a8af93aacbe3ebdce05b3536723f2626354e3fbec6a",
"source-pins.json": "sha256:de93f3cc6689fad2ee00e39ea93d135950fa13e9cdc32a4718f4bf2f4e7b00ab",
"staging-reaper-contract.json": "sha256:c4d0ce5159708303e1d376ab384a483ac79325b11c85dbd5840891667d65beed"
}
}

View file

@ -0,0 +1,51 @@
# Original KeyCape TLS recovery boundary
Source disposition, 2026-10-17, scoped to railiance-cluster INTENT: cluster runtime,
ingress/certificate plumbing and backup posture. This is source preparation only.
It is not native acceptance, an issuer/key custody assent, a reissue decision or
permission to mutate the cluster.
NetKingdom's ingress source names `kc.coulomb.social`, `sso/kc-tls`,
`ClusterIssuer/letsencrypt-prod` and Traefik termination before KeyCape8080.
NetKingdom owns that namespace intent; railiance-cluster owns cert-manager,
ingress controller and cluster backup posture. No inspected source establishes
who independently holds the original private TLS recovery material when the
ordinary issuer/cluster is unavailable. A missing exact routing-catalog entry
cannot prove absence of an owning subsystem.
The identity archive excludes `kc-tls`. Current S2 backup source encrypts SQLite
state, Helm values and kubeconfig to the same host's `/opt/backup/railiance/cluster`
and retains seven versions. No inspected source proves an actually fresh,
independent offsite object or original certificate/key/trust recovery. Archived
RAIL-BS-WP-0004 is historical local-backup delivery; backlog RCLUSTER-WP-0007-T05
is gated HA issuance/renewal; active RCLUSTER-WP-0017 is signer runtime/storage.
None is an existing live task for this original TLS recovery demand.
`railiance-backup-s2` also stages raw state/Helm output in named `/tmp` paths
without early hard-loss cleanup; `railiance-restore-s2` prints whole-state restore
instructions. Do not use either as an isolated TLS extraction path. RCLUSTER-WP-0018
tracks exact inventory, safe private staging/extraction preparation where required,
independent outage proof and future archive-boundary disposition. No original
ciphertext or historical outcome is rewritten by that work.
`tools/public-tls-metadata.py` is an offline public-only step. After the source
owner identifies independent original **public certificate/chain and trust files**:
```sh
python3 tools/public-tls-metadata.py --public-cert ORIGINAL_PUBLIC_CERT_CHAIN.pem --public-trust ORIGINAL_PUBLIC_TRUST.pem
```
It accepts no private-key argument, bounds regular-file reads, refuses symlinks and
non-certificate PEM material, and reports public hashes/SANs/validity/counts only.
The file names are owner-local placeholders, not a key request. Output establishes
public metadata, not originality, hostname/chain acceptance, reissue permission or
custody. Do not supply a Secret object or combined certificate/key PEM. Fail closed
when original source, trust or exact bindings are missing; no fresh self-signed
trust workaround is admitted.
For FUTURE NetKingdom archives, the source owners must explicitly disposition
original TLS coverage or a reviewed reissue contract: producer/custody owner,
private artifact boundary, offline recovery independence, original public hashes,
scoped capture/read permissions, retention and native verification. Merely adding
`kc-tls` to secret_refs would change capture/permission/custody scope and is not
authorized by this source review. It cannot repair the current archive retroactively.

View file

@ -0,0 +1,143 @@
# Original KeyCape TLS recovery boundary
Source disposition, 2026-10-17, scoped to railiance-cluster INTENT: cluster runtime,
ingress/certificate plumbing and backup posture. This is source preparation only.
It is not native acceptance, an issuer/key custody assent, a reissue decision or
permission to mutate the cluster.
NetKingdom's ingress source names `kc.coulomb.social`, `sso/kc-tls`,
`ClusterIssuer/letsencrypt-prod` and Traefik termination before KeyCape8080.
NetKingdom owns that namespace intent; railiance-cluster owns cert-manager,
ingress controller and cluster backup posture. No inspected source establishes
who independently holds the original private TLS recovery material when the
ordinary issuer/cluster is unavailable. A missing exact routing-catalog entry
cannot prove absence of an owning subsystem.
The identity archive excludes `kc-tls`. Current S2 backup source encrypts SQLite
state, Helm values and kubeconfig to the same host's `/opt/backup/railiance/cluster`
and retains seven versions. No inspected source proves an actually fresh,
independent offsite object or original certificate/key/trust recovery. Archived
RAIL-BS-WP-0004 is historical local-backup delivery; backlog RCLUSTER-WP-0007-T05
is gated HA issuance/renewal; active RCLUSTER-WP-0017 is signer runtime/storage.
None is an existing live task for this original TLS recovery demand.
`railiance-backup-s2` also stages raw state/Helm output in named `/tmp` paths
without early hard-loss cleanup; `railiance-restore-s2` prints whole-state restore
instructions. Do not use either as an isolated TLS extraction path. RCLUSTER-WP-0018
tracks exact inventory, safe private staging/extraction preparation where required,
independent outage proof and future archive-boundary disposition. No original
ciphertext or historical outcome is rewritten by that work.
`tools/public-tls-metadata.py` is an offline public-only step. After the source
owner supplies the **selected public certificate/chain and independent trust files**:
```sh
python3 tools/public-tls-metadata.py --public-cert SELECTED_PUBLIC_CERT_CHAIN.pem --public-trust SELECTED_PUBLIC_TRUST.pem
```
It accepts no private-key argument, bounds regular-file reads, refuses symlinks and
non-certificate PEM material, and reports public hashes/SANs/validity/counts only.
The file names are owner-local placeholders, not a key request. Output establishes
public metadata, not originality, hostname/chain acceptance, reissue permission or
custody. Do not supply a Secret object or combined certificate/key PEM. Fail closed
when selected source, independent trust or exact attempt bindings are missing;
no fresh self-signed trust workaround is admitted. This utility does not require
historical ingress leaf/key bytes when a trusted renewed leaf is selected under
the existing reviewed attempt.
For FUTURE NetKingdom archives, the source owners must explicitly disposition
original TLS coverage or a reviewed reissue contract: producer/custody owner,
private artifact boundary, offline recovery independence, original public hashes,
scoped capture/read permissions, retention and native verification. Merely adding
`kc-tls` to secret_refs would change capture/permission/custody scope and is not
authorized by this source review. It cannot repair the current archive retroactively.
## Renewable TLS leaf and original OIDC signing identity
The archived KeyCape RS256 signing key, issuer URL, service registrations and
policy state are distinct from the public HTTPS leaf and its TLS private key.
The current identity archive has no ingress TLS leaf to compare. A separately
reviewed trusted reissue can preserve kc.coulomb.social and the archived OIDC
signing identity without reproducing historical TLS leaf/key bytes. This is an
explicit owner continuity disposition under NK49-T35, not implicit permission
to replace custody, trust or an outage claim.
The existing isolated admission pins the exact certificate-chain and trust PEM
bytes selected for that attempt; it does not compare them with an archived leaf
fingerprint. Those hashes, certificate/key pairing, hostname/CA/validity checks,
archived issuer equality and original signer/JWKS verification remain required.
The receipt's original HTTPS issuer/trust wording does not assert original leaf
byte equality.
Current source and public resource inventory establish a concrete standard path:
ClusterIssuer/letsencrypt-prod uses ACME HTTP-01 with Traefik. Upstream cmctl
create certificaterequest can generate a dedicated TLS key locally, send only a
public CSR to that existing issuer and return the signed certificate locally,
without reading or replacing sso/kc-tls. This creates a new public request and
normal temporary ACME resources and requires separate actual owner/founder
live authority. A Certificate YAML used as cmctl input is a local template, not
an apply of a Certificate or Secret. No issuance occurred in this source work.
The reviewed candidate is cmctl v2.6.1 Linux amd64, published checksum
sha256:0af054c26a2b64ce286fa868d029164867695a93c3eb893662ce16f35e708db0,
source commit 7e56321dbec4f62ea5428b8656c871c6dc0cc112. It is not installed or
admitted by this document; signature-bundle verification remains unperformed.
Its key file is written before API submission, so protected tmpfs staging and
independent cleanup must precede invocation. T03 must admit the existing session
staging/reaper adaptation for that earlier networked child, with success,
refusal, timeout and parent-loss proof; a shell trap alone is insufficient.
This is a supported candidate for a standalone drill while the cluster, DNS,
external HTTP-01 port 80, Traefik, cert-manager and ACME provider are available.
It proves no independence during cluster loss. Do not switch to DNS-01 or a new
provider, read an account key, delete the production Secret or call a surviving-
cluster reissue an independent cluster-loss recovery. T02/T03/T04 retain that
actual independent custody/provider/domain-control and native acceptance scope.
Timestamped current public Certificate/issuer/controller/solver metadata, public
CA/hostname observation, source hashes, upstream contract and exact unexecuted
command/lifecycle proposal are retained in Custodian
`docs/changes/netkingdom-target-2026-10-17/tls-recovery-disposition-r1/`.
This source inventory is neither historical original-key custody nor owner
acceptance of reissue; T35 requires the explicit reviewed disposition before
T33 can bind the selected TLS material to an actual owner-held attempt.
## Earlier private staging and exact public request cleanup
T03 source preparation must register the original guard-compatible private0700
/dev/shm directory and independently surviving pidfd/descendant reaper before
cmctl can write a key. Verify actual writable tmpfs/mount/device/inode before
use; the existing later session reaper is Docker-bound and is not already an
issuance guard. Preserve exact child quiescence and an immutable public checkpoint
before inode-guarded deletion. Reap dead adopted zombies; still refuse live or
unmapped descendants. A shell trap or immediate-child timeout is insufficient.
Keep cmctl, TLS staging and the original six-argument local recovery launcher on
one admitted custody host. A workstation context name is not cluster identity;
bind the existing owner API profile to verified node92.205.62.239. Moving only
cmctl to another host would introduce a separate private-key transfer and is not
part of this source disposition. The existing T33/T35 native review binds the
actual host/tool/target; this paragraph creates no new TLS mode or policy gate.
The source copies local template run labels/annotations to its CertificateRequest.
Record exact public request namespace/name, run/spec/CSR/public-key commitments,
admitted caller and actual UID. Cleanup uses the existing Kubernetes raw DELETE
idiom with DeleteOptions.preconditions.uid and Foreground propagation, then checks
that UID and its actual owner-UID descendants. Never delete by prefix/label/name
alone or touch production kc-tls. A killed client or one NotFound cannot prove
no late accepted Create; preserve the exact unresolved obligation and reconcile
through the owner API. Local plaintext cleanup and remote resource cleanup have
separate verified/uncertain outcomes, with no blind retry or false global success.
The concrete T03 source delta, UID-delete contract and required generated lifecycle
counterexamples are in Custodian
`docs/changes/netkingdom-target-2026-10-17/tls-staging-cleanup-r1/`.
It is a source proposal, not implemented/native cleanup proof. Existing T02–T04
and NK49T35/T33 gates/statuses remain unchanged; no new task or UUID is needed.
The original disaster guide is already located in publication commit
31f483966cdfca4df1d326dee3ec0b6d33ea0e5e at Backup account path
`railiance-backups/net-kingdom-identity/net-kingdom-recovery-57d1940947aa25ff-START-HERE.md`.
Historical readback does not establish current remote visibility or ingress TLS
coverage. No renewed request for an unspecified original TLS backup is needed
to prepare this separate trusted-leaf path; genuine cluster-loss independence
and actual provider/domain-control/custody proof remain existing owner work.

View file

@ -0,0 +1,122 @@
--- a/docs/keycape-tls-recovery-boundary.md
+++ b/docs/keycape-tls-recovery-boundary.md
@@ -29,10 +29,10 @@
ciphertext or historical outcome is rewritten by that work.
`tools/public-tls-metadata.py` is an offline public-only step. After the source
-owner identifies independent original **public certificate/chain and trust files**:
+owner supplies the **selected public certificate/chain and independent trust files**:
```sh
-python3 tools/public-tls-metadata.py --public-cert ORIGINAL_PUBLIC_CERT_CHAIN.pem --public-trust ORIGINAL_PUBLIC_TRUST.pem
+python3 tools/public-tls-metadata.py --public-cert SELECTED_PUBLIC_CERT_CHAIN.pem --public-trust SELECTED_PUBLIC_TRUST.pem
```
It accepts no private-key argument, bounds regular-file reads, refuses symlinks and
@@ -40,8 +40,10 @@
The file names are owner-local placeholders, not a key request. Output establishes
public metadata, not originality, hostname/chain acceptance, reissue permission or
custody. Do not supply a Secret object or combined certificate/key PEM. Fail closed
-when original source, trust or exact bindings are missing; no fresh self-signed
-trust workaround is admitted.
+when selected source, independent trust or exact attempt bindings are missing;
+no fresh self-signed trust workaround is admitted. This utility does not require
+historical ingress leaf/key bytes when a trusted renewed leaf is selected under
+the existing reviewed attempt.
For FUTURE NetKingdom archives, the source owners must explicitly disposition
original TLS coverage or a reviewed reissue contract: producer/custody owner,
@@ -49,3 +51,93 @@
scoped capture/read permissions, retention and native verification. Merely adding
`kc-tls` to secret_refs would change capture/permission/custody scope and is not
authorized by this source review. It cannot repair the current archive retroactively.
+
+## Renewable TLS leaf and original OIDC signing identity
+
+The archived KeyCape RS256 signing key, issuer URL, service registrations and
+policy state are distinct from the public HTTPS leaf and its TLS private key.
+The current identity archive has no ingress TLS leaf to compare. A separately
+reviewed trusted reissue can preserve kc.coulomb.social and the archived OIDC
+signing identity without reproducing historical TLS leaf/key bytes. This is an
+explicit owner continuity disposition under NK49-T35, not implicit permission
+to replace custody, trust or an outage claim.
+
+The existing isolated admission pins the exact certificate-chain and trust PEM
+bytes selected for that attempt; it does not compare them with an archived leaf
+fingerprint. Those hashes, certificate/key pairing, hostname/CA/validity checks,
+archived issuer equality and original signer/JWKS verification remain required.
+The receipt's original HTTPS issuer/trust wording does not assert original leaf
+byte equality.
+Current source and public resource inventory establish a concrete standard path:
+ClusterIssuer/letsencrypt-prod uses ACME HTTP-01 with Traefik. Upstream cmctl
+create certificaterequest can generate a dedicated TLS key locally, send only a
+public CSR to that existing issuer and return the signed certificate locally,
+without reading or replacing sso/kc-tls. This creates a new public request and
+normal temporary ACME resources and requires separate actual owner/founder
+live authority. A Certificate YAML used as cmctl input is a local template, not
+an apply of a Certificate or Secret. No issuance occurred in this source work.
+
+The reviewed candidate is cmctl v2.6.1 Linux amd64, published checksum
+sha256:0af054c26a2b64ce286fa868d029164867695a93c3eb893662ce16f35e708db0,
+source commit 7e56321dbec4f62ea5428b8656c871c6dc0cc112. It is not installed or
+admitted by this document; signature-bundle verification remains unperformed.
+Its key file is written before API submission, so protected tmpfs staging and
+independent cleanup must precede invocation. T03 must admit the existing session
+staging/reaper adaptation for that earlier networked child, with success,
+refusal, timeout and parent-loss proof; a shell trap alone is insufficient.
+
+This is a supported candidate for a standalone drill while the cluster, DNS,
+external HTTP-01 port 80, Traefik, cert-manager and ACME provider are available.
+It proves no independence during cluster loss. Do not switch to DNS-01 or a new
+provider, read an account key, delete the production Secret or call a surviving-
+cluster reissue an independent cluster-loss recovery. T02/T03/T04 retain that
+actual independent custody/provider/domain-control and native acceptance scope.
+
+Timestamped current public Certificate/issuer/controller/solver metadata, public
+CA/hostname observation, source hashes, upstream contract and exact unexecuted
+command/lifecycle proposal are retained in Custodian
+`docs/changes/netkingdom-target-2026-10-17/tls-recovery-disposition-r1/`.
+This source inventory is neither historical original-key custody nor owner
+acceptance of reissue; T35 requires the explicit reviewed disposition before
+T33 can bind the selected TLS material to an actual owner-held attempt.
+
+## Earlier private staging and exact public request cleanup
+
+T03 source preparation must register the original guard-compatible private0700
+/dev/shm directory and independently surviving pidfd/descendant reaper before
+cmctl can write a key. Verify actual writable tmpfs/mount/device/inode before
+use; the existing later session reaper is Docker-bound and is not already an
+issuance guard. Preserve exact child quiescence and an immutable public checkpoint
+before inode-guarded deletion. Reap dead adopted zombies; still refuse live or
+unmapped descendants. A shell trap or immediate-child timeout is insufficient.
+
+Keep cmctl, TLS staging and the original six-argument local recovery launcher on
+one admitted custody host. A workstation context name is not cluster identity;
+bind the existing owner API profile to verified node92.205.62.239. Moving only
+cmctl to another host would introduce a separate private-key transfer and is not
+part of this source disposition. The existing T33/T35 native review binds the
+actual host/tool/target; this paragraph creates no new TLS mode or policy gate.
+
+The source copies local template run labels/annotations to its CertificateRequest.
+Record exact public request namespace/name, run/spec/CSR/public-key commitments,
+admitted caller and actual UID. Cleanup uses the existing Kubernetes raw DELETE
+idiom with DeleteOptions.preconditions.uid and Foreground propagation, then checks
+that UID and its actual owner-UID descendants. Never delete by prefix/label/name
+alone or touch production kc-tls. A killed client or one NotFound cannot prove
+no late accepted Create; preserve the exact unresolved obligation and reconcile
+through the owner API. Local plaintext cleanup and remote resource cleanup have
+separate verified/uncertain outcomes, with no blind retry or false global success.
+
+The concrete T03 source delta, UID-delete contract and required generated lifecycle
+counterexamples are in Custodian
+`docs/changes/netkingdom-target-2026-10-17/tls-staging-cleanup-r1/`.
+It is a source proposal, not implemented/native cleanup proof. Existing T02–T04
+and NK49T35/T33 gates/statuses remain unchanged; no new task or UUID is needed.
+
+The original disaster guide is already located in publication commit
+31f483966cdfca4df1d326dee3ec0b6d33ea0e5e at Backup account path
+`railiance-backups/net-kingdom-identity/net-kingdom-recovery-57d1940947aa25ff-START-HERE.md`.
+Historical readback does not establish current remote visibility or ingress TLS
+coverage. No renewed request for an unspecified original TLS backup is needed
+to prepare this separate trusted-leaf path; genuine cluster-loss independence
+and actual provider/domain-control/custody proof remain existing owner work.

View file

@ -0,0 +1,28 @@
{
"status": "exact source delta ready for owner review; no staging implementation or native proof claimed",
"canonical_source_edits": false,
"runtime_private_or_cluster_calls": 0,
"native_certificate_request_download_install_or_credential_reads": false,
"redundant_tls_mode_or_policy_gate_added": false,
"original_archive_oidc_signing_key_and_issuer_invariants_preserved": true,
"source_only_publication_recommendation": "One existing Cluster boundary doc can be reviewed/published now after actual source-owner review; captured preimage/postimage/patch only. No WP status/UUID/T06 addition needed.",
"concrete_gaps": [
"pre-key actual rw tmpfs registration and observer acknowledgment",
"Docker-specific existing reaper cannot supervise earlier networked CSR child; exact descendant adoption/parent-loss cleanup needed",
"cmctl output-file writes require protected absent-name boundary",
"CertificateRequest lost-create response / exact run-SPKI-spec-caller UID ownership / Foreground delete and actual descendant cleanup",
"one admitted custody host and bound API target; no invented private-key transfer",
"preserve original attended TTY / hidden prompt / sealed-fd launcher contract, not DEVNULL wrapper"
],
"actual_native_cause_limit": "source and prior local process fixture evidence only; no native TLS issuance/recovery cause diagnosed",
"cluster_loss_limit": "existing surviving-cluster HTTP01 route unavailable during cluster loss; independent custody/provider/domain-control remains existing T02\u2013T04 work",
"guide_source_located": {
"commit": "31f483966cdfca4df1d326dee3ec0b6d33ea0e5e",
"path": "Backup/railiance-backups/net-kingdom-identity/net-kingdom-recovery-57d1940947aa25ff-START-HERE.md",
"remote_reread": false,
"another_original_tls_backup_request": false
},
"tests": "No new permanent or dummy tests; exact generated lifecycle/TTY/UID-negative proof is an explicit T03 implementation deliverable, not claimed here.",
"next_owner_source_scope": "existing T03 bounded issuance custody adapter using existing NetKingdom inode cleanup and owner Kubernetes UID-delete idiom; original T33 launcher interface unchanged",
"authority": "fresh actual owner/founder approval remains required for any native issuance/action; this packet grants none"
}

View file

@ -0,0 +1,62 @@
{
"observed_at": "2026-10-17T10:47:12.320878+00:00",
"references": [
{
"path": "/home/worsch/net-kingdom/AGENTS.md",
"sha256": "sha256:95158b1d5c15e6cf8be980f5c0e3b48aeef252654846273887f40516b3fa6a78"
},
{
"path": "/home/worsch/net-kingdom/tools/bootstrap-recovery/isolated-keycape-session.py",
"sha256": "sha256:8fa38efe28167b59a6307f342b2ac8fcda78ecda60e6228f22017424d2343c22"
},
{
"path": "/home/worsch/net-kingdom/tools/bootstrap-recovery/isolated-keycape.py",
"sha256": "sha256:86f37add7221e4c73e3f9d878c09a3cc81061627cbf024ed6887d973d929b718"
},
{
"path": "/home/worsch/net-kingdom/tools/bootstrap-recovery/prepare-isolated-keycape.py",
"sha256": "sha256:5098bdfa4e0ad30627c1716e5e355a2c78dca6ccc607bd2148a2e06126632288"
},
{
"path": "/home/worsch/net-kingdom/tools/bootstrap-recovery/run-isolated-keycape.sh",
"sha256": "sha256:0d994c9c14ebdffdbf7879bd7562042531083d0e8955424d0ae099898a141668"
},
{
"path": "/home/worsch/net-kingdom/tools/bootstrap-recovery/capture-identity.py",
"sha256": "sha256:47dca7c4dc96fa7eb9fb02c81fca9ca8dc5281aac918979eb7367148168ba705"
},
{
"path": "/home/worsch/railiance-platform/scripts/native_factory_acceptance.py",
"sha256": "sha256:291646308a2a749f858638f9f4fa0db33cf545e8b4a0c09c30e884a7be8e4bc0"
},
{
"path": "/home/worsch/railiance-cluster/AGENTS.md",
"sha256": "sha256:5cc7808a6d0e5cdebc7b3f13a0d7dce9cee5441ed6de4703b67cf3fe1800d45b"
},
{
"path": "/home/worsch/railiance-cluster/docs/keycape-tls-recovery-boundary.md",
"sha256": "sha256:f4bcb1d1832c566f9c87793ed2a461c8cd8a00859dc4ae66cd3c160d7ceefa70"
},
{
"path": "/home/worsch/railiance-cluster/workplans/RCLUSTER-WP-0018-keycape-original-tls-recovery.md",
"sha256": "sha256:96d52b6dbae056399159632d50b32ccc6662e4f6d3677af154f7c8a3d618334b"
},
{
"path": "/home/worsch/the-custodian/docs/agent-environment-orientation.md",
"sha256": "sha256:6eb712593adaeb2efd8c69edb22b02662e6c9b155f84f1559e7bdaeb76730439"
},
{
"path": "/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/storage-share-tls-r1/source-history-followthrough-r2.json",
"sha256": "sha256:51a6cf3547f0b9aac1620c472c933bd3eb7213200d78397bf8d851ea4f3f861a"
},
{
"path": "/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/native-runtime-install-r3/local-fixture-results.json",
"sha256": "sha256:339b2fcfd8d407e09874592b681830c605a463a88841351eed494a182d677219"
}
],
"upstream_cmctl_source_url": "https://raw.githubusercontent.com/cert-manager/cmctl/7e56321dbec4f62ea5428b8656c871c6dc0cc112/pkg/create/certificaterequest/certificaterequest.go",
"public_source_sha256": "sha256:0ba0c2d3d4f8530514c8d864b2a9e056f14f763a0b314aa05998f3169ee5bc32",
"matches_frozen_upstream_pin": true,
"tls_r1_all_frozen_entries_unchanged": true,
"owner_boundary_canonical_equals_preimage": true
}

View file

@ -0,0 +1,71 @@
{
"schema": "custodian.tls-staging-reaper-source-boundary.v1",
"status": "owner source proposal; no implementation/native lifecycle proof claimed",
"existing_tasks": [
"RCLUSTER-WP-0018-T03",
"NK-WP-0049-T35",
"NK-WP-0049-T33"
],
"execution_locus": "one admitted host for cmctl/tmpfs/original launcher; exact host/account/tool/API profile unresolved, no private transfer assumed",
"staging": {
"root": "/dev/shm",
"name_pattern": "nk49-keycape-{run_uuid}-{eight_random_chars}",
"mode": "0700",
"key_mode": "0600",
"mount_checks_before_key": [
"actual writable tmpfs",
"mount ID/device and owner UID bound",
"no parent alias/replacement",
"bounded free space"
],
"file_rules": "absent output names; regular owner-only single-link; 1MiB per-file child RLIMIT_FSIZE; disable core dumps before private output",
"read_limit_bytes": 1048576,
"receipt": "bounded immutable public-only metadata checkpoint before private cleanup; no private material"
},
"states": [
"public_source_and_owner_target_bound",
"tmpfs_inode_registered",
"observer_acknowledged",
"submit_intent_durable",
"cmctl_started",
"request_uid_observed_or_submit_uncertain",
"certificate_verified_or_failed",
"original_launcher_consumed_or_not_started",
"exact_local_children_quiescent",
"public_commitments_and_outstanding_duties_durable",
"registered_private_inode_removed_or_cleanup_unverified",
"request_and_owned_children_absent_or_remote_cleanup_uncertain"
],
"absolute_bounds_seconds": {
"issuance_total_from_private_staging_start": 330,
"cmctl_fetch_flag": 300,
"existing_recovery_child_maximum": 1800,
"single_private_execution_deadline": 2130,
"nonrenewable_cleanup_maximum": 60,
"total_including_cleanup": 2190,
"term_grace": 0.5,
"kill_reap": 3,
"watchdog_wait_maximum": 8,
"api_request_maximum_clipped_to_cleanup_remaining": 30
},
"deadline_rules": [
"one original monotonic private execution budget, no renewal across phases",
"no new child after deadline",
"subreaper stops and waits exact trees before local delete",
"cleanup operations clipped to same original 60s cleanup remainder; outstanding remote duties survive timeout",
"existing original launcher reaper and Docker obligations remain separately receipt-bound"
],
"readiness": "no observer ack means zero cmctl/key generation",
"creation_uncertainty": "kill/client timeout does not cancel accepted API mutation; preserve pending creation obligation if UID not durably reconciled",
"authority": "existing T33/T35/RCLUSTER18 source/native owner process; no new TLS mode or approval field",
"observed_local_sandbox_tmpfs": "/dev/shm actual mount is ro,nosuid,nodev,noatime; no usable local private staging or tmpfs proof asserted",
"limitations": [
"local tmpfs does not establish unswappable memory, root/hypervisor isolation or host-loss receipts",
"surviving-cluster HTTP01 path only; independent cluster-loss recovery remains open"
],
"child_io": {
"cmctl": "stdin/stdout/stderr DEVNULL; no custody fd",
"original_launcher": "existing attended TTY path preserved; original hidden prompt/sealed fd; no PTY proxy/logging",
"observer": "pidfds and public metadata only; close unrelated/secret fds"
}
}

View file

@ -0,0 +1 @@
WARN: git commit failed for writeback: Generated-only commits rejected: staged

View file

@ -0,0 +1,34 @@
"""Read-only fresh comparison against the retained verified-client baseline."""
from pathlib import Path
import hashlib
import json
import shlex
import subprocess
BASE = Path(__file__).resolve().parent
RECEIVER = BASE / 'receiver-rollout-r4'
pod = json.loads((RECEIVER / 'delivered-runtime.json').read_text())['api_pod']
def owner(args, content=None):
r = subprocess.run(['ssh', 'railiance01',
'KUBECONFIG=/etc/rancher/k3s/k3s.yaml ' + shlex.join(args)],
input=content, capture_output=True, text=True, timeout=45)
assert r.returncode == 0, 'private-owner-read-refused'
return json.loads(r.stdout)
actual = owner(['kubectl', '-n', 'state-hub', 'get', 'pod', pod['name'], '-o', 'json'])
assert actual['metadata']['uid'] == pod['uid']
helper = BASE.parent / 'netkingdom-target-2026-10-16/cluster-historical-publication-r1/postdeployment-verifier-r1/snapshot_full_row_hashes.py'
assert hashlib.sha256(helper.read_bytes()).hexdigest() == 'f01e93c0fcb4ca155bdf76813626c33e4343f6a8caec2870ab1d785243c60479'
result = owner(['kubectl', '-n', 'state-hub', 'exec', '-i', pod['name'], '-c', 'state-hub', '--', 'python', '-'], helper.read_text())
assert result['status'] == 'captured_readonly'
baseline = json.loads((RECEIVER / 'after-verified-client.json').read_text())
original = json.loads((RECEIVER / 'before-full-row-hashes.json').read_text())
result.update(current_verified_baseline_equal=result['tables'] == baseline['tables'],
original_rows_equal=result['tables'] == original['tables'],
original_failed_comparison_retained=True, original_preimages_restored=False)
(BASE / 'rollout-close-current-full-row-hashes.json').write_text(json.dumps(result, indent=2) + '\n')
assert result['current_verified_baseline_equal']
print(json.dumps({'status': 'verified-current-baseline', 'historical_rows': 18,
'original_failed_comparison_retained': True,
'original_preimages_restored': False}))

View file

@ -0,0 +1,188 @@
# Work Records — ops-warden
> 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 | WARDEN-WP-ADHOC-2026-06-27 | finished | — | workplans/ADHOC-2026-06-27.md |
| workplan | WARDEN-WP-ADHOC-2026-06-29 | finished | — | workplans/ADHOC-2026-06-29.md |
| workplan | WARDEN-WP-ADHOC-2026-08-11 | finished | — | workplans/ADHOC-2026-08-11.md |
| workplan | WARDEN-WP-ADHOC-2026-08-17 | finished | — | workplans/ADHOC-2026-08-17.md |
| workplan | WARDEN-WP-ADHOC-2026-09-08 | finished | — | workplans/ADHOC-2026-09-08.md |
| workplan | WARDEN-WP-ADHOC-2026-10-03 | finished | — | workplans/ADHOC-2026-10-03.md |
| workplan | WARDEN-WP-0016 | finished | — | workplans/WARDEN-WP-0016-ops-bridge-tunnel-cert-pilot.md |
| workplan | WARDEN-WP-0017 | finished | — | workplans/WARDEN-WP-0017-access-front-door-discoverability.md |
| workplan | WARDEN-WP-0018 | finished | — | workplans/WARDEN-WP-0018-whynot-design-npm-lane-activation.md |
| workplan | WARDEN-WP-0019 | finished | — | workplans/WARDEN-WP-0019-route-to-secrets-engine.md |
| workplan | WARDEN-WP-0020 | finished | — | workplans/WARDEN-WP-0020-ops-warden-worker.md |
| workplan | WARDEN-WP-0021 | finished | — | workplans/WARDEN-WP-0021-enable-scheduled-worker-tick.md |
| workplan | WARDEN-WP-0022 | finished | — | workplans/WARDEN-WP-0022-audit-trail-and-activity.md |
| workplan | WARDEN-WP-0023 | finished | — | workplans/WARDEN-WP-0023-intent-scope-alignment-closeout.md |
| workplan | WARDEN-WP-0024 | finished | — | workplans/WARDEN-WP-0024-experiential-memory-and-agent-sessions.md |
| workplan | WARDEN-WP-0025 | finished | — | workplans/WARDEN-WP-0025-forgejo-admin-api-token-lane.md |
| workplan | WARDEN-WP-0026 | finished | — | workplans/WARDEN-WP-0026-credential-disclosure-hygiene.md |
| workplan | WARDEN-WP-0027 | blocked | — | workplans/WARDEN-WP-0027-credential-governance-lockdown.md |
| workplan | WARDEN-WP-0028 | finished | — | workplans/WARDEN-WP-0028-tenant-secret-custody.md |
| workplan | WARDEN-WP-0029 | finished | — | workplans/WARDEN-WP-0029-policy-front-door-and-founder-surface.md |
| workplan | WARDEN-WP-0030 | finished | — | workplans/WARDEN-WP-0030-delegation-register.md |
| workplan | WARDEN-WP-0031 | finished | — | workplans/WARDEN-WP-0031-policy-caller-identity.md |
| workplan | WARDEN-WP-0032 | finished | — | workplans/WARDEN-WP-0032-security-zones.md |
| workplan | WARDEN-WP-0033 | finished | — | workplans/WARDEN-WP-0033-native-lane-handoff.md |
| workplan | WARDEN-WP-0034 | active | — | workplans/WARDEN-WP-0034-layer-model-v07-conformance.md |
| workplan | WARDEN-WP-0035 | finished | — | workplans/WARDEN-WP-0035-policy-nexus-forgejo-source-read-route.md |
| workplan | WARDEN-WP-0036 | finished | — | workplans/WARDEN-WP-0036-attended-login-openbao-output.md |
| workplan | WARDEN-WP-0037 | blocked | — | workplans/WARDEN-WP-0037-whynot-design-forgejo-npm-lane.md |
| workplan | WARDEN-WP-0038 | finished | — | workplans/WARDEN-WP-0038-plan-mutation-intent.md |
| workplan | WARDEN-WP-0039 | finished | — | workplans/WARDEN-WP-0039-explicit-policy-refusal.md |
| workplan | WARDEN-WP-0040 | active | — | workplans/WARDEN-WP-0040-unknown-zone-fail-closed-adoption.md |
| workplan | WARDEN-WP-0041 | finished | — | workplans/WARDEN-WP-0041-catalog-lane-scaffolding.md |
| workplan | WARDEN-WP-0042 | finished | — | workplans/WARDEN-WP-0042-openbao-structure-survey-route.md |
| workplan | WARDEN-WP-0043 | finished | — | workplans/WARDEN-WP-0043-platform-supplier-credentials.md |
| workplan | WARDEN-WP-0044 | active | — | workplans/WARDEN-WP-0044-security-case-follow-through.md |
| task | WARDEN-WP-ADHOC-2026-06-27-T01 | done | — | workplans/ADHOC-2026-06-27.md |
| task | WARDEN-WP-ADHOC-2026-06-29-T01 | done | — | workplans/ADHOC-2026-06-29.md |
| task | WARDEN-WP-ADHOC-2026-08-11-T01 | done | — | workplans/ADHOC-2026-08-11.md |
| task | WARDEN-WP-ADHOC-2026-08-11-T02 | done | — | workplans/ADHOC-2026-08-11.md |
| task | WARDEN-WP-ADHOC-2026-08-11-T03 | done | — | workplans/ADHOC-2026-08-11.md |
| task | WARDEN-WP-ADHOC-2026-08-17-T01 | done | — | workplans/ADHOC-2026-08-17.md |
| task | WARDEN-WP-ADHOC-2026-08-17-T02 | done | — | workplans/ADHOC-2026-08-17.md |
| task | WARDEN-WP-ADHOC-2026-08-17-T03 | done | — | workplans/ADHOC-2026-08-17.md |
| task | WARDEN-WP-ADHOC-2026-08-17-T04 | done | — | workplans/ADHOC-2026-08-17.md |
| task | WARDEN-WP-ADHOC-2026-09-08-T01 | done | — | workplans/ADHOC-2026-09-08.md |
| task | WARDEN-WP-ADHOC-2026-09-08-T02 | done | — | workplans/ADHOC-2026-09-08.md |
| task | WARDEN-WP-ADHOC-2026-09-08-T03 | done | — | workplans/ADHOC-2026-09-08.md |
| task | WARDEN-WP-ADHOC-2026-09-08-T04 | done | — | workplans/ADHOC-2026-09-08.md |
| task | WARDEN-WP-ADHOC-2026-09-08-T05 | done | — | workplans/ADHOC-2026-09-08.md |
| task | WARDEN-WP-ADHOC-2026-09-08-T06 | done | — | workplans/ADHOC-2026-09-08.md |
| task | WARDEN-WP-ADHOC-2026-10-03-T01 | done | — | workplans/ADHOC-2026-10-03.md |
| task | WARDEN-WP-0016-T01 | done | — | workplans/WARDEN-WP-0016-ops-bridge-tunnel-cert-pilot.md |
| task | WARDEN-WP-0016-T02 | done | — | workplans/WARDEN-WP-0016-ops-bridge-tunnel-cert-pilot.md |
| task | WARDEN-WP-0016-T03 | done | — | workplans/WARDEN-WP-0016-ops-bridge-tunnel-cert-pilot.md |
| task | WARDEN-WP-0016-T04 | done | — | workplans/WARDEN-WP-0016-ops-bridge-tunnel-cert-pilot.md |
| task | WARDEN-WP-0017-T01 | done | — | workplans/WARDEN-WP-0017-access-front-door-discoverability.md |
| task | WARDEN-WP-0017-T02 | done | — | workplans/WARDEN-WP-0017-access-front-door-discoverability.md |
| task | WARDEN-WP-0017-T03 | done | — | workplans/WARDEN-WP-0017-access-front-door-discoverability.md |
| task | WARDEN-WP-0018-T01 | done | — | workplans/WARDEN-WP-0018-whynot-design-npm-lane-activation.md |
| task | WARDEN-WP-0018-T02 | done | — | workplans/WARDEN-WP-0018-whynot-design-npm-lane-activation.md |
| task | WARDEN-WP-0018-T03 | done | — | workplans/WARDEN-WP-0018-whynot-design-npm-lane-activation.md |
| task | WARDEN-WP-0019-T01 | done | — | workplans/WARDEN-WP-0019-route-to-secrets-engine.md |
| task | WARDEN-WP-0019-T02 | done | — | workplans/WARDEN-WP-0019-route-to-secrets-engine.md |
| task | WARDEN-WP-0020-T01 | done | — | workplans/WARDEN-WP-0020-ops-warden-worker.md |
| task | WARDEN-WP-0020-T02 | done | — | workplans/WARDEN-WP-0020-ops-warden-worker.md |
| task | WARDEN-WP-0020-T03 | done | — | workplans/WARDEN-WP-0020-ops-warden-worker.md |
| task | WARDEN-WP-0020-T04 | done | — | workplans/WARDEN-WP-0020-ops-warden-worker.md |
| task | WARDEN-WP-0020-T05 | done | — | workplans/WARDEN-WP-0020-ops-warden-worker.md |
| task | WARDEN-WP-0021-T01 | done | — | workplans/WARDEN-WP-0021-enable-scheduled-worker-tick.md |
| task | WARDEN-WP-0021-T02 | done | — | workplans/WARDEN-WP-0021-enable-scheduled-worker-tick.md |
| task | WARDEN-WP-0021-T03 | done | — | workplans/WARDEN-WP-0021-enable-scheduled-worker-tick.md |
| task | WARDEN-WP-0021-T04 | done | — | workplans/WARDEN-WP-0021-enable-scheduled-worker-tick.md |
| task | WARDEN-WP-0021-T05 | done | — | workplans/WARDEN-WP-0021-enable-scheduled-worker-tick.md |
| task | WARDEN-WP-0022-T01 | done | — | workplans/WARDEN-WP-0022-audit-trail-and-activity.md |
| task | WARDEN-WP-0022-T02 | done | — | workplans/WARDEN-WP-0022-audit-trail-and-activity.md |
| task | WARDEN-WP-0022-T03 | done | — | workplans/WARDEN-WP-0022-audit-trail-and-activity.md |
| task | WARDEN-WP-0022-T04 | done | — | workplans/WARDEN-WP-0022-audit-trail-and-activity.md |
| task | WARDEN-WP-0023-T01 | done | — | workplans/WARDEN-WP-0023-intent-scope-alignment-closeout.md |
| task | WARDEN-WP-0023-T02 | done | — | workplans/WARDEN-WP-0023-intent-scope-alignment-closeout.md |
| task | WARDEN-WP-0023-T03 | done | — | workplans/WARDEN-WP-0023-intent-scope-alignment-closeout.md |
| task | WARDEN-WP-0023-T04 | done | — | workplans/WARDEN-WP-0023-intent-scope-alignment-closeout.md |
| task | WARDEN-WP-0023-T05 | done | — | workplans/WARDEN-WP-0023-intent-scope-alignment-closeout.md |
| task | WARDEN-WP-0023-T06 | done | — | workplans/WARDEN-WP-0023-intent-scope-alignment-closeout.md |
| task | WARDEN-WP-0023-T07 | done | — | workplans/WARDEN-WP-0023-intent-scope-alignment-closeout.md |
| task | WARDEN-WP-0024-T01 | done | — | workplans/WARDEN-WP-0024-experiential-memory-and-agent-sessions.md |
| task | WARDEN-WP-0024-T02 | done | — | workplans/WARDEN-WP-0024-experiential-memory-and-agent-sessions.md |
| task | WARDEN-WP-0024-T03 | done | — | workplans/WARDEN-WP-0024-experiential-memory-and-agent-sessions.md |
| task | WARDEN-WP-0024-T04 | done | — | workplans/WARDEN-WP-0024-experiential-memory-and-agent-sessions.md |
| task | WARDEN-WP-0024-T05 | done | — | workplans/WARDEN-WP-0024-experiential-memory-and-agent-sessions.md |
| task | WARDEN-WP-0024-T06 | done | — | workplans/WARDEN-WP-0024-experiential-memory-and-agent-sessions.md |
| task | WARDEN-WP-0024-T07 | done | — | workplans/WARDEN-WP-0024-experiential-memory-and-agent-sessions.md |
| task | WARDEN-WP-0025-T01 | done | — | workplans/WARDEN-WP-0025-forgejo-admin-api-token-lane.md |
| task | WARDEN-WP-0025-T02 | done | — | workplans/WARDEN-WP-0025-forgejo-admin-api-token-lane.md |
| task | WARDEN-WP-0025-T03 | done | — | workplans/WARDEN-WP-0025-forgejo-admin-api-token-lane.md |
| task | WARDEN-WP-0025-T04 | done | — | workplans/WARDEN-WP-0025-forgejo-admin-api-token-lane.md |
| task | WARDEN-WP-0025-T05 | done | — | workplans/WARDEN-WP-0025-forgejo-admin-api-token-lane.md |
| task | WARDEN-WP-0026-T01 | done | — | workplans/WARDEN-WP-0026-credential-disclosure-hygiene.md |
| task | WARDEN-WP-0026-T02 | done | — | workplans/WARDEN-WP-0026-credential-disclosure-hygiene.md |
| task | WARDEN-WP-0026-T03 | done | — | workplans/WARDEN-WP-0026-credential-disclosure-hygiene.md |
| task | WARDEN-WP-0026-T04 | done | — | workplans/WARDEN-WP-0026-credential-disclosure-hygiene.md |
| task | WARDEN-WP-0026-T05 | done | — | workplans/WARDEN-WP-0026-credential-disclosure-hygiene.md |
| task | WARDEN-WP-0026-T06 | done | — | workplans/WARDEN-WP-0026-credential-disclosure-hygiene.md |
| task | WARDEN-WP-0026-T07 | done | — | workplans/WARDEN-WP-0026-credential-disclosure-hygiene.md |
| task | WARDEN-WP-0027-T01 | cancel | — | workplans/WARDEN-WP-0027-credential-governance-lockdown.md |
| task | WARDEN-WP-0027-T02 | todo | — | workplans/WARDEN-WP-0027-credential-governance-lockdown.md |
| task | WARDEN-WP-0027-T03 | cancel | — | workplans/WARDEN-WP-0027-credential-governance-lockdown.md |
| task | WARDEN-WP-0028-T01 | done | — | workplans/WARDEN-WP-0028-tenant-secret-custody.md |
| task | WARDEN-WP-0028-T02 | done | — | workplans/WARDEN-WP-0028-tenant-secret-custody.md |
| task | WARDEN-WP-0028-T03 | done | — | workplans/WARDEN-WP-0028-tenant-secret-custody.md |
| task | WARDEN-WP-0028-T04 | done | — | workplans/WARDEN-WP-0028-tenant-secret-custody.md |
| task | WARDEN-WP-0028-T05 | done | — | workplans/WARDEN-WP-0028-tenant-secret-custody.md |
| task | WARDEN-WP-0028-T06 | done | — | workplans/WARDEN-WP-0028-tenant-secret-custody.md |
| task | WARDEN-WP-0028-T07 | done | — | workplans/WARDEN-WP-0028-tenant-secret-custody.md |
| task | WARDEN-WP-0029-T01 | done | — | workplans/WARDEN-WP-0029-policy-front-door-and-founder-surface.md |
| task | WARDEN-WP-0029-T02 | done | — | workplans/WARDEN-WP-0029-policy-front-door-and-founder-surface.md |
| task | WARDEN-WP-0029-T03 | done | — | workplans/WARDEN-WP-0029-policy-front-door-and-founder-surface.md |
| task | WARDEN-WP-0029-T04 | done | — | workplans/WARDEN-WP-0029-policy-front-door-and-founder-surface.md |
| task | WARDEN-WP-0029-T05 | done | — | workplans/WARDEN-WP-0029-policy-front-door-and-founder-surface.md |
| task | WARDEN-WP-0030-T01 | done | — | workplans/WARDEN-WP-0030-delegation-register.md |
| task | WARDEN-WP-0030-T02 | done | — | workplans/WARDEN-WP-0030-delegation-register.md |
| task | WARDEN-WP-0030-T03 | done | — | workplans/WARDEN-WP-0030-delegation-register.md |
| task | WARDEN-WP-0030-T04 | done | — | workplans/WARDEN-WP-0030-delegation-register.md |
| task | WARDEN-WP-0030-T05 | done | — | workplans/WARDEN-WP-0030-delegation-register.md |
| task | WARDEN-WP-0031-T01 | done | — | workplans/WARDEN-WP-0031-policy-caller-identity.md |
| task | WARDEN-WP-0031-T02 | done | — | workplans/WARDEN-WP-0031-policy-caller-identity.md |
| task | WARDEN-WP-0031-T03 | done | — | workplans/WARDEN-WP-0031-policy-caller-identity.md |
| task | WARDEN-WP-0031-T04 | done | — | workplans/WARDEN-WP-0031-policy-caller-identity.md |
| task | WARDEN-WP-0031-T05 | cancel | — | workplans/WARDEN-WP-0031-policy-caller-identity.md |
| task | WARDEN-WP-0032-T01 | done | — | workplans/WARDEN-WP-0032-security-zones.md |
| task | WARDEN-WP-0032-T02 | done | — | workplans/WARDEN-WP-0032-security-zones.md |
| task | WARDEN-WP-0032-T03 | done | — | workplans/WARDEN-WP-0032-security-zones.md |
| task | WARDEN-WP-0032-T04 | done | — | workplans/WARDEN-WP-0032-security-zones.md |
| task | WARDEN-WP-0032-T05 | done | — | workplans/WARDEN-WP-0032-security-zones.md |
| task | WARDEN-WP-0032-T06 | done | — | workplans/WARDEN-WP-0032-security-zones.md |
| task | WARDEN-WP-0032-T07 | done | — | workplans/WARDEN-WP-0032-security-zones.md |
| task | WARDEN-WP-0033-T01 | done | — | workplans/WARDEN-WP-0033-native-lane-handoff.md |
| task | WARDEN-WP-0033-T02 | done | — | workplans/WARDEN-WP-0033-native-lane-handoff.md |
| task | WARDEN-WP-0033-T03 | done | — | workplans/WARDEN-WP-0033-native-lane-handoff.md |
| task | WARDEN-WP-0033-T04 | done | — | workplans/WARDEN-WP-0033-native-lane-handoff.md |
| task | WARDEN-WP-0033-T05 | done | — | workplans/WARDEN-WP-0033-native-lane-handoff.md |
| task | WARDEN-WP-0033-T06 | done | — | workplans/WARDEN-WP-0033-native-lane-handoff.md |
| task | WARDEN-WP-0034-T01 | done | — | workplans/WARDEN-WP-0034-layer-model-v07-conformance.md |
| task | WARDEN-WP-0034-T02 | done | — | workplans/WARDEN-WP-0034-layer-model-v07-conformance.md |
| task | WARDEN-WP-0034-T03 | done | — | workplans/WARDEN-WP-0034-layer-model-v07-conformance.md |
| task | WARDEN-WP-0034-T04 | done | — | workplans/WARDEN-WP-0034-layer-model-v07-conformance.md |
| task | WARDEN-WP-0034-T05 | wait | — | workplans/WARDEN-WP-0034-layer-model-v07-conformance.md |
| task | WARDEN-WP-0034-T06 | done | — | workplans/WARDEN-WP-0034-layer-model-v07-conformance.md |
| task | WARDEN-WP-0035-T01 | done | — | workplans/WARDEN-WP-0035-policy-nexus-forgejo-source-read-route.md |
| task | WARDEN-WP-0035-T02 | done | — | workplans/WARDEN-WP-0035-policy-nexus-forgejo-source-read-route.md |
| task | WARDEN-WP-0036-T01 | done | — | workplans/WARDEN-WP-0036-attended-login-openbao-output.md |
| task | WARDEN-WP-0036-T02 | done | — | workplans/WARDEN-WP-0036-attended-login-openbao-output.md |
| task | WARDEN-WP-0037-T01 | done | — | workplans/WARDEN-WP-0037-whynot-design-forgejo-npm-lane.md |
| task | WARDEN-WP-0037-T02 | done | — | workplans/WARDEN-WP-0037-whynot-design-forgejo-npm-lane.md |
| task | WARDEN-WP-0037-T03 | wait | — | workplans/WARDEN-WP-0037-whynot-design-forgejo-npm-lane.md |
| task | WARDEN-WP-0038-T01 | done | — | workplans/WARDEN-WP-0038-plan-mutation-intent.md |
| task | WARDEN-WP-0039-T01 | done | — | workplans/WARDEN-WP-0039-explicit-policy-refusal.md |
| task | WARDEN-WP-0039-T02 | done | — | workplans/WARDEN-WP-0039-explicit-policy-refusal.md |
| task | WARDEN-WP-0039-T03 | done | — | workplans/WARDEN-WP-0039-explicit-policy-refusal.md |
| task | WARDEN-WP-0040-T01 | wait | — | workplans/WARDEN-WP-0040-unknown-zone-fail-closed-adoption.md |
| task | WARDEN-WP-0040-T02 | wait | — | workplans/WARDEN-WP-0040-unknown-zone-fail-closed-adoption.md |
| task | WARDEN-WP-0040-T03 | wait | — | workplans/WARDEN-WP-0040-unknown-zone-fail-closed-adoption.md |
| task | WARDEN-WP-0040-T04 | done | — | workplans/WARDEN-WP-0040-unknown-zone-fail-closed-adoption.md |
| task | WARDEN-WP-0041-T01 | done | — | workplans/WARDEN-WP-0041-catalog-lane-scaffolding.md |
| task | WARDEN-WP-0041-T02 | done | — | workplans/WARDEN-WP-0041-catalog-lane-scaffolding.md |
| task | WARDEN-WP-0041-T03 | done | — | workplans/WARDEN-WP-0041-catalog-lane-scaffolding.md |
| task | WARDEN-WP-0041-T04 | done | — | workplans/WARDEN-WP-0041-catalog-lane-scaffolding.md |
| task | WARDEN-WP-0042-T01 | done | — | workplans/WARDEN-WP-0042-openbao-structure-survey-route.md |
| task | WARDEN-WP-0042-T02 | done | — | workplans/WARDEN-WP-0042-openbao-structure-survey-route.md |
| task | WARDEN-WP-0043-T01 | done | — | workplans/WARDEN-WP-0043-platform-supplier-credentials.md |
| task | WARDEN-WP-0043-T02 | done | — | workplans/WARDEN-WP-0043-platform-supplier-credentials.md |
| task | WARDEN-WP-0043-T03 | done | — | workplans/WARDEN-WP-0043-platform-supplier-credentials.md |
| task | WARDEN-WP-0044-T01 | done | — | workplans/WARDEN-WP-0044-security-case-follow-through.md |
| task | WARDEN-WP-0044-T02 | done | — | workplans/WARDEN-WP-0044-security-case-follow-through.md |
| task | WARDEN-WP-0044-T03 | progress | — | workplans/WARDEN-WP-0044-security-case-follow-through.md |
| task | WARDEN-WP-0044-T04 | wait | — | workplans/WARDEN-WP-0044-security-case-follow-through.md |
| intake | WARDEN-IN-0001 | closed | — | intakes/intakes.md |
| intake | WARDEN-IN-0002 | closed | — | intakes/intakes.md |
| intake | WARDEN-IN-0003 | closed | — | intakes/intakes.md |

View file

@ -0,0 +1,60 @@
<!-- custodian-brief: generated by fix-consistency — do not edit manually -->
# Custodian Brief — ops-warden
**Domain:** infotech
**Last synced:** 2026-10-16 00:05 UTC
**State Hub:** http://127.0.0.1:8000 *(adjust if running on a remote machine)*
## Active Workstreams
### Adopt unknown -> fail_closed behind signing-target classification coverage
Progress: 1/4 done | workplan_id: `c8ee441e-1be1-5219-910c-e79ff23cc9ec`
**Open tasks:**
- ! Tasks `611f0901`
*(wait: Warden's own declaration cannot classify the repair-path target workloads; the owner must declare the continuity actor/target and its z2-continuity resolution (see 2026-09-28 review).)*
- ! Tasks `54ad4864`
*(wait: Owners of agt-codex-interhub-bootstrap (ops-hub) and agt-state-hub-bridge (ops-bridge) must publish authoritative workload identity declarations; atm-backup-daily declared by activity-core and compiled 2026-10-09 (z2-protected, admission unsatisfied: M1 below M2 floor).)*
- ! Tasks `93eaf1f2`
*(wait: Gated on T01 and on layer model v0.8 (or successor) reaching accepted; never convert against a proposed standard.)*
### Layer model v0.7 conformance — state the deadline, bind the agent boundary, steward the estate's newest rule
Progress: 5/6 done | workplan_id: `ae3ff76f-883d-5e2f-b6aa-144d61e8fdef`
**Open tasks:**
- ! Tasks `7d1b3c82`
*(wait: Two items to route, not absorb: ops-mason has published no stance map; the second is another repository's to own.)*
### Follow through on KnownSecurityCases along their type's route
Progress: 2/4 done | workplan_id: `f7e81013-6868-5d99-8528-ba75a442eca9`
**Open tasks:**
- ! T03 — Run and verify a case `4a0fa4a7`
*(wait: Catalog integration follows a proven run.)*
- ► T02 — Plan a case run `bcbf6c91`
### Tamper-resistant credential governance + mass rotation/lockdown (Strand B)
Progress: 2/3 done | workplan_id: `21528e8d-a049-523d-9ae1-da7a27cb8bbf`
**Open tasks:**
- · Task: Graded lockdown / break-glass with explicit trust-root `cae498ee`
### Repoint the whynot-design npm lane to Forgejo
Progress: 2/3 done | workplan_id: `42a097db-1c24-558e-a724-030bb2b4443e`
**Open tasks:**
- ! Prove routing and publication `a8b1b855`
*(wait: Hold kept by D3 until SECRETS-WP-0006-T06 moves publishing off the legacy whynot-design npm path; then rotate and prove a fresh publish.)*
## Inbox Hygiene
**Stale unread:** 2 message(s) older than 3 day(s) — triage at session start.
**Missing thread_id:** 3 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.

View file

@ -0,0 +1,188 @@
# Work Records — ops-warden
> 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 | WARDEN-WP-ADHOC-2026-06-27 | finished | — | workplans/ADHOC-2026-06-27.md |
| workplan | WARDEN-WP-ADHOC-2026-06-29 | finished | — | workplans/ADHOC-2026-06-29.md |
| workplan | WARDEN-WP-ADHOC-2026-08-11 | finished | — | workplans/ADHOC-2026-08-11.md |
| workplan | WARDEN-WP-ADHOC-2026-08-17 | finished | — | workplans/ADHOC-2026-08-17.md |
| workplan | WARDEN-WP-ADHOC-2026-09-08 | finished | — | workplans/ADHOC-2026-09-08.md |
| workplan | WARDEN-WP-ADHOC-2026-10-03 | finished | — | workplans/ADHOC-2026-10-03.md |
| workplan | WARDEN-WP-0016 | finished | — | workplans/WARDEN-WP-0016-ops-bridge-tunnel-cert-pilot.md |
| workplan | WARDEN-WP-0017 | finished | — | workplans/WARDEN-WP-0017-access-front-door-discoverability.md |
| workplan | WARDEN-WP-0018 | finished | — | workplans/WARDEN-WP-0018-whynot-design-npm-lane-activation.md |
| workplan | WARDEN-WP-0019 | finished | — | workplans/WARDEN-WP-0019-route-to-secrets-engine.md |
| workplan | WARDEN-WP-0020 | finished | — | workplans/WARDEN-WP-0020-ops-warden-worker.md |
| workplan | WARDEN-WP-0021 | finished | — | workplans/WARDEN-WP-0021-enable-scheduled-worker-tick.md |
| workplan | WARDEN-WP-0022 | finished | — | workplans/WARDEN-WP-0022-audit-trail-and-activity.md |
| workplan | WARDEN-WP-0023 | finished | — | workplans/WARDEN-WP-0023-intent-scope-alignment-closeout.md |
| workplan | WARDEN-WP-0024 | finished | — | workplans/WARDEN-WP-0024-experiential-memory-and-agent-sessions.md |
| workplan | WARDEN-WP-0025 | finished | — | workplans/WARDEN-WP-0025-forgejo-admin-api-token-lane.md |
| workplan | WARDEN-WP-0026 | finished | — | workplans/WARDEN-WP-0026-credential-disclosure-hygiene.md |
| workplan | WARDEN-WP-0027 | blocked | — | workplans/WARDEN-WP-0027-credential-governance-lockdown.md |
| workplan | WARDEN-WP-0028 | finished | — | workplans/WARDEN-WP-0028-tenant-secret-custody.md |
| workplan | WARDEN-WP-0029 | finished | — | workplans/WARDEN-WP-0029-policy-front-door-and-founder-surface.md |
| workplan | WARDEN-WP-0030 | finished | — | workplans/WARDEN-WP-0030-delegation-register.md |
| workplan | WARDEN-WP-0031 | finished | — | workplans/WARDEN-WP-0031-policy-caller-identity.md |
| workplan | WARDEN-WP-0032 | finished | — | workplans/WARDEN-WP-0032-security-zones.md |
| workplan | WARDEN-WP-0033 | finished | — | workplans/WARDEN-WP-0033-native-lane-handoff.md |
| workplan | WARDEN-WP-0034 | active | — | workplans/WARDEN-WP-0034-layer-model-v07-conformance.md |
| workplan | WARDEN-WP-0035 | finished | — | workplans/WARDEN-WP-0035-policy-nexus-forgejo-source-read-route.md |
| workplan | WARDEN-WP-0036 | finished | — | workplans/WARDEN-WP-0036-attended-login-openbao-output.md |
| workplan | WARDEN-WP-0037 | blocked | — | workplans/WARDEN-WP-0037-whynot-design-forgejo-npm-lane.md |
| workplan | WARDEN-WP-0038 | finished | — | workplans/WARDEN-WP-0038-plan-mutation-intent.md |
| workplan | WARDEN-WP-0039 | finished | — | workplans/WARDEN-WP-0039-explicit-policy-refusal.md |
| workplan | WARDEN-WP-0040 | active | — | workplans/WARDEN-WP-0040-unknown-zone-fail-closed-adoption.md |
| workplan | WARDEN-WP-0041 | finished | — | workplans/WARDEN-WP-0041-catalog-lane-scaffolding.md |
| workplan | WARDEN-WP-0042 | finished | — | workplans/WARDEN-WP-0042-openbao-structure-survey-route.md |
| workplan | WARDEN-WP-0043 | finished | — | workplans/WARDEN-WP-0043-platform-supplier-credentials.md |
| workplan | WARDEN-WP-0044 | active | — | workplans/WARDEN-WP-0044-security-case-follow-through.md |
| task | WARDEN-WP-ADHOC-2026-06-27-T01 | done | — | workplans/ADHOC-2026-06-27.md |
| task | WARDEN-WP-ADHOC-2026-06-29-T01 | done | — | workplans/ADHOC-2026-06-29.md |
| task | WARDEN-WP-ADHOC-2026-08-11-T01 | done | — | workplans/ADHOC-2026-08-11.md |
| task | WARDEN-WP-ADHOC-2026-08-11-T02 | done | — | workplans/ADHOC-2026-08-11.md |
| task | WARDEN-WP-ADHOC-2026-08-11-T03 | done | — | workplans/ADHOC-2026-08-11.md |
| task | WARDEN-WP-ADHOC-2026-08-17-T01 | done | — | workplans/ADHOC-2026-08-17.md |
| task | WARDEN-WP-ADHOC-2026-08-17-T02 | done | — | workplans/ADHOC-2026-08-17.md |
| task | WARDEN-WP-ADHOC-2026-08-17-T03 | done | — | workplans/ADHOC-2026-08-17.md |
| task | WARDEN-WP-ADHOC-2026-08-17-T04 | done | — | workplans/ADHOC-2026-08-17.md |
| task | WARDEN-WP-ADHOC-2026-09-08-T01 | done | — | workplans/ADHOC-2026-09-08.md |
| task | WARDEN-WP-ADHOC-2026-09-08-T02 | done | — | workplans/ADHOC-2026-09-08.md |
| task | WARDEN-WP-ADHOC-2026-09-08-T03 | done | — | workplans/ADHOC-2026-09-08.md |
| task | WARDEN-WP-ADHOC-2026-09-08-T04 | done | — | workplans/ADHOC-2026-09-08.md |
| task | WARDEN-WP-ADHOC-2026-09-08-T05 | done | — | workplans/ADHOC-2026-09-08.md |
| task | WARDEN-WP-ADHOC-2026-09-08-T06 | done | — | workplans/ADHOC-2026-09-08.md |
| task | WARDEN-WP-ADHOC-2026-10-03-T01 | done | — | workplans/ADHOC-2026-10-03.md |
| task | WARDEN-WP-0016-T01 | done | — | workplans/WARDEN-WP-0016-ops-bridge-tunnel-cert-pilot.md |
| task | WARDEN-WP-0016-T02 | done | — | workplans/WARDEN-WP-0016-ops-bridge-tunnel-cert-pilot.md |
| task | WARDEN-WP-0016-T03 | done | — | workplans/WARDEN-WP-0016-ops-bridge-tunnel-cert-pilot.md |
| task | WARDEN-WP-0016-T04 | done | — | workplans/WARDEN-WP-0016-ops-bridge-tunnel-cert-pilot.md |
| task | WARDEN-WP-0017-T01 | done | — | workplans/WARDEN-WP-0017-access-front-door-discoverability.md |
| task | WARDEN-WP-0017-T02 | done | — | workplans/WARDEN-WP-0017-access-front-door-discoverability.md |
| task | WARDEN-WP-0017-T03 | done | — | workplans/WARDEN-WP-0017-access-front-door-discoverability.md |
| task | WARDEN-WP-0018-T01 | done | — | workplans/WARDEN-WP-0018-whynot-design-npm-lane-activation.md |
| task | WARDEN-WP-0018-T02 | done | — | workplans/WARDEN-WP-0018-whynot-design-npm-lane-activation.md |
| task | WARDEN-WP-0018-T03 | done | — | workplans/WARDEN-WP-0018-whynot-design-npm-lane-activation.md |
| task | WARDEN-WP-0019-T01 | done | — | workplans/WARDEN-WP-0019-route-to-secrets-engine.md |
| task | WARDEN-WP-0019-T02 | done | — | workplans/WARDEN-WP-0019-route-to-secrets-engine.md |
| task | WARDEN-WP-0020-T01 | done | — | workplans/WARDEN-WP-0020-ops-warden-worker.md |
| task | WARDEN-WP-0020-T02 | done | — | workplans/WARDEN-WP-0020-ops-warden-worker.md |
| task | WARDEN-WP-0020-T03 | done | — | workplans/WARDEN-WP-0020-ops-warden-worker.md |
| task | WARDEN-WP-0020-T04 | done | — | workplans/WARDEN-WP-0020-ops-warden-worker.md |
| task | WARDEN-WP-0020-T05 | done | — | workplans/WARDEN-WP-0020-ops-warden-worker.md |
| task | WARDEN-WP-0021-T01 | done | — | workplans/WARDEN-WP-0021-enable-scheduled-worker-tick.md |
| task | WARDEN-WP-0021-T02 | done | — | workplans/WARDEN-WP-0021-enable-scheduled-worker-tick.md |
| task | WARDEN-WP-0021-T03 | done | — | workplans/WARDEN-WP-0021-enable-scheduled-worker-tick.md |
| task | WARDEN-WP-0021-T04 | done | — | workplans/WARDEN-WP-0021-enable-scheduled-worker-tick.md |
| task | WARDEN-WP-0021-T05 | done | — | workplans/WARDEN-WP-0021-enable-scheduled-worker-tick.md |
| task | WARDEN-WP-0022-T01 | done | — | workplans/WARDEN-WP-0022-audit-trail-and-activity.md |
| task | WARDEN-WP-0022-T02 | done | — | workplans/WARDEN-WP-0022-audit-trail-and-activity.md |
| task | WARDEN-WP-0022-T03 | done | — | workplans/WARDEN-WP-0022-audit-trail-and-activity.md |
| task | WARDEN-WP-0022-T04 | done | — | workplans/WARDEN-WP-0022-audit-trail-and-activity.md |
| task | WARDEN-WP-0023-T01 | done | — | workplans/WARDEN-WP-0023-intent-scope-alignment-closeout.md |
| task | WARDEN-WP-0023-T02 | done | — | workplans/WARDEN-WP-0023-intent-scope-alignment-closeout.md |
| task | WARDEN-WP-0023-T03 | done | — | workplans/WARDEN-WP-0023-intent-scope-alignment-closeout.md |
| task | WARDEN-WP-0023-T04 | done | — | workplans/WARDEN-WP-0023-intent-scope-alignment-closeout.md |
| task | WARDEN-WP-0023-T05 | done | — | workplans/WARDEN-WP-0023-intent-scope-alignment-closeout.md |
| task | WARDEN-WP-0023-T06 | done | — | workplans/WARDEN-WP-0023-intent-scope-alignment-closeout.md |
| task | WARDEN-WP-0023-T07 | done | — | workplans/WARDEN-WP-0023-intent-scope-alignment-closeout.md |
| task | WARDEN-WP-0024-T01 | done | — | workplans/WARDEN-WP-0024-experiential-memory-and-agent-sessions.md |
| task | WARDEN-WP-0024-T02 | done | — | workplans/WARDEN-WP-0024-experiential-memory-and-agent-sessions.md |
| task | WARDEN-WP-0024-T03 | done | — | workplans/WARDEN-WP-0024-experiential-memory-and-agent-sessions.md |
| task | WARDEN-WP-0024-T04 | done | — | workplans/WARDEN-WP-0024-experiential-memory-and-agent-sessions.md |
| task | WARDEN-WP-0024-T05 | done | — | workplans/WARDEN-WP-0024-experiential-memory-and-agent-sessions.md |
| task | WARDEN-WP-0024-T06 | done | — | workplans/WARDEN-WP-0024-experiential-memory-and-agent-sessions.md |
| task | WARDEN-WP-0024-T07 | done | — | workplans/WARDEN-WP-0024-experiential-memory-and-agent-sessions.md |
| task | WARDEN-WP-0025-T01 | done | — | workplans/WARDEN-WP-0025-forgejo-admin-api-token-lane.md |
| task | WARDEN-WP-0025-T02 | done | — | workplans/WARDEN-WP-0025-forgejo-admin-api-token-lane.md |
| task | WARDEN-WP-0025-T03 | done | — | workplans/WARDEN-WP-0025-forgejo-admin-api-token-lane.md |
| task | WARDEN-WP-0025-T04 | done | — | workplans/WARDEN-WP-0025-forgejo-admin-api-token-lane.md |
| task | WARDEN-WP-0025-T05 | done | — | workplans/WARDEN-WP-0025-forgejo-admin-api-token-lane.md |
| task | WARDEN-WP-0026-T01 | done | — | workplans/WARDEN-WP-0026-credential-disclosure-hygiene.md |
| task | WARDEN-WP-0026-T02 | done | — | workplans/WARDEN-WP-0026-credential-disclosure-hygiene.md |
| task | WARDEN-WP-0026-T03 | done | — | workplans/WARDEN-WP-0026-credential-disclosure-hygiene.md |
| task | WARDEN-WP-0026-T04 | done | — | workplans/WARDEN-WP-0026-credential-disclosure-hygiene.md |
| task | WARDEN-WP-0026-T05 | done | — | workplans/WARDEN-WP-0026-credential-disclosure-hygiene.md |
| task | WARDEN-WP-0026-T06 | done | — | workplans/WARDEN-WP-0026-credential-disclosure-hygiene.md |
| task | WARDEN-WP-0026-T07 | done | — | workplans/WARDEN-WP-0026-credential-disclosure-hygiene.md |
| task | WARDEN-WP-0027-T01 | cancel | — | workplans/WARDEN-WP-0027-credential-governance-lockdown.md |
| task | WARDEN-WP-0027-T02 | todo | — | workplans/WARDEN-WP-0027-credential-governance-lockdown.md |
| task | WARDEN-WP-0027-T03 | cancel | — | workplans/WARDEN-WP-0027-credential-governance-lockdown.md |
| task | WARDEN-WP-0028-T01 | done | — | workplans/WARDEN-WP-0028-tenant-secret-custody.md |
| task | WARDEN-WP-0028-T02 | done | — | workplans/WARDEN-WP-0028-tenant-secret-custody.md |
| task | WARDEN-WP-0028-T03 | done | — | workplans/WARDEN-WP-0028-tenant-secret-custody.md |
| task | WARDEN-WP-0028-T04 | done | — | workplans/WARDEN-WP-0028-tenant-secret-custody.md |
| task | WARDEN-WP-0028-T05 | done | — | workplans/WARDEN-WP-0028-tenant-secret-custody.md |
| task | WARDEN-WP-0028-T06 | done | — | workplans/WARDEN-WP-0028-tenant-secret-custody.md |
| task | WARDEN-WP-0028-T07 | done | — | workplans/WARDEN-WP-0028-tenant-secret-custody.md |
| task | WARDEN-WP-0029-T01 | done | — | workplans/WARDEN-WP-0029-policy-front-door-and-founder-surface.md |
| task | WARDEN-WP-0029-T02 | done | — | workplans/WARDEN-WP-0029-policy-front-door-and-founder-surface.md |
| task | WARDEN-WP-0029-T03 | done | — | workplans/WARDEN-WP-0029-policy-front-door-and-founder-surface.md |
| task | WARDEN-WP-0029-T04 | done | — | workplans/WARDEN-WP-0029-policy-front-door-and-founder-surface.md |
| task | WARDEN-WP-0029-T05 | done | — | workplans/WARDEN-WP-0029-policy-front-door-and-founder-surface.md |
| task | WARDEN-WP-0030-T01 | done | — | workplans/WARDEN-WP-0030-delegation-register.md |
| task | WARDEN-WP-0030-T02 | done | — | workplans/WARDEN-WP-0030-delegation-register.md |
| task | WARDEN-WP-0030-T03 | done | — | workplans/WARDEN-WP-0030-delegation-register.md |
| task | WARDEN-WP-0030-T04 | done | — | workplans/WARDEN-WP-0030-delegation-register.md |
| task | WARDEN-WP-0030-T05 | done | — | workplans/WARDEN-WP-0030-delegation-register.md |
| task | WARDEN-WP-0031-T01 | done | — | workplans/WARDEN-WP-0031-policy-caller-identity.md |
| task | WARDEN-WP-0031-T02 | done | — | workplans/WARDEN-WP-0031-policy-caller-identity.md |
| task | WARDEN-WP-0031-T03 | done | — | workplans/WARDEN-WP-0031-policy-caller-identity.md |
| task | WARDEN-WP-0031-T04 | done | — | workplans/WARDEN-WP-0031-policy-caller-identity.md |
| task | WARDEN-WP-0031-T05 | cancel | — | workplans/WARDEN-WP-0031-policy-caller-identity.md |
| task | WARDEN-WP-0032-T01 | done | — | workplans/WARDEN-WP-0032-security-zones.md |
| task | WARDEN-WP-0032-T02 | done | — | workplans/WARDEN-WP-0032-security-zones.md |
| task | WARDEN-WP-0032-T03 | done | — | workplans/WARDEN-WP-0032-security-zones.md |
| task | WARDEN-WP-0032-T04 | done | — | workplans/WARDEN-WP-0032-security-zones.md |
| task | WARDEN-WP-0032-T05 | done | — | workplans/WARDEN-WP-0032-security-zones.md |
| task | WARDEN-WP-0032-T06 | done | — | workplans/WARDEN-WP-0032-security-zones.md |
| task | WARDEN-WP-0032-T07 | done | — | workplans/WARDEN-WP-0032-security-zones.md |
| task | WARDEN-WP-0033-T01 | done | — | workplans/WARDEN-WP-0033-native-lane-handoff.md |
| task | WARDEN-WP-0033-T02 | done | — | workplans/WARDEN-WP-0033-native-lane-handoff.md |
| task | WARDEN-WP-0033-T03 | done | — | workplans/WARDEN-WP-0033-native-lane-handoff.md |
| task | WARDEN-WP-0033-T04 | done | — | workplans/WARDEN-WP-0033-native-lane-handoff.md |
| task | WARDEN-WP-0033-T05 | done | — | workplans/WARDEN-WP-0033-native-lane-handoff.md |
| task | WARDEN-WP-0033-T06 | done | — | workplans/WARDEN-WP-0033-native-lane-handoff.md |
| task | WARDEN-WP-0034-T01 | done | — | workplans/WARDEN-WP-0034-layer-model-v07-conformance.md |
| task | WARDEN-WP-0034-T02 | done | — | workplans/WARDEN-WP-0034-layer-model-v07-conformance.md |
| task | WARDEN-WP-0034-T03 | done | — | workplans/WARDEN-WP-0034-layer-model-v07-conformance.md |
| task | WARDEN-WP-0034-T04 | done | — | workplans/WARDEN-WP-0034-layer-model-v07-conformance.md |
| task | WARDEN-WP-0034-T05 | wait | — | workplans/WARDEN-WP-0034-layer-model-v07-conformance.md |
| task | WARDEN-WP-0034-T06 | done | — | workplans/WARDEN-WP-0034-layer-model-v07-conformance.md |
| task | WARDEN-WP-0035-T01 | done | — | workplans/WARDEN-WP-0035-policy-nexus-forgejo-source-read-route.md |
| task | WARDEN-WP-0035-T02 | done | — | workplans/WARDEN-WP-0035-policy-nexus-forgejo-source-read-route.md |
| task | WARDEN-WP-0036-T01 | done | — | workplans/WARDEN-WP-0036-attended-login-openbao-output.md |
| task | WARDEN-WP-0036-T02 | done | — | workplans/WARDEN-WP-0036-attended-login-openbao-output.md |
| task | WARDEN-WP-0037-T01 | done | — | workplans/WARDEN-WP-0037-whynot-design-forgejo-npm-lane.md |
| task | WARDEN-WP-0037-T02 | done | — | workplans/WARDEN-WP-0037-whynot-design-forgejo-npm-lane.md |
| task | WARDEN-WP-0037-T03 | wait | — | workplans/WARDEN-WP-0037-whynot-design-forgejo-npm-lane.md |
| task | WARDEN-WP-0038-T01 | done | — | workplans/WARDEN-WP-0038-plan-mutation-intent.md |
| task | WARDEN-WP-0039-T01 | done | — | workplans/WARDEN-WP-0039-explicit-policy-refusal.md |
| task | WARDEN-WP-0039-T02 | done | — | workplans/WARDEN-WP-0039-explicit-policy-refusal.md |
| task | WARDEN-WP-0039-T03 | done | — | workplans/WARDEN-WP-0039-explicit-policy-refusal.md |
| task | WARDEN-WP-0040-T01 | wait | — | workplans/WARDEN-WP-0040-unknown-zone-fail-closed-adoption.md |
| task | WARDEN-WP-0040-T02 | wait | — | workplans/WARDEN-WP-0040-unknown-zone-fail-closed-adoption.md |
| task | WARDEN-WP-0040-T03 | wait | — | workplans/WARDEN-WP-0040-unknown-zone-fail-closed-adoption.md |
| task | WARDEN-WP-0040-T04 | done | — | workplans/WARDEN-WP-0040-unknown-zone-fail-closed-adoption.md |
| task | WARDEN-WP-0041-T01 | done | — | workplans/WARDEN-WP-0041-catalog-lane-scaffolding.md |
| task | WARDEN-WP-0041-T02 | done | — | workplans/WARDEN-WP-0041-catalog-lane-scaffolding.md |
| task | WARDEN-WP-0041-T03 | done | — | workplans/WARDEN-WP-0041-catalog-lane-scaffolding.md |
| task | WARDEN-WP-0041-T04 | done | — | workplans/WARDEN-WP-0041-catalog-lane-scaffolding.md |
| task | WARDEN-WP-0042-T01 | done | — | workplans/WARDEN-WP-0042-openbao-structure-survey-route.md |
| task | WARDEN-WP-0042-T02 | done | — | workplans/WARDEN-WP-0042-openbao-structure-survey-route.md |
| task | WARDEN-WP-0043-T01 | done | — | workplans/WARDEN-WP-0043-platform-supplier-credentials.md |
| task | WARDEN-WP-0043-T02 | done | — | workplans/WARDEN-WP-0043-platform-supplier-credentials.md |
| task | WARDEN-WP-0043-T03 | done | — | workplans/WARDEN-WP-0043-platform-supplier-credentials.md |
| task | WARDEN-WP-0044-T01 | done | — | workplans/WARDEN-WP-0044-security-case-follow-through.md |
| task | WARDEN-WP-0044-T02 | done | — | workplans/WARDEN-WP-0044-security-case-follow-through.md |
| task | WARDEN-WP-0044-T03 | progress | — | workplans/WARDEN-WP-0044-security-case-follow-through.md |
| task | WARDEN-WP-0044-T04 | wait | — | workplans/WARDEN-WP-0044-security-case-follow-through.md |
| intake | WARDEN-IN-0001 | closed | — | intakes/intakes.md |
| intake | WARDEN-IN-0002 | closed | — | intakes/intakes.md |
| intake | WARDEN-IN-0003 | closed | — | intakes/intakes.md |

View file

@ -0,0 +1,41 @@
---
id: WARDEN-WP-ADHOC-2026-06-27
type: workplan
title: "Ad Hoc Tasks — 2026-06-27"
domain: infotech
repo: ops-warden
status: finished
owner: claude
topic_slug: custodian
created: "2026-06-27"
updated: "2026-06-27"
state_hub_workstream_id: "a222c91f-3bb5-58a4-b6b2-f0fb18cdd5c3"
---
# Ad Hoc Tasks — 2026-06-27
Low-risk opportunistic fixes completed directly during the consolidation session.
### T01 — Fix stale `warden` CLI install + make it usable outside the repo
```task
id: WARDEN-WP-ADHOC-2026-06-27-T01
status: done
priority: medium
state_hub_task_id: "9176b560-8ca5-5143-888d-479857fe60f0"
```
issue-core reported (msg `70bcf238`) that the `warden` CLI on `~/.local/bin` lacked
the `route` subcommand, forcing a `uv run warden` fallback.
- [x] Root cause: `uv tool install` had reused a **cached wheel** (version stayed
`0.1.0`), so the installed `warden.cli` predated the `route`/`access`/`policy`
subcommands. `uv cache clean ops-warden` + `uv tool install . --reinstall` fixed it.
- [x] Deeper cause: even rebuilt, `warden route`/`policy` failed outside a checkout
because the catalog + posture descriptors live in `registry/` at repo root,
outside the package. Bundled `registry/` into the wheel via hatch
`force-include` → `warden/_registry`, and added a packaged-data fallback in
`find_catalog_path` / `find_posture_path` (after the repo walk, so source runs
still prefer the repo's `registry/` as the single source of truth).
- [x] Verified `warden route list` / `warden policy list` work from `/tmp`; 200 tests
pass, lint clean.

View file

@ -0,0 +1,40 @@
---
id: WARDEN-WP-ADHOC-2026-06-29
type: workplan
title: "Ad Hoc Tasks — 2026-06-29"
domain: infotech
repo: ops-warden
status: finished
owner: claude
topic_slug: custodian
created: "2026-06-29"
updated: "2026-06-29"
state_hub_workstream_id: "13fa845f-852e-55ec-a2a5-2296996e0216"
---
# Ad Hoc Tasks — 2026-06-29
### T01 — Joint-smoke mode for the deployed flex-auth (assist FLEX-WP-0007 T4)
```task
id: WARDEN-WP-ADHOC-2026-06-29-T01
status: done
priority: medium
state_hub_task_id: "62540533-f4ca-5176-9237-32adbeb292ee"
```
flex-auth (msg `ea00620b`) asked ops-warden to help close FLEX-WP-0007 T4 (joint OpenBao
+ policy-gate production smoke). Their deployed runtime is reachable on CoulombCore via
the flex-auth-coulombcore tunnel at `127.0.0.1:18090`, but `policy_gate_production_smoke.sh`
spawned its **own** local flex-auth binary — so it never exercised the deployed runtime.
- [x] Added `FLEX_AUTH_EXTERNAL=1` mode to `scripts/policy_gate_production_smoke.sh`: skips
the local `serve`/`load-registry` and runs the allow/deny/vault paths against the
already-running deployed flex-auth, with a `/healthz` precheck that fails fast with a
"is the flex-auth-coulombcore tunnel up?" hint (verified: clean exit 2 when down).
- [x] Verified the committed `production_registry_snapshot.json` is **current** (rebuilt
from `~/.config/warden/inventory.yaml`, diff-clean; 4 actors).
- [x] Answered flex-auth's three questions and handed the operator the exact CoulombCore
runbook (see reply). Remaining T4 steps are operator-gated and cannot run from the
workstation: mint a scoped `VAULT_TOKEN` (ops-warden holds no standing token by
design), run the joint smoke on CoulombCore, then flip `policy.enabled: true`.

View file

@ -0,0 +1,145 @@
---
id: WARDEN-WP-ADHOC-2026-08-11
type: workplan
title: "Ad Hoc Tasks — 2026-08-11"
domain: infotech
repo: ops-warden
status: finished
owner: claude
topic_slug: custodian
created: "2026-08-11"
updated: "2026-08-15"
state_hub_workstream_id: "9f99cc64-4682-5f20-b13e-89af2b6f7c70"
---
# Ad Hoc Tasks — 2026-08-11
### T01 — Repair stale `rapp-qonto-keycape-client` wiki anchor (restore green routing suite)
```task
id: WARDEN-WP-ADHOC-2026-08-11-T01
status: done
priority: medium
state_hub_task_id: "0771d121-278c-556e-9509-841cf6e657c3"
```
rapp-postgres (msg `96907986`, residual from RAPP-POSTGRES-WP-0002-T04) reported the
focused routing suite at 60/61: `test_every_wiki_ref_anchor_resolves` failed because
`rapp-qonto-keycape-client` pointed at `wiki/CredentialRouting.md#credential-routing-catalog`,
an anchor that does not exist. The intended heading is `## Routing catalog index`.
- [x] Repointed `rapp-qonto-keycape-client.wiki_ref` to
`wiki/CredentialRouting.md#routing-catalog-index` (the live heading). No other
entry used the stale anchor.
- [x] `uv run pytest tests/test_routing.py -q` → **61 passed**.
- [x] `warden route find "database credential"` still returns the active
`database-dynamic-credentials` rapp-postgres entry; that T04 route is untouched —
still `warden_executes: false` with no `steps:` block (pointer, not procedure).
### T02 — Triage the stale ops-warden inbox (11 unread, C-28/C-29)
```task
id: WARDEN-WP-ADHOC-2026-08-11-T02
status: done
priority: medium
state_hub_task_id: "0ed58145-732f-5102-b6a8-b931d9b6ba08"
```
`fix-consistency` flagged 11 unread messages older than 3 days, two of them as possible
work requests (C-29). All eleven triaged, answered where an answer was owed, and marked
read. Inbox is now empty.
- [x] **railiance-platform front-door thread** (`72d3ee84`, `cb50ea3c`, `6b058584`,
`5d47caaa`, `78c1c075`) — confirmed both lanes active and placeholder-free
(`issue-core-ingestion-api-key` catalog.yaml:219, `openrouter-llm-connect`
catalog.yaml:283, promoted in 364eb7d), closing RAILIANCE-WP-0009-T06 and
RAILIANCE-WP-0010-T06. Answered their open question: the stable ops-warden
selector is **`openrouter-llm-connect`**, not `llm-connect-openrouter-api-key`;
asked them to cross-reference the id in CCR-2026-0003 rather than have ops-warden
rename an active, test-referenced lane.
- [x] **railiance01 / activity-core** (`467153f3`, `3b9c6a77`, `fe796249`, `674f02e0`) —
superseded. STATE-WP-0071 finished; the `.git/FETCH_HEAD` blocker was filesystem
ownership, not SSH, and no `warden sign` was needed. Nothing was ever open for
ops-warden. Pointed activity-core at `warden plan` / `warden route find` instead of
hub round-trips for access questions.
- [x] **llm-connect LLM-WP-0006** (`f5975211`) — superseded: `OPENROUTER_API_KEY` was
provisioned through OpenBao custody 2026-07-02 (CCR-2026-0003), ES synced,
llm-connect rolled out. Restated the boundary — ops-warden does not populate
Secrets — and pointed at the now-active catalog lane. `LLM_CONNECT_URL` wiring
remains activity-core's.
- [x] **secrets-engine warden-sign** (`80456912`) — see T03; replied with status and
referred the live-apply question to the owner.
### T03 — warden-sign AppRole: PARKED pending WP-0027 break-glass + ops-bridge cutover
```task
id: WARDEN-WP-ADHOC-2026-08-11-T03
status: done
priority: medium
state_hub_task_id: "337ae793-c6b0-59e9-8a07-3a7ccba237aa"
```
secrets-engine (msg `80456912`, 2026-06-29) is holding a validated non-mutating dry-run
for policy + AppRole `warden-sign` (exact `update` grants on `ssh/sign/{agt,adm,atm}-role`).
It is blocked on two **operator** actions: recording/approving the SECRETS-WP-0004
decision, and providing the mode-0600 lane bootstrap token
(`~/.secrets-engine/bootstrap/prod-warden-sign.token`). secrets-engine correctly refused
to substitute the broader platform-admin token.
**The request has been overtaken by events.** Since 2026-07-01 the scoped `VAULT_TOKEN`
need is served by the railiance-platform credential broker
(`ops-warden-warden-sign-token`, active; `credential.py exec --grant ops-warden/warden-sign`,
proven via `make credential-exec-ops-warden-smoke`) — no AppRole involved.
**Resolved 2026-08-11: parked** — neither withdrawn nor proceeding. Founder decision
after reviewing the mechanics. Communicated to secrets-engine (msg `863c7b57`) with an
explicit instruction to stop holding apply readiness.
An initial recommendation to **withdraw** was revised on inspection:
- `warden sign` reads `VAULT_TOKEN` from the environment and has no AppRole login path
(`src/warden/vault.py:24-27`). The AppRole is a way for a *host* to obtain a token,
not an ops-warden code path — withdrawing costs no code and removes no working
capability. For the workstation, the broker plus attended operator OIDC is sufficient.
- The uncovered case is **unattended signing on a remote host** — the pending ops-bridge
cert_command cutover. `wiki/playbooks/ops-bridge-tunnel-cert.md:61` still falls back to
a manually exported `VAULT_TOKEN` there.
- Running the broker on that host means placing the railiance-platform checkout *and its
issuer token* (`credential-broker-warden-sign-issuer` — authority to mint warden-sign
tokens repeatedly) on it. The AppRole exchanges for a `warden-sign`-only token
(`update` on `ssh/sign/{agt,adm,atm}-role`). Both are standing credentials on a remote
host; **the AppRole is the narrower one**, and is the established NetKingdom pattern
for this shape (`rein-openweights-openrouter-approle`,
`agent-harness-binky-mail-approle`).
No circular dependency exists today: OpenBao is a public endpoint
(`https://bao.coulomb.social`), so obtaining a cert never requires already holding one.
**Un-park triggers** (either one re-opens the question):
1. **WARDEN-WP-0027** (backlog) designs the graded lockdown / break-glass path and its
explicit trust-root — a second autonomous path to signing is an input to that design,
not an isolated decision.
2. **ops-bridge cert_command live cutover** reaches unattended signing on a remote host.
Cheap to reverse: the secrets-engine dry-run plan is validated, re-runnable, and correct
as drafted. The two operator gates (SECRETS-WP-0004 decision, mode-0600 lane bootstrap
token) are deliberately *not* being satisfied while parked.
**Closed 2026-08-15.** Neither un-park trigger has fired:
1. `WARDEN-WP-0027` is still `backlog`; T02 (graded lockdown / break-glass) stays
`cancel` with the workplan. The parked AppRole remains an input on that task.
2. ops-bridge live cert_command cutover has not reached unattended signing on a
remote host. WP-0016 is finished as *pilot-ready and handed off*, not migrated.
This ad-hoc's job was the operator question (withdraw vs keep). The 2026-08-11
answer stands: **neither — park and hand off**. Applying or withdrawing the
AppRole is out of scope here and would re-open the operator gates that were
deliberately left unsatisfied. Long-term ownership is `WARDEN-WP-0027-T02`
(and the ops-bridge cutover if that arrives first). secrets-engine already has
the park instruction (msg `863c7b57`); no new apply/withdraw ask.
- [x] Park decision recorded and communicated (2026-08-11)
- [x] Ownership transferred to `WARDEN-WP-0027` T02; playbook pointer updated
- [x] Ad-hoc closed so a `wait` item does not linger past the handoff

View file

@ -0,0 +1,129 @@
---
id: WARDEN-WP-ADHOC-2026-08-17
type: workplan
title: "Ad Hoc Tasks — 2026-08-17"
domain: infotech
repo: ops-warden
status: finished
owner: claude
topic_slug: custodian
created: "2026-08-17"
updated: "2026-08-17"
state_hub_workstream_id: "5c6c2bbb-b944-5afd-b89c-20d865518849"
---
# Ad Hoc Tasks — 2026-08-17
Inbox triage session. Three unread messages, all follow-on from the WP-0030 delegation
register plus one new design question that lands on ops-warden as the estate's
workload-identity owner.
### T01 — Answer flex-auth: how should `/v1/check` authenticate its callers?
```task
id: WARDEN-WP-ADHOC-2026-08-17-T01
status: done
priority: high
state_hub_task_id: "04a2f8f9-e70b-5eed-ad87-343c8f9ef501"
```
flex-auth (msg `130a148c`, FLEX-WP-0015 T02) reported that `POST /v1/check` and
`/v1/batch_check` authenticate no caller — any workload with cluster network reach can
assert any subject/tenant and receive an authoritative allow. It lands on ops-warden
because ops-warden owns how workloads prove identity in this estate, and because the
ops-warden pre-sign gate is a flex-auth PEP that would implement the calling side.
Their four questions answered in `wiki/NetKingdomSecurityMap.md`
§ *Service-to-service caller authentication (in-cluster)*:
- [x] **Q1 — is there an existing estate pattern to adopt?** No. Surveyed what exists:
ops-warden SSH certs (host reachability, not pod→pod HTTP), KeyCape
`client_credentials` (`rapp-qonto-keycape-client` — a custodied client secret per
caller), OpenBao AppRole (host-standing; the WP-0030 register already flags it as
having no owner front door). None covers in-cluster service→service HTTP, so
flex-auth is not growing a parallel mechanism by adopting one.
- [x] **Q2 — which mechanism?** Confirmed their instinct: **(a) Kubernetes
ServiceAccount TokenReview**, with a *projected* token carrying an explicit
`audience`, keeping `automountServiceAccountToken: false` and adding the volume
per Deployment. Rejected (c) shared-secret header — it manufactures a `risk: high`
credential lane with a rotation owner per caller, on the authorization path, which
is exactly the interim-proxy debt WP-0030 exists to stop growing, and ops-warden
would end up fronting it. Deferred (b) mTLS as the stronger end state that first
needs an answer to *who owns the workload X.509 CA* — nobody does; ops-warden
issues SSH certs, not workload X.509.
- [x] **Q3 — authenticate only, or also constrain?** Both, but split: bind the asserted
`system` to the authenticated ServiceAccount and reject a mismatch (identity
binding, no per-consumer operational cost). Keep the *resource-type* allowlist
("only ops-warden may ask about `ssh-certificate`") in the policy package, not in
the auth middleware — flex-auth is the policy engine, and encoding it in its own
admission layer puts authorization in two places where only one is versioned.
- [x] **Q4 — rollout shape?** Warn-only first, as they proposed; ops-warden adopts the
calling side on its own schedule. Binding condition is sequencing, not a date:
`policy.enabled` must not flip anywhere while `/v1/check` still answers
unauthenticated callers.
- [x] Recorded as a **pattern**, not a catalog entry — `registry/routing/catalog.yaml`
indexes credential needs and their owners; caller authentication is neither.
- [x] Noted the division of the call: mechanism is ops-warden's/architectural; accepting
the pod-spec change and rollout timing are the operator's.
### T02 — user-engine: USER_ENGINE_PROXY_SECRET stays railiance-apps; record consumer-only
```task
id: WARDEN-WP-ADHOC-2026-08-17-T02
status: done
priority: medium
state_hub_task_id: "0e815282-2fad-5c8d-be34-398e492737d0"
```
user-engine (msg `2af4a124`) answered the open confirm question on
`coulomb-social-runtime-env`. Decided by Bernd 2026-08-16, State Hub decision
`8fe22037-5bbb-4487-bb86-e4beccee454b` against USER-WP-0021: the secret is
infrastructure trust between ingress and workload, not a user-domain fact; user-engine
consumes it and has no authority over custody, rotation, or issuance.
- [x] `intended_owner` stays `railiance-apps`; dropped the now-answered
"confirm whether user-engine should front USER_ENGINE_PROXY_SECRET" clause from
`blocked_on`, which now names only the real remaining blocker (CCR not applied).
- [x] Recorded the positive signal they asked for: `consumers: [user-engine]` on the
lane, with the decision id in a comment.
- [x] Their second point — `USER_ENGINE_EVENT_TOKEN`, `USER_ENGINE_MAIL_TOKEN`, and the
transactional SMTP lane gating public registration — checked against the catalog:
`audit-core-senders` (native → ops-mason) and `email-connect-transactional`
(interim → secrets-engine) already cover the event and mail token custody. No new
lane opened; told them which ids to watch.
### T03 — key-cape: `rapp-qonto-keycape-client` interim accepted; refresh the blocker
```task
id: WARDEN-WP-ADHOC-2026-08-17-T03
status: done
priority: medium
state_hub_task_id: "e21781d9-a35d-5916-b335-d12131f97a22"
```
key-cape (msg `099b7cba`) acknowledged the lane staying interim on ops-warden.
KEY-WP-0008 is finished and its closeout does **not** include a key-cape-native
`client_secret_basic` exchange/rotation command; that is a separate, not-yet-opened
workplan.
- [x] Refreshed `blocked_on` so it names the current reality rather than implying an
in-flight KEY-WP-0008 dependency: the native command is unopened work, and
key-cape has acked the interim hold.
- [x] `key-cape-oidc-login` left pointing at `secrets-engine` — key-cape explicitly did
not claim the generic-vs-per-lane custody split, and that lane's blocker is the
same unanswered secrets-engine question as the other six.
- [x] `reviewed` bumped to 2026-08-17 on both touched lanes so `warden route gaps`
staleness reflects a real re-check.
### T04 — Session hygiene
```task
id: WARDEN-WP-ADHOC-2026-08-17-T04
status: done
priority: low
state_hub_task_id: "b15724e0-c27a-5260-a810-4dd25bff2228"
```
- [x] `uv run pytest -q` → 338 passed, 4 deselected (routing anchor + no-double-source
checks green after the catalog edits).
- [x] All three messages replied to and marked read; inbox empty.

View file

@ -0,0 +1,179 @@
---
id: WARDEN-WP-ADHOC-2026-09-08
type: workplan
title: "Ad Hoc Tasks — 2026-09-08"
domain: infotech
repo: ops-warden
status: finished
owner: claude
topic_slug: netkingdom
created: "2026-09-08"
updated: "2026-09-08"
state_hub_workstream_id: "182f3aff-8c8a-5766-8a68-5fe35f423f7a"
---
# Ad Hoc Tasks — 2026-09-08
Four key-cape messages, all follow-on from `WARDEN-WP-0033`. Two carry answers
ops-warden's catalog was waiting for; both make a recorded blocker less true than
it reads. This is the T05 mechanism paying out — and this time the owner ran it
against us rather than us running it against them.
### T01 — Narrow the `rapp-qonto-keycape-client` blocker (msg `08d42f47`)
```task
id: WARDEN-WP-ADHOC-2026-09-08-T01
status: done
priority: high
state_hub_task_id: "29cef28a-d93b-5318-bfdb-69dbe11fa3ba"
```
The blocker recorded "still no key-cape-native exchange/rotation command",
`verified: source-read` against key-cape source on 2026-08-28. Accurate then;
two things landed after that read:
- `keycape service-token` (2026-09-05, KEY-WP-0014-T03) — the client_secret_basic
exchange the blocker records as absent.
- `keycape verify-client` (2026-09-08) — rotation step 3 as one command,
including predecessor rejection and identical-secret detection.
**Narrowed, not cleared**, as key-cape asked. What remains genuinely blocked is
steps 1-2: successor generation and the CAS write to both custodians. Those are
custody and deployment acts, key-cape deliberately ships no command for them, and
no admitted ops-warden lane authorizes them either (T03).
`verified` moved to `owner-confirmed` — the correction came from the owner.
`rotation.automatable` flipped `true` → `false`, which key-cape explicitly asked
us not to do "either way" without precision. Done with the precision they wanted,
in the only place the schema has room for it: the flag is a single hint consumed
by a future executable driver (`WARDEN-WP-0027` Strand B), and a driver told
`true` would attempt custody acts with no admitted transport — the dangerous
direction. So the flag fails safe and the *steps* now carry the per-step truth,
each naming whether it is automatable and why. Offered them a schema change if a
per-step field would serve better; not built on speculation.
### T02 — Record key-cape's acceptance of `key-cape-oidc-login` (msg `12f1bdfa`)
```task
id: WARDEN-WP-ADHOC-2026-09-08-T02
status: done
priority: high
state_hub_task_id: "8efb8013-7b90-52ef-a17c-d63f5ce3752c"
```
key-cape **accepted** ownership: interactive login, OIDC identity-token issuance
and MFA. This confirms the 2026-08-28 correction of `intended_owner` from
secrets-engine to key-cape, and confirms secrets-engine's 2026-08-21 refusal was
right. `verified: asked-and-waiting` → `owner-confirmed`.
**The lane stays `interim`.** Acceptance covers the identity half only. The
`fetch_command` yields an *OpenBao* token — the `netkingdom` mount, the
`role=<domain>` mapping, the token store and its enforcement are OpenBao's, with
role and policy definition railiance-platform's. `keycape login` yields a
key-cape issuer JWT to a private file: different credential type, different
verifier, different enforcement owner. Accepting an owner is not the same as the
owner fronting the lane, and `ADR-0003` says the interim cover is retired when
the front door exists, not when ownership is agreed. Retirement condition is now
recorded on the entry.
Their reading of the risk is right and we confirmed it: `warden_executes: false`
with a `wiki_ref` pointer, so per `ADR-0001` this is a pointer layer, not a code
path with programmatic consumers. `risk: standard` and
`workload_ref: not-applicable` are correct — a human identity act with no KV
path, consistent with how `ADR-0008` treats the other non-KV lanes.
### T03 — Answer "who executes Qonto rotation steps 1-2" (msg `0a6c9d88`)
```task
id: WARDEN-WP-ADHOC-2026-09-08-T03
status: done
priority: high
state_hub_task_id: "bd621821-2116-51c9-a09a-a3660b5e5557"
```
Answer: **nobody, admittedly, today** — and ops-warden must not invent one.
Steps 1-2 mutate custody on `platform/workloads/rapp-qonto/keycape-client` and
`sso/keycape-rapp-qonto-client`. Custody of platform workload paths is
railiance-platform's; the admitted execution front door for provisioned
secret-exec is `secrets-engine exec`. ops-warden owns neither and absorbing it
would be `ADR-0002` and `ADR-0005` violated in one act. Routed to
railiance-platform, with secrets-engine named as the transport owner — and
secrets-engine independently confirmed the same boundary three days earlier
(msg `15f0c0ca`): they will not rewrite a production lane's custody state from a
coordination message without owner confirmation.
key-cape was right not to treat a generic `warden route` match as authorization.
See T04 for what our own tool did when asked.
### T04 — `warden plan` returned `autonomous` for a custody *write* (defect)
```task
id: WARDEN-WP-ADHOC-2026-09-08-T04
status: done
priority: high
state_hub_task_id: "9ada01d2-b14b-579e-921c-275820f255db"
```
Verifying T03 against our own front door:
```
warden plan "generate a successor client secret ... and CAS-write it to
OpenBao platform/workloads/... and sso/..." --json
verdict: autonomous
lane_id: rapp-qonto-keycape-client
commands: warden access ... --exec / --out / --wrap
```
A need that says *generate* and *CAS-write* resolved to `autonomous` and was
answered with three **read** transports. This is the `WARDEN-WP-0033-T06` shape
recurring — a generic keyword match producing a confident wrong act — except T06
fixed one route by adding an exact lane, and the defect is in the classifier.
`warden plan` has no notion of read versus mutate intent, so every write need
matches the lane that *reads* the same path and comes back `autonomous`.
Not fixed here: a classifier is design work with an approval-semantics blast
radius, which the convention says is a workplan, not an adhoc.
Promoted to **`WARDEN-WP-0038`** (`proposed`).
Worth stating plainly: key-cape distrusted this output on principle and was right
to. Had they trusted it, our tool would have told them to proceed autonomously on
an unadmitted custody mutation.
### T05 — Acknowledge the KEY-WP-0009 handoff (msg `5bc58847`)
```task
id: WARDEN-WP-ADHOC-2026-09-08-T05
status: done
priority: low
state_hub_task_id: "06bf8508-d1a2-51b4-b951-02539524ebb9"
```
Informational; no route retired or changed. Their routing note matches ours:
"login / OIDC / MFA" → key-cape means **issuance only**, and secret *values*
route to OpenBao custody. Nothing to change in `wiki/CredentialRouting.md`.
Also records that the KEY-WP-0009 reply ops-warden reported never arriving indeed
never existed (KEY-WP-0014-T02, audited 2026-09-05). Our 2026-08-28 finding was
right, and the correction is theirs, recorded.
### T06 — Unpin two tests from a transient catalog state
```task
id: WARDEN-WP-ADHOC-2026-09-08-T06
status: done
priority: medium
state_hub_task_id: "a0b88db9-2886-54dd-a925-9e0521044aa0"
```
`test_stale_gaps_flags_unverified_even_when_the_date_is_today` and
`test_cli_route_gaps_fail_on_stale_exits_3` both asserted that
`key-cape-oidc-login` specifically sat `asked-and-waiting`. T02 answering that
question broke them — **the tests failed on good news**, not on a defect, and a
test that fails when a counterparty replies will get suppressed rather than read.
Both now assert the property they were always about: any lane whose blocker was
never re-established is stale regardless of its date, over whatever lanes are in
that state. The named-lane version of the claim already exists as a unit test on
`Delegation.is_verified`, which is where a fixture belongs.

View file

@ -0,0 +1,31 @@
---
id: WARDEN-WP-ADHOC-2026-10-03
type: workplan
title: "Documentary feedback on Emergent Security repository pilot"
domain: infotech
repo: ops-warden
status: finished
owner: codex
topic_slug: ops-warden
created: "2026-10-03"
updated: "2026-10-03"
state_hub_workstream_id: "246dc069-0f9b-516b-9db4-9914323da16f"
---
## Review retained pilot evidence against owner boundaries
```task
id: WARDEN-WP-ADHOC-2026-10-03-T01
status: done
priority: medium
state_hub_task_id: "c532cd72-88c4-543b-b6db-caf1bcb74a3d"
```
Evidence: docs/evidence/2026-10-03-emergent-pilot-owner-feedback.md.
Completed authorized documentary feedback on the retained read-only 0.2.0
Claude scenario; preserved original version and evidence limits, qualified
owner boundary wording and identified bounded 0.2.2 follow-up scenarios.
No provider session, runtime control, deployment, credential or institutional
adoption was performed. Next pilot/adoption gates remain existing
EMERGENT-WP-0002-T08/T09 and decision d20a9750-2b29-534e-8227-fb866581a6ca.
No new operational residual was created.

View file

@ -0,0 +1,124 @@
---
id: WARDEN-WP-0016
type: workplan
title: "ops-bridge cert_command pilot — readiness gate + handoff"
domain: infotech
repo: ops-warden
status: finished
owner: claude
topic_slug: custodian
planning_priority: high
planning_order: 16
created: "2026-06-27"
updated: "2026-06-27"
state_hub_workstream_id: "a56da8db-38bc-4bbe-8671-823360ec9245"
---
# WARDEN-WP-0016 — ops-bridge cert_command pilot (readiness gate + handoff)
**Scope:** Close ops-warden's side of the last **Partial** INTENT criterion — *"ops-bridge
integrates via a stable `cert_command`"*. The migration playbook
(`wiki/playbooks/ops-bridge-tunnel-cert.md`, WP-0013) and the `cert_command` contract
(`wiki/CertCommandInterface.md`) already exist, but the pilot has never been run because
the readiness checks are scattered manual checkboxes across three owners (ops-warden,
ops-bridge, railiance-infra). This WP ships the **automated readiness gate** an operator
runs *before* touching tunnel config, plus an offline `cert_command` contract smoke, and
hands the verified pilot to ops-bridge.
**Boundary (unchanged):** ops-warden issues certs and verifies its own side is ready.
The **live tunnel cutover is ops-bridge's to execute** — this WP does not (cannot) flip a
running tunnel. "Done" here means *pilot-ready and handed off*, not *tunnel migrated*.
**Out of scope:** editing `~/.config/bridge/tunnels.yaml` (ops-bridge owns it); deploying
host principals (railiance-infra); requiring a live OpenBao token for the contract smoke
(use the local backend).
**Depends on:** WP-0013 (playbook + contract), the SSH lane (prod-verified).
---
## Tasks
### T1 — Read-only `cert_command` readiness preflight
```task
id: WARDEN-WP-0016-T01
status: done
priority: high
state_hub_task_id: "fea84495-dbec-480a-b42b-90e39f414b78"
```
- [x] `scripts/check_tunnel_cert_readiness.py` — given `--actor`, `--pubkey`, `--config`
(warden.yaml) and optional `--infra` (ssh_principals.yaml), asserts the cert_command
path is ready **without signing anything**: config loads + backend known; actor in
inventory with a valid type + TTL within the type max; pubkey file exists, parses,
and is not a private key; actor principals present; (optional) principals deployed
in the infra file (mirrors `check_principals_drift._infra_principals`). Exit 0/1/2.
- [x] Checklist-style report (✓/✗/·); never prints a private key or token.
- [x] Tests: `tests/test_tunnel_cert_readiness.py` (ready, unknown actor, missing/private
pubkey, infra present/missing, TTL-over-max, cert_command string). 9 unit cases.
### T2 — Offline cert_command contract smoke
```task
id: WARDEN-WP-0016-T02
status: done
priority: medium
state_hub_task_id: "e34ae1a8-2ba9-4324-8d1a-005d61dae478"
```
- [x] Opt-in `--sign-smoke` mode runs the actual `cert_command` against the **local**
backend and validates the emitted cert: identity matches the actor, principals match
inventory, validity window within the type's max TTL. Refuses a vault backend (must
be offline). Proves the contract end to end with no live OpenBao.
- [x] Window measured from the cert's own `valid_from`→`valid_before` (via
`parse_cert_metadata`) so it is timezone-robust — fixes a CEST off-by-2h artifact
where local-time ssh-keygen output was read as UTC.
- [x] `integration`-marked test (needs `ssh-keygen`, skipped in the default suite) plus a
non-integration test that `--sign-smoke` refuses a vault backend.
### T3 — Playbook gate + ops-bridge handoff
```task
id: WARDEN-WP-0016-T03
status: done
priority: medium
state_hub_task_id: "330e01f4-4927-4280-b0e0-49d35b4416d6"
```
- [x] `wiki/playbooks/ops-bridge-tunnel-cert.md` now leads with **Step 0 — Readiness gate**
(the exact `check_tunnel_cert_readiness.py` invocation + `--sign-smoke` note); the
manual checklist remains as the human-readable backing.
- [x] Sent ops-bridge the coordination handoff (pilot `agt-state-hub-bridge`, the
readiness-gate command, and the cutover steps ops-bridge owns).
### T4 — INTENT/SCOPE alignment
```task
id: WARDEN-WP-0016-T04
status: done
priority: low
state_hub_task_id: "4726f5bb-4ffd-484f-8674-91ee5658434f"
```
- [x] SCOPE: INTENT gap row moved from "Partial — tunnels still static-key" to
"Pilot-ready — readiness gate shipped; live cutover handed to ops-bridge"; known-gaps
row updated; readiness script added to the implemented SSH-lane list.
---
## Acceptance
- `scripts/check_tunnel_cert_readiness.py` gates the pilot read-only and is tested.
- The offline contract smoke validates a real cert against the local backend.
- The playbook leads with the automated gate; ops-bridge has the handoff with exact steps.
- No secret material in any script, test, doc, or log. ops-warden's boundary is intact:
it verifies and hands off; ops-bridge executes the cutover.
---
## See also
- `wiki/playbooks/ops-bridge-tunnel-cert.md`, `wiki/CertCommandInterface.md`
- `scripts/check_principals_drift.py` (reused helpers)
- `history/2026-06-24-intent-scope-gap-analysis.md`

View file

@ -0,0 +1,120 @@
---
id: WARDEN-WP-0017
type: workplan
title: "Access front-door discoverability — stop reading as SSH-only"
domain: infotech
repo: ops-warden
status: finished
owner: claude
topic_slug: custodian
planning_priority: high
planning_order: 17
created: "2026-06-27"
updated: "2026-06-27"
state_hub_workstream_id: "cf8b392e-7624-4585-8935-a85e29202935"
---
# WARDEN-WP-0017 — Access front-door discoverability
**Problem:** WP-0014 made ops-warden the operator **access front door** — for
`exec_capable` lanes (OpenBao reads, key-cape login) `warden access <need> --fetch/--exec`
proxies the fetch **as the caller** and streams the value to them (ops-warden holds
nothing). But every *discovery* surface still tells the pre-WP-0014 story, so agents
(e.g. whynot-design needing `NPM_AUTH_TOKEN`) conclude "ops-warden only issues SSH certs
and replies with a pointer, not a token" and never find the proxy.
**Fix:** propagate the WP-0014 conduit charter to the surfaces agents actually read. This
is a *messaging/discoverability* change — **no** change to the security model: the conduit
stays a conduit (no custody, no standing broker; the responsibility-map boundary holds).
**Out of scope:** ops-warden holding/brokering token values (that would override the
WP-0014 charter); shipping the concrete OpenBao npm KV path (railiance-platform infra —
tracked separately); any new fetch capability (the proxy already exists).
**Depends on:** WP-0014 (the proxy lane being described).
---
## Surfaces that mislead today
| Surface | Says now | Should say |
| --- | --- | --- |
| `warden route` table `warden` column | binary `issue` / `route` | `issue` / **`assist`** (exec_capable) / `route` |
| `warden route` `--json` | no proxyability field | add `warden_role` + `exec_capable` |
| `warden access` closing line | "warden advises, the owner vends" | for exec_capable: "ops-warden can fetch this for you as the caller…" |
| `.claude/rules/credential-routing.md` | "issues SSH certs **only**… reply is a pointer, not a key" | issues SSH certs **and** is the access front door; exec_capable lanes proxy as you |
| Federated capability registry | only "SSH certificate issuance" | also "Operator access front door / caller-identity fetch proxy" |
| SCOPE one-liner + capability block | SSH + routing + posture | add the access-assist/proxy front door |
---
## Tasks
### T1 — CLI discoverability: route role + access framing
```task
id: WARDEN-WP-0017-T01
status: done
priority: high
state_hub_task_id: "6e98df42-b5b4-49f8-a444-3c6346c8abd7"
```
- [x] `warden route` table: three-valued `warden` column — `issue` / `assist`
(exec_capable) / `route`. `_entry_summary` JSON gains `warden_role` + `exec_capable`;
`route show` JSON `next_action` surfaces the proxy for exec_capable lanes.
- [x] `warden access` closing line: for `exec_capable` lanes leads with "ops-warden can
fetch this for you as the caller (`--fetch`/`--exec`); runs the owner's tool with
your identity, value never held/cached/logged." Non-exec lanes keep "advises, owner
vends." `_access_json` `next_action` mirrors it.
- [x] Tests in `tests/test_routing.py` (warden_role issue/assist) and `tests/test_access.py`
(front-door framing for exec lane, owner-vends for route-only lane). 210 pass.
### T2 — Agent rule + SCOPE reframe
```task
id: WARDEN-WP-0017-T02
status: done
priority: high
state_hub_task_id: "6e2a7067-1afc-4f38-8d99-4d5c36a4661c"
```
- [x] `.claude/rules/credential-routing.md`: reframed the lead ("issues SSH certs **and**
is the operator access front door…") and the quick routing table (`ops-warden role`
column: Issue / Assist / Route). Kept the true anti-pattern: don't POST a State Hub
message for a secret *value* — it comes from the CLI front door run as you.
- [x] SCOPE one-liner reframed to "steward **and front door**"; added a second `capability`
block "Operator access front door (caller-identity fetch proxy)".
### T3 — Federated capability registration
```task
id: WARDEN-WP-0017-T03
status: done
priority: medium
state_hub_task_id: "7199625b-e78e-4495-8ca0-076100ae9f08"
```
- [x] Registered the State Hub capability "Operator access front door (caller-identity
fetch proxy)" (id `708e46f6`, repo ops-warden) — the hub had **no** ops-warden
security capability before, so the front door was undiscoverable cross-domain.
- [x] Sent whynot-design (msg `83a3bb2e`) the corrected path: `warden access "npm auth
token" --fetch/--exec`, the CLI refresh, the OpenBao-auth prereq, and the
railiance-platform path caveat.
---
## Acceptance
- An agent doing the first-line `warden route find` / `--json` lookup can see ops-warden
*assists* (proxies) the OpenBao lane, not merely points.
- The credential-routing rule and federated capability registry describe the access
front door; none of them say "SSH certificates only".
- The conduit boundary is unchanged and explicit: ops-warden fetches *as the caller* and
holds nothing — no custody, no broker.
---
## See also
- `WARDEN-WP-0014` (the proxy lane), `wiki/OperatorAccessAssist.md`
- `.claude/rules/credential-routing.md`, `registry/routing/catalog.yaml`

View file

@ -0,0 +1,103 @@
---
id: WARDEN-WP-0018
type: workplan
title: "Activate whynot-design npm publish lane + resolvable readiness flag"
domain: infotech
repo: ops-warden
status: finished
owner: claude
topic_slug: custodian
planning_priority: high
planning_order: 18
created: "2026-06-29"
updated: "2026-06-29"
state_hub_workstream_id: "1256aca2-5979-4d21-818e-0de42c5d811b"
---
# WARDEN-WP-0018 — whynot-design npm lane activation + `resolvable` flag
**Trigger:** railiance-platform completed provisioning the whynot-design npm publish lane
(CCR-2026-0001, commit 8f617fc): `status=active`, `access_frontdoor.readiness=ready`,
`resolvable=true`, positive fetch passed + negative (non-whynot) login denied. They asked
ops-warden to activate the dedicated catalog selector and notify whynot-design. This is the
first concrete `warden access --fetch`-resolvable non-SSH lane — the end-to-end proof of the
WP-0014 conduit + WP-0017 discoverability work.
**whynot-design's spec** (msg 2687dc31) drove the shape: zero-placeholder command keyed by a
stable id, owner-confirmed concrete path/field/role, a machine-readable readiness flag, and a
publish-vs-read scope split.
**Boundary unchanged:** ops-warden holds no token; the lane proxies the read as the caller.
---
## Tasks
### T1 — Concrete catalog entry + playbook
```task
id: WARDEN-WP-0018-T01
status: done
priority: high
state_hub_task_id: "189d0883-22b9-42dc-bda0-89460509a87d"
```
- [x] Added `whynot-design-npm-publish` to `registry/routing/catalog.yaml` (`status: active`,
`exec_capable`, `lane: secret`) with the **owner-confirmed, zero-placeholder** handoff:
path `platform/workloads/coulomb/whynot-design/npm-publish` (the superseded
`whynot-design/whynot-design/…` form is **not** used), field `NPM_AUTH_TOKEN`, OIDC
`bao login -method=oidc -path=netkingdom role=whynot-design-workload-kv-read`, policy
`workload-kv-read-whynot-design-npm-publish`, flex-auth `secret.read:whynot-design`.
- [x] `wiki/playbooks/whynot-design-npm-publish.md` — worker checklist, scopes, operator
go-ahead note (publish is immutable + outward-facing). Catalog `wiki_ref` points to it.
- [x] Passes the `_assert_no_secret_material` guard (templates/identifiers only, no value).
### T2 — `resolvable` readiness flag + stable-id resolution
```task
id: WARDEN-WP-0018-T02
status: done
priority: high
state_hub_task_id: "b5dc1013-5334-43ff-afd6-1f99d521358f"
```
- [x] `RouteEntry.resolvable` — true when a lane is active, exec_capable, and its fetch
command/path carry **no** unresolved `<…>` placeholder. Surfaced in the route/access
`--json` (`_entry_summary`). Generic `openbao-api-key` and the `<domain>` login lane
report `false`; `whynot-design-npm-publish` reports `true`.
- [x] `Catalog.find` now resolves an **exact catalog-id** match first, so
`warden access whynot-design-npm-publish …` is deterministic regardless of keyword
collisions (whynot-design's "stable keyed command").
- [x] Tests: `tests/test_routing.py` (concrete+resolvable lane, template lanes not
resolvable, exact-id wins); fixed a `test_access` no-match query that incidentally
substring-collided (`no` ⊂ `whynot`). 213 pass, lint clean.
### T3 — Close the loop
```task
id: WARDEN-WP-0018-T03
status: done
priority: medium
state_hub_task_id: "95b00ef8-477a-4f0d-bd71-6154fba401f5"
```
- [x] Notified whynot-design (reply 744977ae) with the zero-placeholder command
`warden access whynot-design-npm-publish --exec -- npm publish`, the `resolvable` gate,
the coulomb-tenant path correction, and the operator-go-ahead reminder.
- [x] Confirmed activation to railiance-platform (reply f76d3a9e). Sibling lanes
(`issue-core-ingestion-api-key`, `openrouter-llm-connect`) stay `draft` per their
deferral, pending CCR-2026-0002/0003 provisioning.
---
## Acceptance
- `warden access whynot-design-npm-publish` resolves to a concrete, owner-confirmed,
zero-placeholder lane; `--json` reports `resolvable: true`.
- Template/generic lanes report `resolvable: false`; exact-id lookup is deterministic.
- No secret value in catalog, playbook, tests, or logs; ops-warden holds nothing.
## See also
- `WARDEN-WP-0014` (proxy lane), `WARDEN-WP-0017` (discoverability)
- railiance-platform CCR-2026-0001, `docs/workload-kv-access-lanes.md`

View file

@ -0,0 +1,89 @@
---
id: WARDEN-WP-0019
type: workplan
title: "Route secret-exec lanes to secrets-engine (route-primary, proxy fallback)"
domain: infotech
repo: ops-warden
status: finished
owner: claude
topic_slug: custodian
planning_priority: high
planning_order: 19
created: "2026-06-29"
updated: "2026-06-29"
state_hub_workstream_id: "5e49abb6-497f-4640-a484-2da5f39a7c4e"
---
# WARDEN-WP-0019 — Route secret-exec lanes to secrets-engine
**Trigger:** secrets-engine (SECRETS-WP-0003, msg 765a03f0) shipped a native secret-exec
front door — `secrets-engine route <id> --json` and `secrets-engine exec --catalog <id> --
<cmd>` with canonical decision ids — and asked ops-warden to **route to it**. This is the
owner-native execution lane that ops-warden's `warden access --exec` proxy was filling as a
stopgap (WP-0014). whynot-design already published `@whynot/design@0.4.0` through the proxy
on this same lane, so both paths resolve today.
**Decision (Bernd, 2026-06-29): route-primary, proxy-fallback.** For lanes secrets-engine
owns, ops-warden surfaces `secrets-engine exec/route` as the **primary** path and keeps its
own `warden access --exec` as a documented **transparent fallback**. ops-warden stays the
discovery front door; secrets-engine is the exec owner. Boundary unchanged: ops-warden holds
or stores no token on either path.
**Out of scope:** ops-warden invoking `secrets-engine exec` itself (it routes/points, the
caller runs it); changing the proxy's security model; the production policy-gate flip.
---
## Tasks
### T1 — Catalog + CLI: surface the owner-native exec front door
```task
id: WARDEN-WP-0019-T01
status: done
priority: high
state_hub_task_id: "ea153605-7a14-4db7-8bce-d780ea143f8a"
```
- [x] `RouteEntry` gains `exec_owner` / `exec_command` / `pointer_command` (pointers only,
screened by `_assert_no_secret_material`) and a `has_native_exec` property.
- [x] `whynot-design-npm-publish` entry: `exec_owner: secrets-engine`,
`exec_command: secrets-engine exec --catalog whynot-design-npm-publish -- <cmd>`,
`pointer_command: secrets-engine route whynot-design-npm-publish --json`. Keep the
existing `fetch_command`/`exec_capable` (the proxy fallback).
- [x] `warden access`: when `exec_owner` is set, render the secrets-engine exec as the
**primary** line and the `warden access --exec` proxy as the **fallback**; JSON gains
`exec_owner`/`exec_command`/`pointer_command`. `route find/show` JSON too.
- [x] Tests in `tests/test_routing.py` / `tests/test_access.py`.
### T2 — Agent rule, SCOPE, playbook
```task
id: WARDEN-WP-0019-T02
status: done
priority: medium
state_hub_task_id: "96059b8a-8938-4763-b3d0-cc5a0eb2465c"
```
- [x] `.claude/rules/credential-routing.md`: add secrets-engine as the secret-exec owner;
for OpenBao-backed secret lanes the route is "secrets-engine `exec` (primary),
ops-warden `warden access --exec` (transparent fallback)".
- [x] SCOPE: add secrets-engine to Related Repos + the routing model; note the
whynot-design lane is **production-exercised** (real 0.4.0 publish), not just resolvable.
- [x] `wiki/playbooks/whynot-design-npm-publish.md`: lead with the secrets-engine exec
command; fix the fallback one-liner per whynot-design's field notes
(`--field NPM_AUTH_TOKEN`, and `--no-policy` while `policy.enabled=false`).
---
## Acceptance
- `warden access whynot-design-npm-publish` shows the secrets-engine exec as primary and the
warden proxy as fallback; `--json` carries `exec_owner`/`exec_command`.
- The credential-routing rule names secrets-engine as the secret-exec owner.
- No secret material anywhere; ops-warden holds no token on either path.
## See also
- secrets-engine SECRETS-WP-0003, decision e6381a56, `docs/whynot-design-real-publish-closeout.md`
- `WARDEN-WP-0014` (proxy), `WARDEN-WP-0017` (discoverability), `WARDEN-WP-0018` (lane activation)

View file

@ -0,0 +1,176 @@
---
id: WARDEN-WP-0020
type: workplan
title: "ops-warden worker — autonomous coordination via llm-connect"
domain: infotech
repo: ops-warden
status: finished
owner: claude
topic_slug: custodian
planning_priority: high
planning_order: 20
created: "2026-06-29"
updated: "2026-06-29"
state_hub_workstream_id: "c906ba1d-f991-4fb0-b113-59432ddf87c0"
---
# WARDEN-WP-0020 — ops-warden worker (`warden worker`)
**Problem:** ops-warden's coordination lane (State Hub inbox `to_agent=ops-warden`) is
handled only when a human spins up an ops-warden session and relays instructions. That
doesn't scale — Bernd is hand-relaying between flex-auth ↔ secrets-engine ↔ ops-warden
across sessions.
**Goal:** a `warden worker` CLI that pulls ops-warden's unread coordination requests and,
using **llm-connect** for inference, drives each to an ops-warden action (answer a routing
question, draft+send a reply, mark read, propose/commit a catalog diff, or escalate) — so
the inbox is handled without a human starting a session.
**Decisions (Bernd, 2026-06-29):** **full-auto in-scope** (worker executes any in-scope
action; escalates only secrets/prod/out-of-scope) and **scheduled/unattended** (cron or
activity-core). Because there is no human in the loop for in-scope actions, the guardrails
are load-bearing and the rollout is staged: **dry-run → manual → scheduled**.
**Build vs reuse:** inference = llm-connect (`/execute`); trigger = cron or activity-core
(reuse the durable task factory, don't reinvent scheduling). Worker logic lives in warden.
## Guardrails (non-negotiable — full-auto rests on these)
1. **Fixed charter, non-overridable.** The boundary (issue SSH; route everything else;
conduit-not-broker; never hold/print a secret value) is a fixed system policy. Message
content is **untrusted data**, never instructions that can relax it (prompt-injection
containment).
2. **Action allowlist.** Every action is validated against an allowlist before execution;
off-list → escalate. No secret handling, no prod-config writes, no irreversible/outward
actions without an explicit human ack.
3. **No-secret invariant.** Refuse any task requiring a secret value in hand or in a prompt.
4. **Full audit + dry-run.** Every action emits a progress event; `--dry-run` shows the
plan without executing. Scheduled mode only after a clean dry-run shakedown.
## Hard dependency
llm-connect must be operational — it needs its provider key (`OPENROUTER_API_KEY`,
CCR-2026-0003, currently deferred by railiance-platform/secrets-engine). The worker is
built against llm-connect's contract; it cannot run the brain until that lands.
---
## Tasks
### T1 — Worker scaffold (llm-connect-independent, safe)
```task
id: WARDEN-WP-0020-T01
status: done
priority: high
state_hub_task_id: "979c2d9b-0803-442f-aa2e-acb02bac07e9"
```
- [x] `src/warden/worker.py`: State Hub inbox client (`HubClient.unread`), a `Brain`
protocol, a deterministic `RuleBrain` default (answers clear routing questions;
escalates the rest), the `PlannedAction`/`WorkerPlan` model, the guardrail allowlist +
`validate_action` (enforced brain-agnostically in `build_plans`), and a `render_plans`
dry-run renderer (plan only, no execution).
- [x] `warden worker run [--once] [--dry-run]` CLI; `--dry-run` is the default and
`--execute` is refused (exit 2) until the guarded executor lands (T3).
- [x] `tests/test_worker.py` (RuleBrain routing/secret/prod/unknown, guardrail downgrades a
reckless brain on secret/prod, off-allowlist rejection, render, CLI). 18 cases.
- [x] Live dry-run against the real hub verified — read the inbox and produced a guardrailed
plan (it surfaced secrets-engine's OIDC-role reply, demonstrating the value).
### T2 — llm-connect brain
```task
id: WARDEN-WP-0020-T02
status: done
priority: high
state_hub_task_id: "52d281b2-7d48-44f5-b77e-80e3ed500b5f"
```
- [x] llm-connect brought operational (operator set OPENROUTER_API_KEY k8s secret + restart).
Contract discovered empirically from the running service: `POST /execute {"prompt":...}`
→ `{"content": "<text>", ...}` (no OpenAPI; custom JSON API). End-to-end verified (pong).
- [x] `LlmConnectBrain` (src/warden/worker.py): embeds the fixed charter + the message as
untrusted data into the prompt, calls `/execute`, parses a JSON action plan
(`_extract_json` tolerates fences/prose), and defensively escalates on malformed/empty/
transport-error. Configurable `LLM_CONNECT_URL`. The guardrail pass still enforces the
allowlist + no-secret invariant on whatever the model returns.
- [x] `warden worker run --brain rule|llm` selector (dry-run default). Tests:
`tests/test_worker.py` (extract_json, parse, escalate-on-flag/malformed/transport,
guardrail-catches-unsafe-LLM-action). **Live verified** against the real inbox: the LLM
brain produced a sensible reply+mark_read for the secrets-engine message and correctly
escalated the llm-connect secret-custody request. 236 tests, lint clean.
### T3 — Action dispatch + guardrails (full-auto in-scope)
```task
id: WARDEN-WP-0020-T03
status: done
priority: high
state_hub_task_id: "3a71965e-42d5-4258-9761-aced804c88e7"
```
- [x] `HubClient` gained writes (`mark_read`, `send_reply`, `add_progress`); `execute_plan`
/ `execute_plans` run the **safe, allowlisted** actions — route_answer (reply with the
computed answer + auto mark-read), reply (with an LLM-drafted body), progress_note,
mark_read. Escalated plans and non-auto-executable kinds are left for a human.
- [x] **Deliberate guardrail:** `propose_catalog_diff` (and any code/routing change) is NOT
auto-executed even under full-auto — a bad catalog commit could misroute credentials,
so it goes to human review (recoverability over convenience). AUTO_EXECUTABLE is the
messaging/hub tier only. No secret value is ever read, sent, or logged.
- [x] `warden worker run --execute` runs the guarded executor (dry-run still the default);
per-message audit summary. Tests in `tests/test_worker.py` (route_answer reply+mark,
reply-with/without-body, escalated skip, catalog-diff left-for-human, progress_note,
failure-without-crash). 243 pass, lint clean.
- [x] **Conservative tier is now the `--execute` default (Bernd's Option A, 2026-06-30):**
`run_conservative` triages NEW messages into a reviewed digest (`worker-digest.md`)
with drafted replies, posts ONE progress note, tracks seen ids (schedule-safe dedup),
and sends **nothing** to other agents / marks nothing read. `--full-auto` opts into the
auto-send path. Live-verified with the LLM brain: produced a high-quality draft reply
to secrets-engine and flagged the llm-connect request as NEEDS YOU. 244 tests.
Rationale: the guardrails prevent *security* harm but not LLM *content* errors, so replies
stay drafts-for-approval until quality is proven — matches the build-stage/recoverability
posture. Conservative mode is safe to schedule (T4).
### T4 — Scheduled trigger
```task
id: WARDEN-WP-0020-T04
status: done
priority: medium
state_hub_task_id: "7f77ea6d-c281-42c5-ad25-2a0bb9fd68de"
```
- [x] `scripts/worker-tick.sh` — scheduled tick for the conservative worker. `flock`
concurrency guard (no overlapping runs); brings up a short-lived kubectl port-forward
to llm-connect (or honors `LLM_CONNECT_URL`, or falls back to the rule brain offline).
Ships **disabled**; the header documents the cron entry to enable it (every 15 min).
Dry-shakedown done (the conservative live run + the rule-brain tick both verified).
Schedules the **conservative** tier only — never the auto-send path.
### T5 — Docs / SCOPE / INTENT
```task
id: WARDEN-WP-0020-T05
status: done
priority: medium
state_hub_task_id: "6e7ae317-7f8b-468a-bb5c-b08093ed43a0"
```
- [x] SCOPE: recorded the coordination worker (`warden worker`) as an implemented
capability — conservative triage default, full-auto opt-in, llm-connect brain, the
four guardrails, schedulable tick. The guardrails + the conservative-by-default
posture are documented as the worker's security-model statement (here + in the
build-stage decision 813899f9).
---
## Acceptance
- `warden worker run --dry-run` reads the real inbox and prints a guardrailed plan.
- Full-auto execution runs only in-scope, allowlisted actions; secrets/prod/out-of-scope
escalate; every action is audited. No secret value ever enters a prompt, log, or commit.
- Scheduled mode is enabled only after a dry-run shakedown.
## See also
- llm-connect (inference), activity-core (durable trigger), kaizen-agentic (personas)
- `.claude/rules/credential-routing.md` (the boundary the worker enforces)

View file

@ -0,0 +1,142 @@
---
id: WARDEN-WP-0021
type: workplan
title: "Enable the scheduled worker tick — conservative inbox triage, unattended"
domain: infotech
repo: ops-warden
status: finished
owner: claude
topic_slug: custodian
planning_priority: high
planning_order: 21
created: "2026-06-30"
updated: "2026-06-30"
state_hub_workstream_id: "8c487014-b630-4016-a4f0-31b971a473d2"
---
# WARDEN-WP-0021 — Enable the scheduled worker tick
**Goal:** turn the WP-0020 conservative worker from *built-but-disabled* into a reliable,
unattended schedule — so ops-warden's State Hub inbox is auto-triaged into a digest of
**drafted replies** the operator reviews and approves, without anyone starting a session.
This is the payoff of WP-0020: it ends the cross-session relay toil.
**Posture (unchanged):** schedule the **conservative** tier only — triage + draft, never
auto-send (Option A / build-stage decision `813899f9`). The four guardrails hold. Easy
kill switch is a requirement, not an afterthought (recoverability).
**What "enabled" means here:** (1) the tick runs on a schedule and survives the failure
modes (hub/llm-connect down → graceful degrade), (2) the operator actually *sees* new
drafts, (3) the operator can *act* on a draft with one command, (4) it's trivial to stop.
**Out of scope:** the full-auto (auto-send) path; flipping `policy.enabled`; moving the
worker off the workstation.
**Depends on / relates to:** WP-0020 (the worker + `scripts/worker-tick.sh`); the State
Hub migration to railiance01 (`cust-wp-0011`/`0038`) may change `WARDEN_HUB_URL` later —
the tick already honors that env var.
---
## Decisions to settle (first)
- **Scheduler:** `systemd --user` timer (recommended — clean logs via `journalctl`,
`systemctl --user status`, built-in scheduling) vs. plain cron (simplest) vs.
activity-core (ecosystem-native durable trigger; heavier for build stage). Recommend the
systemd user timer; cron documented as the one-liner fallback.
- **Cadence:** every 15 min (default) — adjustable.
- **llm-connect reachability:** per-tick short-lived port-forward (current behaviour) with
rule-brain fallback, vs. a persistent forward. Recommend keeping the per-tick forward +
fallback for build stage (no standing process).
---
## Tasks
### T1 — Scheduler install + enablement + kill switch
```task
id: WARDEN-WP-0021-T01
status: done
priority: high
state_hub_task_id: "10451fe6-7fab-4ae0-8494-e6cfdfbcf8cf"
```
- [ ] `systemd --user` timer + service units (`ops-warden-worker.{service,timer}`) that run
`scripts/worker-tick.sh` on the chosen cadence, with `WARDEN_HUB_URL` / `WORKER_BRAIN`
from an env file. Install script + documented cron fallback one-liner.
- [ ] Concurrency is already guarded by the tick's `flock`; verify under the timer.
- [ ] **Kill switch:** `systemctl --user disable --now ops-warden-worker.timer` (and the
env-file `WORKER_ENABLED=0` short-circuit) — one command to stop, documented.
### T2 — Scheduled-run robustness (graceful degradation)
```task
id: WARDEN-WP-0021-T02
status: done
priority: high
state_hub_task_id: "1f35f816-1af5-46ff-b48c-1715f3ae5784"
```
- [ ] Harden `worker-tick.sh` for unattended runs: bounded timeouts, hub-unreachable →
clean skip + log (no crash loop), llm-connect-unreachable → rule-brain fallback
(already present; verify), non-zero exit only on real faults.
- [ ] End-to-end verify a real timer-fired tick: new message → digest + progress note;
no new message → no-op; hub down → graceful skip.
### T3 — Operator visibility (see new drafts)
```task
id: WARDEN-WP-0021-T03
status: done
priority: medium
state_hub_task_id: "3c7f6423-8db0-4bc6-b67d-078d9d929c6d"
```
- [ ] Surface new drafts beyond the file: desktop `notify-send` on new digest (when a
display is present) and/or keep the hub progress note as the durable signal.
- [ ] `warden worker status` — last run time, pending-draft count, digest path, timer state.
### T4 — Review→send loop (`warden worker approve`)
```task
id: WARDEN-WP-0021-T04
status: done
priority: high
state_hub_task_id: "dabc9fc0-abb1-4e9d-b87e-5f0c5950693c"
```
- [ ] Persist structured drafts during the tick (`state_dir/worker-drafts.json`:
message_id → to_agent, subject, drafted body, thread_id — no secret material).
- [ ] `warden worker approve <message_id> [--edit]` — send the reviewed draft as the
caller's reply + mark read; `warden worker drafts` to list pending. This is what makes
the scheduled digest *actionable* in one command instead of hand-composing.
### T5 — Runbook + SCOPE
```task
id: WARDEN-WP-0021-T05
status: done
priority: medium
state_hub_task_id: "9915da96-1b33-4d0f-b752-408ea8d43333"
```
- [ ] `wiki/playbooks/scheduled-worker.md` — enable/disable, cadence, the approve workflow,
failure modes, and the build-stage posture (conservative only). SCOPE note.
---
## Acceptance
- A `systemd --user` timer (or cron) runs the conservative tick unattended; one command
disables it.
- A timer-fired tick triages new messages into a digest + progress note and degrades
gracefully when the hub or llm-connect is down.
- The operator is notified of new drafts and can send a reviewed draft with
`warden worker approve <id>`.
- Still conservative: nothing is auto-sent; no secret value is read, sent, or logged.
## See also
- `WARDEN-WP-0020` (the worker + `scripts/worker-tick.sh`), build-stage decision `813899f9`
- `cust-wp-0011`/`cust-wp-0038` (State Hub → railiance01; future `WARDEN_HUB_URL`)

View file

@ -0,0 +1,119 @@
---
id: WARDEN-WP-0022
type: workplan
title: "Audit trail + `warden activity` — one place to see what ops-warden did"
domain: infotech
repo: ops-warden
status: finished
owner: claude
topic_slug: custodian
planning_priority: high
planning_order: 22
created: "2026-07-01"
updated: "2026-07-01"
state_hub_workstream_id: "fc8afa28-68a7-4250-a19e-9754829f0cd5"
---
# WARDEN-WP-0022 — Audit trail + `warden activity`
**Problem:** ops-warden's actions are recorded in scattered places — `signatures.log`
`access-audit.log`, the systemd journal (worker ticks), and
State Hub progress notes (the narrative). There is **no single, structured audit trail**
and no one command to answer *"what did ops-warden do in the last N days?"*. For a security
steward, a coherent, metadata-only audit record is table stakes.
**Goal:** a unified, append-only audit log that captures **every** ops-warden action with a
common shape (never a secret value), and a single `warden activity` command to read it.
**Non-negotiable (this is a security tool's audit):** the audit record holds **metadata
only** — actor/subject, action, target/path id, decision id, TTL, outcome, timestamp — and
**never** a token, key, cert body, or other secret. A secret-material guard rejects any
event field that looks like a value (mirrors the catalog `_assert_no_secret_material`).
**Posture:** read/append-only, in-boundary (local logs + optional hub read). Tamper-evident
hash-chaining is noted as an optional hardening for when the ecosystem reaches testing.
**Relates to:** WP-0014 (`access-audit.log`), the SSH lane (`signatures.log`), WP-0020/0021
(the worker). Linger is now enabled (worker survives logout); full logged-out value also
needs the State Hub + tunnels to be login-independent (State Hub → railiance01,
`cust-wp-0011`).
---
## Tasks
### T1 — Unified audit event log
```task
id: WARDEN-WP-0022-T01
status: done
priority: high
state_hub_task_id: "7f8f768a-4c62-4096-bad8-912cea0f35a7"
```
- [x] `src/warden/audit.py`: append-only JSONL at `state_dir/audit.jsonl`. Common event
schema — `ts`, `kind` (`sign`|`access`|`worker`), `action`, `subject`, `target`,
`decision_id`, `outcome`, `source`. `record_event(**meta)` with a secret-material
guard (reject token prefixes / high-entropy runs) so no value can ever land here.
`read_events(*, since, kinds)` for the reader.
- [x] Log rotation / bound (size or age) so it stays manageable.
### T2 — Instrument the actions
```task
id: WARDEN-WP-0022-T02
status: done
priority: high
state_hub_task_id: "e7ae4037-ca79-4557-81f0-bfb8478ff647"
```
- [x] Emit an audit event from each ops-warden action: `warden sign` (cert issued —
actor, type, ttl, backend, policy_decision_id), `warden access --fetch/--exec`
(proxy — need id, owner, decision id), and the worker (`approve` → reply sent to X;
tick → triage summary N/drafted/escalated). Fold the existing `signatures.log` /
`access-audit.log` in as sources (keep back-compat; don't drop a record).
- [x] Assert no secret value reaches the audit in any path (tests).
### T3 — `warden activity` command
```task
id: WARDEN-WP-0022-T03
status: done
priority: high
state_hub_task_id: "4439bdd8-1461-47df-8b0b-048df7384a68"
```
- [x] `warden activity [--days N] [--kind sign|access|worker] [--json] [--hub]` — a single
chronological view merging the audit log (and, for back-compat, `signatures.log` /
`access-audit.log`); `--hub` also pulls recent ops-warden State Hub progress notes for
the narrative. Human table by default; stable `--json` for agents.
### T4 — Tests, runbook, SCOPE
```task
id: WARDEN-WP-0022-T04
status: done
priority: medium
state_hub_task_id: "bdfb8703-7a79-43e7-913b-19d61722f164"
```
- [x] Tests: audit append/read/rotation, the secret-material guard rejects values, the
instrumented actions emit events, `warden activity` filtering + `--json` shape.
- [x] `wiki/AuditTrail.md` (what's recorded, the no-secret guarantee, how to query, the
linger + login-independence note). SCOPE entry.
---
## Acceptance
- Every ops-warden action (sign, access proxy, worker send/tick) appends a metadata-only
audit event; the secret-material guard is proven to reject values.
- `warden activity --days 3` answers "what did ops-warden do" in one command; `--json`
gives agents a stable shape.
- No secret value appears in the audit log, ever.
## See also
- `WARDEN-WP-0014` (`access-audit.log`), `WARDEN-WP-0020`/`0021` (the worker)
- `wiki/OperatorAccessAssist.md` (the metadata-only audit principle)
- `wiki/AuditTrail.md`

View file

@ -0,0 +1,222 @@
---
id: WARDEN-WP-0023
type: workplan
title: "INTENT–SCOPE Alignment Closeout"
domain: infotech
repo: ops-warden
status: finished
owner: codex
topic_slug: custodian
planning_priority: high
planning_order: 23
created: "2026-07-01"
updated: "2026-07-01"
depends_on_workplans:
- WARDEN-WP-0022
state_hub_workstream_id: "7bad1ec4-a7c2-4980-b8f9-49a7f5408574"
---
# WARDEN-WP-0023 — INTENT–SCOPE Alignment Closeout
## Goal
Close the July 2026 INTENT↔SCOPE gaps that ops-warden can address directly: sync
aspirational docs with shipped capabilities, coordinate the remaining production
integration blockers (flex-auth flip, ops-bridge cutover), improve daily operator
UX for broker-backed signing, and establish a repeatable catalog promotion cadence.
Audit implementation stays in **WARDEN-WP-0022**; this workplan sequences and
surrounds it.
**Assessment:** `history/2026-07-01-intent-scope-gap-analysis.md`
## Boundary
- ops-warden does **not** deploy flex-auth, flip ops-bridge tunnels, or implement
the credential broker — it documents, coordinates, and routes.
- Production cutover evidence is captured here; execution remains with owning repos.
---
## Tasks
### T01 — Persist gap analysis
```task
id: WARDEN-WP-0023-T01
status: done
priority: high
state_hub_task_id: "52485c90-87fe-40b1-9db5-a51ebb957dd5"
```
Write and link `history/2026-07-01-intent-scope-gap-analysis.md` with success
criteria matrix, mission pillars, prioritized gaps, and workplan recommendation.
Acceptance:
- History file exists and is referenced from SCOPE and this workplan.
- State Hub progress note logged for the assessment.
**2026-07-01:** Assessment written at
`history/2026-07-01-intent-scope-gap-analysis.md`.
### T02 — Refresh INTENT.md
```task
id: WARDEN-WP-0023-T02
status: done
priority: high
state_hub_task_id: "9a9b3631-8948-45af-ace1-c19ee74ace4d"
```
Update `INTENT.md` so the aspirational doc reflects shipped reality without
becoming a second SCOPE:
- Mission pillar #2: assist layer (`warden access`) and owner-native exec routing
(secrets-engine, railiance-platform credential broker).
- NetKingdom literacy table: add secrets-engine and credential broker rows.
- Credential flow diagram: broker vs secrets-engine vs OpenBao proxy vs SSH issue.
- flex-auth: caller-side policy gate shipped; production flip external (FLEX-WP-0007).
- Workload posture stewardship and coordination worker as steward capabilities.
- Evolution notes pointer to July gap analysis.
Acceptance:
- INTENT still describes direction, not implementation inventory.
- No contradiction with SCOPE 2026-07-01 boundary (ops-warden does not mint tokens).
**2026-07-01:** INTENT.md updated.
### T03 — Production integration coordination pack
```task
id: WARDEN-WP-0023-T03
status: done
priority: high
state_hub_task_id: "26f23798-494b-45fc-baa8-af27bdffa038"
```
Prepare operator/coordination artifacts for the two P1 external gaps:
1. **flex-auth production flip** — checklist in `wiki/PolicyGatedSigning.md` or a
short playbook section: prerequisites, `policy.enabled: true` steps, rollback,
joint smoke with `credential-exec-ops-warden-smoke`, FLEX-WP-0007 cross-link.
2. **ops-bridge live cutover** — evidence template (non-secret): tunnel id, readiness
gate output, first warden-signed connection timestamp, pointer to
`wiki/playbooks/ops-bridge-tunnel-cert.md`.
Optionally post State Hub coordination messages to `flex-auth` and `ops-bridge`
agents with pointers only (no secrets).
Acceptance:
- A human operator can run the flip/cutover checklists without re-deriving steps.
- Evidence fields are defined; completion is recorded via State Hub progress when done.
**2026-07-01:** Rollback section added to `wiki/PolicyGatedSigning.md`; live cutover
evidence template added to `wiki/playbooks/ops-bridge-tunnel-cert.md`.
### T04 — `warden sign` broker hint when `VAULT_TOKEN` unset
```task
id: WARDEN-WP-0023-T04
status: done
priority: medium
state_hub_task_id: "85e324f9-273d-4740-a202-9c4e8fb122ae"
```
When `backend: vault` and `VAULT_TOKEN` (or configured `token_env`) is missing,
emit a structured hint pointing at `ops-warden-warden-sign-token` and the
`railiance-platform` `credential exec` command — not a generic error only.
Acceptance:
- Unit test covers the hint text (catalog id + exec shape, no secret placeholders).
- Manual `export VAULT_TOKEN` remains documented as fallback in playbooks.
**2026-07-01:** `src/warden/vault_hints.py` + `tests/test_vault.py`.
### T05 — Catalog draft-lane promotion checklist
```task
id: WARDEN-WP-0023-T05
status: done
priority: medium
state_hub_task_id: "82608692-2845-41e1-a498-90ed53780748"
```
Document the promotion criteria for `registry/routing/catalog.yaml` entries from
`draft` → `active` (concrete path, owner confirmation, `resolvable` or
`exec_owner` native exec, playbook with `#worker-checklist`, tests). Add to
`wiki/CredentialRouting.md` or a short `wiki/playbooks/catalog-lane-promotion.md`.
If any draft lane has owner-confirmed concrete paths during this WP, promote one
as a worked example (issue-core, OpenRouter, STS, or database — whichever is ready).
Acceptance:
- Checklist is reviewable by humans and agents.
- At least one promotion example or explicit “none ready yet” note in the workplan.
**2026-07-01:** `wiki/playbooks/catalog-lane-promotion.md` — worked example
`ops-warden-warden-sign-token`; four draft lanes explicitly not ready.
### T06 — SCOPE and workplan consistency
```task
id: WARDEN-WP-0023-T06
status: done
priority: medium
state_hub_task_id: "79ca7b9a-554e-4952-9393-a29b100f6190"
```
Fix SCOPE inconsistencies noted in the July assessment:
- “All workplans finished” → acknowledge WP-0022/0023 as active/ready.
- Latest gap analysis pointer → `history/2026-07-01-intent-scope-gap-analysis.md`.
- Link WP-0023 from Getting Oriented.
Acceptance:
- SCOPE and gap analysis cross-link correctly.
- Uncommitted SCOPE edits from 2026-07-01 broker routing are committed with this WP.
**2026-07-01:** SCOPE.md updated.
### T07 — Sequence WP-0022 audit implementation
```task
id: WARDEN-WP-0023-T07
status: done
priority: high
state_hub_task_id: "1f3b3b33-974e-49bf-be4a-9d50b702c2a4"
```
Promote `WARDEN-WP-0022` from `proposed` to `ready` (or `active` when T02–T06 allow
bandwidth). Ensure dependency is explicit; log State Hub note that WP-0022 is the
implementation vehicle for INTENT pillar 6 (observable gatekeeping).
Acceptance:
- WP-0022 frontmatter status updated.
- WP-0023 `depends_on_workplans` includes WP-0022.
- Hub consistency run syncs both workplans.
**2026-07-01:** WP-0022 implemented and both workplans marked `finished`.
---
## Exit criteria
- July gap analysis is the canonical reassessment (linked from SCOPE).
- INTENT.md no longer understates assist, posture, worker, or owner-native exec.
- Production integration checklists exist for flex-auth flip and ops-bridge cutover.
- `warden sign` surfaces the broker path when vault backend lacks a token.
- Catalog promotion cadence is documented; WP-0022 is queued for implementation.
## See also
- `history/2026-07-01-intent-scope-gap-analysis.md`
- `WARDEN-WP-0022-audit-trail-and-activity.md`
- `wiki/playbooks/ops-warden-warden-sign-token.md`
- `~/flex-auth/workplans/FLEX-WP-0007-ops-warden-policy-gate-production-deployment.md`

View file

@ -0,0 +1,269 @@
---
id: WARDEN-WP-0024
type: workplan
title: "Experiential Memory Across Worker, Agent Sessions, And OpenRouter"
domain: infotech
repo: ops-warden
status: finished
owner: codex
topic_slug: custodian
planning_priority: high
planning_order: 24
created: "2026-07-02"
updated: "2026-07-02"
state_hub_workstream_id: "5d9fafb3-f9b6-43bf-b259-5f5301daa2e9"
---
# WARDEN-WP-0024 — Experiential memory across worker, agent sessions, and OpenRouter
## Problem
`warden worker` (WARDEN-WP-0020) plans each inbox message in isolation. Coding
agent sessions (Claude Code, Codex, Grok, and future agents) invoke `warden
route` and `warden access` without continuity — every session rediscovers routing
from scratch. OpenRouter inference via llm-connect is invoked even when a
stabilized routing pattern already exists.
ops-warden needs **experiential memory** that is present in every runtime:
| Surface | Today | Target |
| --- | --- | --- |
| `warden worker` + OpenRouter | Stateless per message | Retrieve past outcomes before plan; record after execute |
| Coding agent session | Cold `warden route find` | Activate relevant memory at session start and after each warden call |
| Operator CLI | No learning loop | Same store; optional `--memory` context on advisory commands |
## Goal
Integrate phase-memory as ops-warden's shared experiential substrate so routing,
coordination, and inference improve from recorded outcomes — without ever
storing secret values or widening the guardrail allowlist.
## Non-Goals
- Hold or vend secrets (conduit-not-broker unchanged).
- Auto-commit routing catalog diffs (human review remains required).
- Own phase-memory schemas (PMEM-WP-0016 owns the profile and contract).
- Replace llm-connect or embed OpenRouter keys in ops-warden.
## Guardrails (unchanged — memory is untrusted context)
1. Retrieved memory is **context**, not instructions — fixed charter wins.
2. Guardrail allowlist enforced on every action regardless of memory content.
3. No secret values in memory writes, activation packs, or llm-connect prompts.
4. `propose_catalog_diff` stays human-reviewed even when memory is confident.
## Architecture
```mermaid
sequenceDiagram
participant Agent as Coding agent<br/>(claude/codex/grok)
participant Warden as ops-warden CLI
participant Memory as phase-memory store
participant LLM as llm-connect / OpenRouter
participant Hub as State Hub
Note over Agent,Hub: Agent session path
Agent->>Warden: warden route find "..."
Warden->>Memory: activate(session_kind=warden.agent.grok)
Memory-->>Warden: bounded routing context
Warden-->>Agent: route answer + memory digest
Warden->>Memory: record episode (metadata only)
Note over Warden,Hub: Worker tick path
Warden->>Hub: unread messages
Warden->>Memory: activate(session_kind=warden.worker)
Memory-->>Warden: stabilized + fluid context
alt stabilized match
Warden->>Warden: RuleBrain (skip LLM)
else ambiguous
Warden->>LLM: LlmConnectBrain + activated context
LLM-->>Warden: action plan
end
Warden->>Warden: guardrail pass
Warden->>Hub: execute / escalate
Warden->>Memory: record outcome
```
## T01 - Canonical memory store and discovery
```task
id: WARDEN-WP-0024-T01
status: done
priority: high
state_hub_task_id: "6305f1bc-c016-4298-adc2-a07d52b6aca5"
```
Establish the canonical phase-memory store path and discovery commands.
Acceptance:
- Default store at XDG location (e.g. `~/.local/share/warden/memory/`) with
override via `WARDEN_MEMORY_STORE`.
- `warden memory status` reports store path, schema version, episode counts by
`session_kind`, and last activation timestamp — no secret fields.
- Store initializes idempotently; missing phase-memory dependency fails with a
clear install pointer, not a traceback.
- Tests cover default path, override, and first-run initialization.
## T02 - Session recording hooks in CLI commands
```task
id: WARDEN-WP-0024-T02
status: done
priority: high
state_hub_task_id: "242a4d9a-5375-4df8-8d43-063b0491d202"
```
Record metadata-only episodes when `route`, `access`, and `sign` complete.
Acceptance:
- Each command writes a phase-memory event with `session_kind` derived from
environment: `WARDEN_AGENT_ID` (claude|codex|grok|...) when set, else
`warden.operator`.
- Recorded fields match `PMEM-WP-0016-T03` schema; secret patterns are rejected.
- Recording is opt-out via `WARDEN_MEMORY=0` for air-gapped/debug use.
- `warden activity` and phase-memory audit export remain consistent (no
duplicate secret-bearing logs).
## T03 - Memory-aware worker tick
```task
id: WARDEN-WP-0024-T03
status: done
priority: high
state_hub_task_id: "176fcae1-e4e5-481f-9a83-e9a7000fac1a"
```
Retrieve coordination memory before `Brain.plan` and record outcomes after execute.
Acceptance:
- `warden worker run` activates memory with `session_kind=warden.worker` before
planning each message.
- Activated context is passed to `LlmConnectBrain` as bounded, redacted context
— never replacing the fixed charter.
- Post-execute hook records: message id, proposed actions, guardrail outcome,
escalation reason, and route ids referenced.
- Worker tick with empty store degrades gracefully (current behavior).
## T04 - Agent session activation helper
```task
id: WARDEN-WP-0024-T04
status: done
priority: high
state_hub_task_id: "16501557-6cde-44ea-bc6f-1726cb7ec070"
```
Expose memory activation for coding agent sessions across Claude, Codex, Grok, and
future agents.
Acceptance:
- `warden memory activate [--agent <id>] [--json]` returns a bounded activation
package for session orientation.
- Agent instructions (AGENTS.md template in consuming repos) document setting
`WARDEN_AGENT_ID` and calling `warden memory activate` at session start.
- Activation works without OpenRouter (local retrieval only).
- Tests cover agent ids: `claude`, `codex`, `grok`, and an unknown future id.
## T05 - Cross-runtime continuity
```task
id: WARDEN-WP-0024-T05
status: done
priority: high
state_hub_task_id: "55ae679c-c08f-4afd-8646-9f5f3019f86e"
```
Ensure worker ticks see agent session episodes and vice versa.
Acceptance:
- Agent session routing outcomes appear in worker activation within the same
store without manual export/import.
- Worker escalation outcomes are visible to subsequent agent session activations.
- Continuity is verified by an integration test: agent session write → worker
read → agent session read round-trip.
- Episode retention follows the ops-warden profile compaction rules from
PMEM-WP-0016.
## T06 - OpenRouter efficiency layer
```task
id: WARDEN-WP-0024-T06
status: done
priority: medium
state_hub_task_id: "fc2dffcf-7184-4f3d-8653-d26dc18a9afc"
```
Skip or downgrade OpenRouter calls when stabilized memory provides a verified match.
Acceptance:
- When stabilized memory contains a verified route match for the message need,
`RuleBrain` handles the plan and llm-connect is not called.
- Ambiguous cases still use `LlmConnectBrain`; memory context is additive only.
- Efficiency metrics (llm_calls_avoided, rule_brain_resolution_rate) are
recorded for evaluation scenarios in PMEM-WP-0016.
- Dry-run mode reports whether LLM would have been skipped and why.
## T07 - Operator and agent documentation
```task
id: WARDEN-WP-0024-T07
status: done
priority: medium
state_hub_task_id: "ca2aaf23-833f-49a6-a49b-a0b659208f5f"
```
Document the memory workflow for operators and coding agents.
Acceptance:
- Wiki page covers: store location, session kinds, agent env vars, memory
activation at session start, and interaction with OpenRouter inference.
- ops-warden AGENTS.md and the state-hub agent template include memory
orientation steps for Claude, Codex, and Grok sessions.
- Credential routing doc cross-links PMEM-WP-0016 contract; anti-patterns
updated (no secret values in memory store).
- Scheduled worker playbook notes memory retention and evaluation gate checks.
## Acceptance Criteria
- phase-memory is present and useful in all three runtime surfaces (worker,
agent session, operator CLI) through one canonical store.
- OpenRouter inference cost decreases when stabilized routing memory matches.
- Cross-runtime continuity is tested and regression-gated.
- Security invariants (no-secret, allowlist, conduit-not-broker) are unchanged.
## Dependencies
- `PMEM-WP-0016` (phase-memory) — profile, event schema, activation pack,
evaluation scenarios. **Implement contract (T01–T03) before WARDEN T03–T05.**
- `WARDEN-WP-0020` — worker brain and guardrails (finished).
- `WARDEN-WP-0022` — audit trail for correlation (finished).
- llm-connect / OpenRouter — required only for T06 live efficiency measurement;
T01–T05 work offline.
## Suggested Implementation Order
1. PMEM-WP-0016 T01–T03 (profile + contract + event schema)
2. WARDEN-WP-0024 T01–T02 (store + CLI recording)
3. PMEM-WP-0016 T04 (activation pack)
4. WARDEN-WP-0024 T03–T05 (worker + agent activation + continuity)
5. PMEM-WP-0016 T05–T06 + WARDEN-WP-0024 T06–T07 (evaluation + docs)
## Closure Review
Integrated phase-memory across all ops-warden runtime surfaces:
- `warden memory status` and `warden memory activate` with canonical XDG store.
- CLI recording hooks on `route find`, `access`, and `sign` (opt-out via
`WARDEN_MEMORY=0`).
- Memory-aware worker planning with stabilized-route RuleBrain short-circuit
and LlmConnectBrain context injection.
- Cross-runtime continuity verified in tests (agent session → worker activation).
- `wiki/OpsWardenMemory.md` and AGENTS.md session orientation for Claude,
Codex, Grok, and future agents.

View file

@ -0,0 +1,118 @@
---
id: WARDEN-WP-0025
type: workplan
title: "Forgejo admin PAT OpenBao lane (CCR-2026-0006)"
domain: infotech
repo: ops-warden
status: finished
owner: grok
topic_slug: custodian
planning_priority: high
planning_order: 25
created: "2026-07-12"
updated: "2026-07-13"
state_hub_workstream_id: "70c11222-d8e7-5936-99f7-7d626a4a5deb"
---
# WARDEN-WP-0025 — Forgejo admin PAT OpenBao lane
**Trigger:** the-custodian CCR request (msg `54125f84`) — establish OpenBao custody
for the Forgejo site-admin PAT at `platform/workloads/forgejo/forgejo-admin`.
Drivers: `ACTIVITY-WP-0020` weekly package prune and railiance-apps/platform tools
still using `/tmp/forgejo-tegwick-api-token` or `FORGEJO_ADMIN_TOKEN`.
**Boundary:** ops-warden holds no PAT; workstation OIDC fetch mirrors
`railiance-backup-offsite-lane` (CCR-2026-0004). Distinct from `forgejo-mailer`
(SMTP via ESO). Phase 1: no cluster ExternalSecret.
**Depends on:** platform-operator approval of CCR-2026-0006; attended PAT mint.
---
## Tasks
### T1 — Draft CCR + policy metadata
```task
id: WARDEN-WP-0025-T01
status: done
priority: high
state_hub_task_id: "2c288bf0-b39c-5f5b-ad25-eed3da826dc3"
```
- [x] `CCR-2026-0006-forgejo-admin-api-token-lane.yaml` (`status: proposed`)
- [x] `openbao/policies/workload-kv-read-forgejo-admin.hcl`
- [x] `docs/workload-kv-access-lanes.md` section
### T2 — ops-warden catalog + playbook
```task
id: WARDEN-WP-0025-T02
status: done
priority: high
state_hub_task_id: "01b81595-f9e5-5746-b0dd-2075189cb00e"
```
- [x] Catalog entry `forgejo-admin-api-token` (`status: draft`)
- [x] `wiki/playbooks/forgejo-admin-api-token.md`
### T3 — Platform-operator approval + metadata apply
```task
id: WARDEN-WP-0025-T03
status: done
priority: high
state_hub_task_id: "279b74d7-3240-5ca0-897d-b1ddffd23c4e"
```
- [x] CCR approved by platform-operator (bernd.worsch, 2026-07-12)
- [x] `scripts/credential-change.py applier-dry-run CCR-2026-0006`
- [x] Policy `workload-kv-read-forgejo-admin` + OIDC role `forgejo-admin-workload-kv-read` applied on `bao.coulomb.social`
- [x] Non-secret `delegated_metadata_apply` evidence on CCR
### T4 — Attended PAT provision + verification
```task
id: WARDEN-WP-0025-T04
status: done
priority: high
state_hub_task_id: "4e21232c-62a1-5115-acce-edfbcfa84e48"
```
- [x] Mint PAT as Forgejo user `tegwick` (attended, 2026-07-12)
- [x] Stored at `platform/workloads/forgejo/forgejo-admin`, field `API_TOKEN`
(initial provision used field `Token`; re-stored as `API_TOKEN` 2026-07-13 to
match CCR/catalog/playbook `fetch_command`)
- [x] Positive fetch verified 2026-07-13 — `bao kv get -field=API_TOKEN …` returns
non-empty; PAT valid against `forgejo.coulomb.social` (`/api/v1/user` →
`login=tegwick`, `is_admin=true`)
- [x] Negative default-policy denial recorded on CCR
- [x] Promoted catalog `forgejo-admin-api-token` to `active` (`resolvable: true`)
### T5 — Notify downstream consumers
```task
id: WARDEN-WP-0025-T05
status: done
priority: medium
state_hub_task_id: "f8c7c70b-b9b6-5980-a25b-7f11033b81a2"
```
- [x] Ack the-custodian with CCR id + catalog id (no secret values) — msg 949e8ed1, 2026-07-13
- [x] Signal railiance-platform / activity-core / railiance-apps to wire `load_token()`
OpenBao paths — msgs 5be8e500 / 9ed1af98 / 2e47b6e5, 2026-07-13
---
## Acceptance
- CCR-2026-0006 approved, policy + OIDC role live, PAT in OpenBao
- `warden route find "forgejo admin pat"` → `forgejo-admin-api-token`
- No PAT in Git, State Hub, workplans, logs, or chat
- Workstation file drop retired as steady-state path after downstream wiring
## See also
- `railiance-platform/credential-change-requests/CCR-2026-0006-forgejo-admin-api-token-lane.yaml`
- `activity-core/workplans/ACTIVITY-WP-0020-weekly-forgejo-package-prune.md`
- Custodian message `54125f84-e9df-4fa0-9309-7370633a20d3`

View file

@ -0,0 +1,261 @@
---
id: WARDEN-WP-0026
type: workplan
title: "Credential disclosure hygiene + rotation guidance (Strand A)"
domain: infotech
repo: ops-warden
status: finished
owner: codex
topic_slug: custodian
planning_priority: high
planning_order: 26
created: "2026-07-16"
updated: "2026-07-16"
state_hub_workstream_id: "331c7620-bd34-5acd-9135-591985b568e5"
---
# Credential disclosure hygiene + rotation guidance (Strand A)
## Origin
Follow-up to a credential-disclosure incident on 2026-07-16 while verifying
`CCR-2026-0004` (railiance offsite backup lane). A negative policy test was run
as `BAO_TOKEN=$(bao token create -policy=default -field=token) bao kv get <path>`.
The `token create` was denied (workload role lacks it), `BAO_TOKEN` fell back to
the caller's privileged login token, and `bao kv get` printed all three field
values (`NC_WEBDAV_TOKEN`, `NC_WEBDAV_URL`, `AGE_PRIVATE_KEY`) into an agent
session transcript. Buildup mode — exposure accepted, learnings captured.
**Root causes:** (1) a deny-test that read the secret *data* path at all;
(2) a silent privileged-token fallback; (3) the read landed in a logged context.
This is **Strand A** (disclosure hygiene). Tamper-resistant policy governance and
one-command mass rotation/lockdown (Strand B) are deliberately **deferred** — see
"Out of scope" below.
## Goal
Make accidental secret disclosure structurally hard, and make ops-warden the
authoritative source for **how each secret is rotated or re-established** —
advisory knowledge held next to the routing catalog, not in OpenBao.
## Design guardrails (binding on acceptance)
- **Verification never reads secret data.** Use `bao token capabilities` (allow/deny)
instead of `kv get` for positive/negative lane tests.
- **Masking is defense-in-depth, not a boundary.** Any display filter is a wrapper
convenience; raw `bao kv get <path>` remains the documented anti-pattern.
- **Rotation guidance covers both `rotate` (provider re-mint) and `re-establish`
(regenerate from source, e.g. a new age keypair).**
- **Coverage gate:** every `active` (and newly promoted) catalog lane must carry
rotation guidance, enforced by a scorecard check.
## Out of scope (Strand B — deferred, not built here)
- Executable one-command rotation of all tainted secrets.
- Graded lockdown / break-glass seal + re-key with a designed trust-root.
- Policy time-travel / policy-as-code reconcile-to-past-commit. (Drift *detection*
may be revisited separately; the reconcile machinery is out.)
## Task: Capabilities-based lane verification
```task
id: WARDEN-WP-0026-T01
status: done
priority: high
state_hub_task_id: "1cb22a40-b7c6-560a-a805-7766a5786dcc"
```
Done 2026-07-16: canonical capabilities-safe verification pattern added to
`wiki/playbooks/catalog-lane-promotion.md` (fleet promotion checklist criterion 8
+ dedicated section), and applied to the `railiance-backup-offsite-lane` and
`forgejo-admin-api-token` playbook verify sections. Positive/negative proven via
`bao token capabilities` against the KV v2 data path — never `bao kv get`; the
denied `default` token-create is documented as a pass, not a fallback trigger.
Live CCR-2026-0004 re-verify carried under T07.
Replace secret-reading verify flows with capability checks. Positive test:
approved identity has `read` on the KV data path. Negative test: a `default`-only
identity is `deny`. Both via `bao token capabilities <token> <path>` (or the
self endpoint) — **never** `bao kv get`. Update
`wiki/playbooks/*lane*.md` verify sections and any lane-verify helper.
**Done when:** the documented and tooled verify path for any lane proves
allow/deny without reading a secret value, and the CCR-2026-0004 re-verify uses
it (see T07).
## Task: Safe access transport (no stdout values)
```task
id: WARDEN-WP-0026-T02
status: done
priority: high
state_hub_task_id: "bcb7da96-0a28-5484-bf3e-06e97acf5873"
```
Done 2026-07-16: sanctioned transports added to `warden access` so a value never
lands on stdout — `--out FILE` (mode-0600 file), `--exec` (child env, pre-existing),
and `--wrap` (single-use OpenBao response-wrapping token via `bao kv get -wrap-ttl`,
caller `bao unwrap`s in their own context). Raw `--fetch` to a non-TTY stdout is now
refused (exit 6) unless `--unsafe-stdout` is passed (interactive human only).
`proxy_fetch_to_file`/`proxy_fetch_wrapped`/`build_wrapped_fetch` in `proxy.py`; tests
in `tests/test_proxy.py`. Anti-pattern + transports documented fleet-wide in
`.claude/rules/credential-routing.md` and `wiki/OperatorAccessAssist.md` (G2).
`warden access` / fetch paths must emit values only into an env var, a file, or
a **response-wrapping token** (`bao … -wrap-ttl`), never a stdout table. Add a
wrapping-token transport for values that must move between processes. Record in
canon (`credential-routing` rules) that raw `bao kv get <path>` (full table) is
the anti-pattern; the sanctioned path is `warden access … --field <F>` into env.
**Done when:** the sanctioned fetch path cannot print a value to a terminal, and
the anti-pattern is documented fleet-wide.
## Task: Masking display filter (defense-in-depth)
```task
id: WARDEN-WP-0026-T03
status: done
priority: medium
state_hub_task_id: "d90b0628-fa1d-527d-99c3-28a7ed933e52"
```
Done 2026-07-16: `warden/mask.py` (`fingerprint`/`mask_value` — presence, length,
8-char sha256 prefix; never the value) + `proxy_fetch_fingerprint` and a
`warden access … --fingerprint` masked status view (bypasses the stdout guard
because it emits no value). Lets two parties compare sha256 prefixes to confirm a
shared value (e.g. rotation landed) without disclosure. Explicitly labelled
defense-in-depth — raw `bao kv get` bypasses it — in `wiki/OperatorAccessAssist.md`
and the module docstring. Tests in `tests/test_mask.py` + a CLI test in
`tests/test_proxy.py`.
In the warden wrapper, mask KV data values by default when any listing/status is
shown — display presence, length, and a short non-reversible hash instead of the
value. Explicitly labelled as defense-in-depth (raw bao bypasses it).
**Done when:** wrapper-mediated output never shows a raw KV value, with the
limitation documented.
## Task: Agent read-boundary on high-risk lanes
```task
id: WARDEN-WP-0026-T04
status: done
priority: high
state_hub_task_id: "827fa67d-5f69-5fac-bdce-9903b1b909fb"
```
Done 2026-07-16: Catalog `risk: high|standard` (default standard). High-risk:
`railiance-backup-offsite-lane`, `forgejo-admin-api-token`, `openrouter-llm-connect`.
OpenBao policy `agent-high-risk-boundary` (railiance-platform + live write) grants
metadata/capabilities only and **denies** data-read on those paths — verified with
minted agent token (data=deny, metadata=read). `warden access` with
`WARDEN_AGENT_ID` set refuses raw value stream on high-risk lanes (exit 7);
`--out`/`--exec`/`--wrap`/`--fingerprint` remain. Playbook:
`wiki/playbooks/agent-read-boundary.md`. Tests in `tests/test_routing.py` +
`tests/test_proxy.py`.
**Repo: railiance-platform (OpenBao policy/roles).** Agent identities receive
`capabilities`/metadata and wrapping tokens on high-risk lanes, not raw data
reads. Align with the existing credential-routing rule ("ops-warden proxies reads
as the caller and must not retain values"). Classify which lanes are high-risk
(recovery escrow like `AGE_PRIVATE_KEY`, upload tokens).
**Done when:** at least the high-risk lanes deny raw data reads to agent roles
while still allowing wrapped/proxied access, verified via capabilities checks.
## Task: EXPOSED taint convention
```task
id: WARDEN-WP-0026-T05
status: done
priority: medium
state_hub_task_id: "09ef8727-31da-59ac-aac7-2d47924569fe"
```
Done 2026-07-16: Convention documented in `wiki/playbooks/exposed-taint.md`
(`exposed_at`, `exposed_version`, `exposed_reason`, `exposed_ref` on KV v2
custom_metadata). First worked mark applied to
`platform/workloads/railiance/backup/offsite-lane` version 2 (disclosure
incident). `warden taint <id>` (+ `--json`) reports taint via metadata-only
`bao kv metadata get` — never secret data (`src/warden/taint.py`). Tests in
`tests/test_taint.py`.
**Repo: railiance-platform (OpenBao) + ops-warden surface.** Establish a KV v2
`custom_metadata` convention to mark a tainted secret: `exposed_at=<datetime>`
and the affected `version`. Identify semi-automatic candidates from the OpenBao
audit log (reads from agent/shared contexts). `warden` surfaces taint status for
a lane (advisory; no auto-rotation here).
**Done when:** a secret can be marked EXPOSED via a documented convention and
`warden` reports whether a lane is currently tainted.
## Task: Rotation / re-establishment guidance registry
```task
id: WARDEN-WP-0026-T06
status: done
priority: high
state_hub_task_id: "a2e1544e-e501-57eb-a40e-9a2147cef12a"
```
Done 2026-07-16: `rotation:` block (method rotate|re-establish, ordered steps,
owner, automatable) added to the routing model/parser (`RotationGuide`,
`RouteEntry.rotation`, `vends_secret`), screened for secret material in a prose-safe
mode. `warden rotate-guide <id>` (human + `--json`) surfaces the guidance; `warden
route show --json` carries `has_rotation` + `rotation`. Coverage enforced by the new
`catalog_rotation_coverage` scorecard check (every active secret-vending lane must
have a block) and promotion checklist criterion 9. Rotation blocks authored for all
7 active vending lanes + the draft railiance-backup lane (re-establish example: age
keypair regen + re-encrypt). Tests in `tests/test_routing.py`. Also fixed a
pre-existing keyword collision (bare `npm` on the forgejo-admin lane → `forgejo-npm`)
so "npm token" routes to the generic lane again.
Give every catalog lane **structured-but-advisory** renewal guidance, held in the
ops-warden registry (not in OpenBao). Add a `rotation:` block per catalog entry
capturing: `method` (rotate | re-establish), ordered `steps` (provider re-mint /
keygen / OpenBao write / re-encrypt-and-reupload where relevant), `owner`, and
`automatable` (bool, for future Strand-B). Surface via `warden rotate-guide <id>`
(and/or `warden route … --rotate`). Add a **scorecard coverage check**: every
`active` lane must have a `rotation:` block; flag any that don't.
**Done when:** `warden rotate-guide <id>` returns actionable renewal steps for
every active lane, and the scorecard fails if any active lane lacks guidance.
## Task: Incident lessons + first worked lane (CCR-2026-0004)
```task
id: WARDEN-WP-0026-T07
status: done
priority: medium
state_hub_task_id: "62d8286f-7954-52a8-bce6-6a16072e5246"
```
Done 2026-07-16: Lessons note present. Capabilities-safe live re-verify on
`bao.coulomb.social`: lane-policy token → `read` on data path; default + agent
boundary → `deny`; field keys present (no values printed). Catalog promoted
`draft`→`active`, `fetch_command` pinned to `NC_WEBDAV_TOKEN` (no placeholders)
so `resolvable: true`; `risk: high`; rotation guidance + EXPOSED taint on v2.
CCR-2026-0004 evidence + `access_frontdoor.resolvable: true` / `readiness: ready`.
Playbook + `wiki/CredentialRouting.md` updated. Operator may still rotate the
exposed values optionally (buildup) — not a promotion blocker.
Write a short lessons-learned note (buildup context; exposure accepted; the three
root causes). Apply T01 + T06 to `CCR-2026-0004` as the first worked lane:
re-verify it the capabilities-safe way so it can finally promote to
`resolvable: true` (unblocking `RAILIANCE-WP-0015`), and ensure its `rotation:`
block (rotate Nextcloud token; re-establish age keypair + re-encrypt artifacts)
is present. **Rotation of the exposed values is the operator's optional call, not
a blocker** (buildup).
**Done when:** the lessons note exists, CCR-2026-0004 has capabilities-based
verify + rotation guidance, and its promotion path is unblocked.
## References
- `CCR-2026-0004-railiance-backup-offsite-lane.yaml` (railiance-platform)
- `wiki/playbooks/railiance-backup-offsite-lane.md`
- `.claude/rules/credential-routing.md` (the-custodian, fleet-inlined)
- `RAILIANCE-WP-0015` (railiance-apps) — cnpg backup coverage, gated on CCR-2026-0004
- Strand B (deferred): tamper-resistant governance, one-command rotation/lockdown,
policy time-travel — capture separately if/when justified.

View file

@ -0,0 +1,303 @@
---
id: WARDEN-WP-0027
type: workplan
title: "Tamper-resistant credential governance + mass rotation/lockdown (Strand B)"
domain: infotech
repo: ops-warden
status: blocked
flavor: implementation
owner: codex
topic_slug: custodian
planning_priority: medium
planning_order: 27
created: "2026-07-16"
updated: "2026-10-03"
state_hub_workstream_id: "21528e8d-a049-523d-9ae1-da7a27cb8bbf"
---
# Tamper-resistant credential governance + mass rotation/lockdown (Strand B)
## Origin
Explicitly deferred from `WARDEN-WP-0026` (credential disclosure hygiene, Strand A).
WP-0026 "Out of scope" carves out the heavyweight governance machinery and directs
that it be "capture[d] separately if/when justified." This workplan is that capture.
Strand A makes accidental disclosure structurally hard and gives every lane
**advisory** rotation guidance. Strand B is the **executable, tamper-resistant**
layer: turning that advisory guidance into one-command action and hardening policy
governance against silent drift or malicious change.
## Status: blocked on T02 (reviewed 2026-09-28)
Activated by the operator on 2026-08-22 after the second activation gate became
concrete. `RAILIANCE-WP-0024-T03` now requires an approved recovery window,
fresh encrypted Raft snapshot evidence, provider-console access, named abort
authority, and the live **2-of-3 Shamir quorum** before a coordinated reboot.
That is fleet policy requiring a designed break-glass path, not speculative
heavyweight machinery.
Activation is deliberately narrow. No mass-disclosure incident triggers T01,
and no fleet policy currently mandates T03's signed policy-manifest reconcile.
Those tasks remain `cancel`; cancellation here continues to mean deferred, not
abandoned. T02 alone was activated; it now waits on a fresh attended drill.
## Activation gate (promote to `ready` only when ≥1 holds)
- A real disclosure incident requires rotating **more than a couple** of secrets at
once, making manual per-lane rotation (Strand A) too slow.
- Fleet policy for OpenBao mandates tamper-evident policy governance or a designed
break-glass path.
- Audit/compliance requires provable "reconcile policy to a known-good commit."
## Goal
Make credential response **executable and trustworthy at fleet scale**: one command
rotates or re-establishes a set of tainted secrets; policy changes are tamper-evident
and reconcilable to a known-good state; a graded lockdown / break-glass path exists
with an explicit trust-root.
## Scope boundary (unchanged from Strand A)
ops-warden still **custodies no secret values**. Strand B orchestrates the owner's
tools (OpenBao, provider re-mint, railiance-platform credential broker) as the
caller — it does not hold or vend secrets. Rotation execution runs the owner-native
path; ops-warden sequences and verifies it.
## Task: Executable mass rotation driver
```task
id: WARDEN-WP-0027-T01
status: cancel
priority: high
state_hub_task_id: "b5691939-9d84-5115-9618-0f8839010d14"
```
Turn Strand A's per-lane `rotation:` guidance (WP-0026 T06) into an executable
driver: `warden rotate <id>` (single lane) and `warden rotate --tainted` (all lanes
marked EXPOSED per WP-0026 T05). Each step runs the owner-native command
(provider re-mint, `bao kv put`, re-encrypt+reupload for re-establish lanes) as the
caller, verifies via capabilities (never reads the value), and clears the
`exposed_at` taint on success. Dry-run first; idempotent; per-lane failure isolates.
**Done when:** a tainted set of lanes can be rotated/re-established with one command,
each verified capabilities-safe, taint cleared only on success.
**Depends on:** WP-0026 T05 (taint convention), T06 (rotation registry).
## Task: Graded lockdown / break-glass with explicit trust-root
```task
id: WARDEN-WP-0027-T02
status: todo
decision_id: 1591daa1-6e85-4dfd-aadd-5a9fe1ef47fa
priority: medium
state_hub_task_id: "cae498ee-6307-5d32-9f1b-a471cfcc2536"
```
2026-10-11: requalified after founder decision D3 (trust root, build-mode default) resolved — see the-custodian docs/decisions/2026-10-11-security-unblock-decisions.md.
Design and document a graded lockdown: (a) soft — deny agent roles read on all
high-risk lanes; (b) hard — seal + re-key with a pre-designed trust-root and quorum.
Define the trust-root (who holds unseal shares, recovery keys), the break-glass
invocation, and the re-entry path. **Design + runbook first**; any executable seal
step is opt-in and attended.
**Done when:** a documented, rehearsed break-glass path exists with a named
trust-root and quorum, and soft-lockdown is executable via capabilities-based
policy toggles.
**Parked input — the warden-sign AppRole (2026-08-11).** secrets-engine holds a
validated dry-run for a `warden-sign` AppRole (policy + role, `update` on
`ssh/sign/{agt,adm,atm}-role` only). It was **parked** rather than withdrawn
specifically because it is an input to this task: today the credential broker is the
only *autonomous* path to a signing token, so if its issuer lapses, recovery is a
founder OIDC act. Whether a second autonomous path should exist — and whether it is
the AppRole or something this trust-root design supersedes — is a break-glass
question, not a routing one. Resolve it here. Context and the counter-argument
(AppRole is narrower in capability than placing the broker's issuer token on a remote
host) are in `workplans/ADHOC-2026-08-11.md` T03 (ad-hoc **finished** 2026-08-15;
question lives here now); secrets-engine was told to stop holding apply
readiness until this task or the ops-bridge cutover fires (msg `863c7b57`).
**Rebaselined 2026-08-22.** The July description predates the authoritative
OpenBao migration. The current barrier is a rotated **2-of-3**, not the
aspirational 3-of-5 value still present in ops-warden's legacy posture
descriptor. `RMASTER-WP-0020` records two attended restart/unseal cycles on
2026-08-03 with shares supplied through hidden prompts, matching inventory,
audit continuity, exact-path read, and sibling denial. That is valid rehearsal
evidence for re-entry, but it is not misreported as a current production
emergency-seal drill.
The soft half is also already live: `RAILIANCE-WP-0022` reports total concrete
high-risk path coverage and a dedicated coding-agent AppRole proving deny-wins
without reading a secret value. ops-warden consumes that owner control through
`scripts/check_agent_read_boundary.py`; it does not take ownership of the live
OpenBao policy.
The consumer contract and remaining acceptance gate are now documented in
`docs/credential-governance-break-glass.md`. One attended production emergency
seal/unseal drill remains. It must use railiance-platform's evidence template
and validator, have snapshot/quorum/console/driver/abort receipts before the
seal hold point, and be executed by the platform owner—not by a coding agent.
**Parked AppRole disposition:** do not unpark the standalone `warden-sign`
AppRole for T02 break-glass. The normal broker remains autonomous; an issuer
failure deliberately crosses into attended OIDC and, below that, the 2-of-3
platform trust-root. A standing AppRole placed outside the broker would weaken
that boundary and still would not recover a sealed OpenBao. A separately
approved ops-bridge unattended-signing design may re-evaluate the narrow
AppRole under its own workplan; that is service access, not recovery authority.
**Read-only readiness 2026-08-22.** The owner `node-reboot` preflight passed
every automated check without observing a secret value: node/k3s, platform-pg
and continuous archiving, a 16.37-hour successful backup, initialized/unsealed
OpenBao with `shares=3` and `threshold=2`, ESO stores/projections, and the
reviewed audit-core digest were healthy. `ready_for_live_execution` correctly
remained false because no approved window, current encrypted off-host snapshot
receipt, provider-console attestation, quorum attestation, owner-ack flag, or
named abort operator was supplied.
That preflight also exposed a Warden safety defect: the ceremony request matched
the generic `openbao-api-key` template and proposed paste-once provisioning.
`openbao-shamir-recovery-ceremony` now distinguishes the owner-operated approval
ceremony from secret retrieval. It is a non-value-bearing pointer and can never
offer `--fetch`, `--exec`, `--out`, `--wrap`, or paste-once share transport.
**Scenario established 2026-08-22.** The operator delegated preparation to the
agent and reserved their involvement for the final GO/NO-GO. The bounded,
non-secret scenario is
`docs/evidence/WARDEN-WP-0027-T02-drill-scenario-2026-08-22.md`: ops-warden
coordinates and enforces the hold point; railiance-platform is the proposed
OpenBao driver; railiance-infra is the proposed independent console/abort owner;
and railiance-master is asked only for a metadata-only 2-of-3 availability
attestation. All three owner receipts and a green fully parameterized preflight
are required before the final operator question.
The railiance-infra role is scoped by the authoritative review contract
`WARDEN-WP-0027-T02-DRILL-20260822-01-INFRA` in
`interfaces/reviews/WARDEN-WP-0027-T02-DRILL-20260822-01-railiance-infra.json`.
It binds the exact scenario artifact and expiry, requires explicit independent
provider-console and distinct-abort attestations, permits metadata-only evidence,
and states `authorizes_execution: false`.
**Preparation approved 2026-08-22.** Decision
`9da57559-712a-4521-b46e-a4c69729f9d2` authorizes preparation only and narrows
the possible live scope to one intentional OpenBao seal followed by the existing
2-of-3 unseal ceremony. It excludes a host reboot, re-key, restore, policy
change, PVC mutation, credential disclosure, and general workload restart.
Railiance-infra satisfied its direct contract with receipt
`01a02b4b-7295-7836-b288-f29407008524`. The revisioned, non-authorizing gate
matrix is in
`docs/evidence/WARDEN-WP-0027-T02-drill-preparation-checklist-2026-08-22.md`;
platform snapshot/driver and master quorum contracts remain required before the
fully parameterized read-only preflight.
**Live attempt terminal NO-GO 2026-08-23.** All owner receipts and the final
read-only preflight became green, and the operator opened the exact bounded
window with decision `449a697a-5303-4582-aa9e-b0bc8b35ab2d`. The platform
driver stopped before seal when attended OIDC succeeded but the read-only agent
home prevented token-helper persistence and the underlying client emitted the
short-lived credential into captured output despite `-no-print`. The credential
was immediately self-revoked. Independent metadata confirmed OpenBao remained
initialized and unsealed with zero unseal progress; no seal, unseal, reboot,
restore, policy, PVC, or workload mutation occurred. NO-GO decision
`85724c0c-e70f-4e2f-a8c6-a9cb1ea6331b` supersedes and consumes the GO; the
scenario and its receipts cannot be reused.
The direct remediation interface is `RAILIANCE-WP-0026-T01`. Ops-warden commit
`0fae0904ce8d8694338dd53a8a79abec5fec788d` replaces the persistent login-only
handoff with a contained login-plus-reviewed-command envelope. It proves a
private writable helper before OIDC, captures login/child/revocation stdout and
stderr, fails closed on any output or persistence defect, self-revokes, and
removes the helper. No live OIDC was used for verification. The value-safe
owner receipt is
`docs/evidence/RAILIANCE-WP-0026-T01-ops-warden-receipt.json`; a future drill
requires owner acceptance, fresh receipts, a new scenario id, and a new human
decision.
**Containment review status 2026-08-23.** `railiance-infra` independently
accepted the exact implementation revision at its commit `186b030` and State
Hub message `7b2846dd-4b7c-4933-b61f-921e8ccf9ff2`, after rerunning the 42
focused tests and lint. Direct platform review request
`9a5f8973-0e74-4df2-8089-5a5bdc42295b` now carries the exact revision, receipt,
test results, and infra acceptance. T02 remains `progress` until
`railiance-platform` accepts or requests changes. No human GO is relevant while
that owner gate is open; acceptance will permit preparation of a new scenario,
not execution or reuse of the terminal one.
**Owner gate CLOSED — accepted 2026-08-23, found 2026-08-28.**
`railiance-platform` accepted the exact revision `0fae0904`. Recorded in their
`RPF-WP-0017-attended-login-output-containment.md` (`status: finished`), which
also records railiance-infra's independent approval at `186b030` and marks all
five acceptance criteria met. The receipt digest was verified here rather than
taken on trust: `docs/evidence/RAILIANCE-WP-0026-T01-ops-warden-receipt.json` at
ops-warden `bc1966da` hashes to
`d2ba444ed16989590325697e69d25283dc75a9432c29a72e627e80bf9fd987e4`, matching
their record exactly.
Note the identifier: the remediation interface recorded above as
`RAILIANCE-WP-0026-T01` is `RPF-WP-0017-T01` in the owner's repo. Searching for
the cited id finds an unrelated workplan, which is part of why this sat unnoticed.
**Their acceptance is source acceptance only and authorizes no live OIDC or
drill** — their words, and the boundary holds. So T02's state changes but its
`Done when` does not: what was blocked was *preparing a new scenario*, and that
is now permitted. The terminal NO-GO scenario and its receipts remain unusable.
**Third instance of the same failure this session.** The acceptance existed for
five days; no message reached ops-warden. Identically, `key-cape` accepted the
WP-0033-T04 question on 2026-08-23 with no message, and nine unread messages
were sitting on already-superseded threads. The `verified:` field added by
WP-0033-T05 was built for exactly this and it works — what does not work is
waiting for a counterparty to tell you. Re-checking a blocker means reading the
owner's repository.
**Remaining to close T02:** one attended production emergency seal/unseal drill,
requiring a new scenario id, fresh owner receipts from platform/infra/master, a
fully parameterized green preflight, and a new human GO. It is executed by the
platform owner, never by a coding agent. That is an operator decision, not an
agent one, so T02 stays `progress` and the decision is surfaced rather than taken.
2026-10-03: human gate routed to hub decision 1591daa1-6e85-4dfd-aadd-5a9fe1ef47fa (D3: Trust root and secret custody: unseal/recovery holders, break-glass, and the deferred rotation of exposed material) by CUST-WP-0075; resolving that decision raises C-40 here.
## Task: Tamper-evident policy governance + reconcile
```task
id: WARDEN-WP-0027-T03
status: cancel
priority: medium
state_hub_task_id: "7dbedcdc-dd5a-551c-bff1-0702fea0a9cf"
```
Policy-as-code for OpenBao policies/roles with tamper-evidence: a signed/hashed
manifest of the intended policy set, drift **detection** against live OpenBao, and a
reconcile-to-known-good-commit path. (Drift detection may land independently even if
full reconcile stays gated.) Coordinate ownership with railiance-platform (OpenBao
deployment) — ops-warden contributes the ops-access policy surface, not the cluster.
**Done when:** live OpenBao policy state is diffable against a signed known-good
manifest, and an attended reconcile can restore it.
**Cross-repo:** railiance-platform (OpenBao cluster + policy custody).
## References
- `WARDEN-WP-0026` — Strand A (disclosure hygiene, advisory rotation) — prerequisite
- `.claude/rules/credential-routing.md` — ops-warden custodies no secret values
- `railiance-platform` — OpenBao cluster, policy custody, credential broker
- `wiki/AccessRouting.md` — issue vs route vs assist boundary
- `workplans/ADHOC-2026-08-11.md` T03 — parked warden-sign AppRole (ad-hoc finished
2026-08-15); T02 resolves it as unsuitable for break-glass, while a separately
approved ops-bridge unattended-signing design may still re-evaluate it
cert_command cutover
- `secrets-engine` `SECRETS-WP-0004` — the parked AppRole apply/handoff
### 2026-09-28 loose-end review
T02 now waits and the workplan is blocked. Source containment acceptance is
already complete in railiance-platform's archived RPF-WP-0017; it is not a live
drill receipt. The terminal August scenario remains consumed. Completion still
requires a fresh platform-driven attended emergency seal/unseal scenario, fresh
platform/infra/master receipts and a new founder GO. The cancelled T01/T03 stay
cancelled. No ceremony, rotation or production mutation was performed.

View file

@ -0,0 +1,421 @@
---
id: WARDEN-WP-0028
type: workplan
title: "Tenant secret custody — NetKingdom pattern for client/tenant secrets"
domain: infotech
repo: ops-warden
status: finished
owner: grok
topic_slug: custodian
planning_priority: high
planning_order: 28
created: "2026-07-16"
updated: "2026-07-17"
state_hub_workstream_id: "6b66228a-199a-5b85-b5e2-a7afeaab903b"
---
# WARDEN-WP-0028 — Tenant secret custody (NetKingdom pattern)
## Origin
Founder establishing **binky-control** as the control-plane repo for a Coulomb
client/tenant (Binky-Hedgehog GmbH). Immediate need: store **tenant-specific**
secrets in OpenBao (e.g. company mailbox usernames/passwords for IMAP scan) and
consume them safely via email-connect / agents **without** putting values in
git, State Hub, chat, or workplans.
BINKY-WP-0003-T01 prepared a draft plan
(`binky-control/integrations/company-email-openbao.md`) that targeted
`secret/prod/binky/...` via secrets-engine. That draft is directionally right on
*workflow*, but it does not match a production-ready custody layout on
`bao.coulomb.social`. This workplan resolves the NetKingdom layout and makes
**tenant** secrets first-class — **separate from** platform workload lanes
(`platform/workloads/...`).
## Goal
Define and land a **repeatable, multi-tenant-safe** pattern for:
1. Where tenant secrets live in OpenBao (path + mount convention)
2. Who owns policy, CCR, catalog, and front door
3. How agents and workloads obtain values (sanctioned transports only)
4. First concrete lane: **binky company email IMAP** (unblocks real-world event
intake for binky-control)
## Architectural facts (as of 2026-07-16)
### Planes (ADR-0006)
| Plane | Role | Example |
| --- | --- | --- |
| Bootstrap | First trust / recovery | unseal, platform-root |
| Platform control | Shared identity, secrets, authz | OpenBao, key-cape, flex-auth, railiance-platform |
| Tenant | Scoped client/workload authority | coulomb products, **binky**, future clients |
**Binky is a tenant**, not platform-root. Tenant secrets must not require
platform-admin for day-to-day read, and tenant operators must not gain
platform-root grants.
### OpenBao mounts (live + planned)
| Mount | Type | Role |
| --- | --- | --- |
| **`platform/`** | KV v2 | **Platform control-plane** secrets: `platform/workloads/...` (fleet CCRs, ESO) and `platform/operators/...` — *not* client/tenant commercial secrets |
| **`secret/`** | KV v2 | secrets-engine stage / pilot paths (`secret/coulomb/...`) — not the tenant home |
| **`tenants/`** | KV v2 (**new**) | **Client/tenant commercial secrets** — explicit multi-tenant namespace |
Live under `platform/workloads/` today: `activity-core/`, `coulomb/`, `forgejo/`,
`issue-core/`, `railiance/`, `reuse/`. Those stay where they are; we do **not**
migrate them into `tenants/` in this workplan.
### Dual front-door systems (both real; don't invent a third)
| Layer | Owns | Entry |
| --- | --- | --- |
| **OpenBao** | Custody, ACL, audit | `bao` CLI / API |
| **railiance-platform CCR** | Approved metadata apply (policy + OIDC role + evidence) | `credential-change-requests/CCR-*.yaml` |
| **ops-warden catalog** | Routing + `warden access` proxy (no custody) | `registry/routing/catalog.yaml` |
| **secrets-engine catalog** | Workflow: decision → plan → provision → exec delivery | `secrets-engine/catalog/*.yaml` |
**Binding rule (ops-warden):** never vend secret values; only route/proxy as the
caller. **Binding rule (WP-0026):** capabilities-safe verify; agents on
`risk: high` use `--out` / `--exec` / `--wrap` only.
### Why not bury tenants under `platform/workloads/`
Earlier draft of this workplan used `platform/workloads/<tenant>/...`. Founder
prefer a **shallower, explicit** namespace. That is sound:
- **No conflict with Vault/OpenBao best practice.** Separate mounts (or
top-level prefixes) for different security *domains* are common and good.
`platform/workloads` is an *internal fleet convention* for platform services,
not an industry mandate for every secret.
- **Clearer mental model:** platform plane vs tenant plane (ADR-0006) maps to
mount/prefix names operators can see at a glance.
- **Isolation:** ACLs, list boundaries, and future audit queries stay simpler
when tenant material is not mixed under the same path tree as forgejo/ESO
lanes.
### Decision (2026-07-17) — use dedicated mount `tenants/`
```text
mount: tenants # KV v2, enable once (platform-admin)
logical path: <tenant_slug>/<workload>/<bundle>
CLI: tenants/<tenant_slug>/<workload>/<bundle>
API data path: tenants/data/<tenant_slug>/<workload>/<bundle>
```
**Rejected alternatives (kept for archaeology only):**
| Option | Why not |
| --- | --- |
| `secret/prod/binky/...` | `secret/prod/` does not exist; stage model not ready as sole production path |
| `platform/workloads/binky/...` | Too deep; conflates tenant clients with platform workloads |
| `platform/tenants/binky/...` | Acceptable fallback if enabling a new mount is blocked; prefer full `tenants/` mount |
### Tooling debt to clear (not a reason to keep the deep path)
`railiance-platform/scripts/credential-change.py` currently hard-fails unless:
- `openbao.mount == "platform"`
- `openbao.kv_path` starts with `platform/workloads/`
That guard must be **extended** to allow `mount: tenants` and
`kv_path` under `tenants/` (T03). It is an implementation constraint, not a
security reason to force the deep path.
## Recommended path convention (tenant secrets)
```text
tenants/<tenant_slug>/<workload>/<bundle>
```
| Segment | Rules | Binky email example |
| --- | --- | --- |
| `tenant_slug` | Stable kebab slug; company/client id | `binky` |
| `workload` | Capability or system using the secret | `company-email` |
| `bundle` | Single purpose unit (one CCR / one policy) | `imap` |
Full CLI path:
```text
tenants/binky/company-email/imap
```
Suggested fields (names only — never values in git):
| Field | Purpose |
| --- | --- |
| `IMAP_USERNAME` | mailbox login (often the address) |
| `IMAP_PASSWORD` | app password / mailbox password |
| `IMAP_HOST` | optional if not fixed in consumer config |
| `IMAP_PORT` | optional (default 993 in config) |
Non-secret connection facts (provider host if stable) may live in
`binky-control` config / email-connect yaml as **env names + host**, not
passwords.
### Future multi-mailbox / multi-tenant
```text
tenants/<tenant>/mailbox/<mailbox_id>
# e.g. tenants/binky/mailbox/founder-primary
# tenants/acme/mailbox/billing
```
One CCR + one least-privilege policy **per bundle** (or per mailbox). Do not
grant `tenants/binky/*` list/read to agents — exact-path policies only.
### Risk class
Mailbox credentials and similar client secrets are **`risk: high`** (WP-0026
T04): agent identities get metadata/capabilities only; raw stream refused when
`WARDEN_AGENT_ID` is set.
## Ownership split
| Concern | Owner repo | Artifact |
| --- | --- | --- |
| Tenant business need, IMAP host facts, scan config, queues | **binky-control** | `integrations/*`, mailmeta (metadata only), activity defs |
| OpenBao path, policy HCL, OIDC role, CCR lifecycle | **railiance-platform** | `CCR-YYYY-NNNN-…yaml`, `openbao/policies/…` |
| Routing front door + rotation guidance | **ops-warden** | catalog entry, playbook, `warden access` / `rotate-guide` |
| Exec-time delivery workflow (optional same path) | **secrets-engine** | catalog entry pointing at `tenants/...` (same path; no second copy) |
| Identity groups for tenant operators | **net-kingdom / key-cape** | group e.g. `tenant-binky-operators` (near-term may use `net-kingdom-admins` for founder-only) |
| Mailbox scan consumer | **email-connect** | config with `username_env` / `password_env` only |
## Auth model (near-term vs target)
**Near-term (founder dogfood):** OIDC `netkingdom` role bound to
`groups=net-kingdom-admins` (same pattern as backup/forgejo lanes). Acceptable
while binky is founder-operated only.
**Target (real multi-tenant):**
- IAM group `tenant-binky-operators` (and later per-tenant groups)
- OpenBao OIDC role `binky-company-email-imap-workload-kv-read` bound only to
that group
- flex-auth check `secret.read:binky-company-email` if pre-approval required
- Agents **never** hold the workload-kv-read policy; use operator wrap / exec
injection / AppRole for email-connect scanner only
## First worked lane (deliverable sketch)
| Item | Value |
| --- | --- |
| Catalog id | `binky-company-email-imap` |
| CCR | `CCR-2026-0007` (next free) — title: Binky company email IMAP |
| Mount | `tenants` |
| KV path | `tenants/binky/company-email/imap` |
| Policy | `workload-kv-read-binky-company-email-imap` |
| OIDC role | `binky-company-email-imap-workload-kv-read` |
| Consumer | email-connect `scan-mailbox` via env inject |
| Front door | `warden access binky-company-email-imap --out FILE` or `--exec -- …` |
| Provision | **Red lane, founder once** — value via `bao kv put … @file` or secrets-engine provision; never chat |
## Out of scope
- Mass rotation / lockdown machinery (WARDEN-WP-0027 Strand B)
- Sending mail / SMTP from company address (separate lane if needed)
- Migrating all `secret/` mount lanes onto `platform/` (separate secrets-engine
/ railiance work)
- Full key-cape tenant group productization (may be a net-kingdom follow-on)
## Tasks
### T01 — Canon note: tenant secret path + ownership
```task
id: WARDEN-WP-0028-T01
status: done
priority: high
state_hub_task_id: "9982a884-7a50-593e-861e-cc8d2343a0ba"
```
Done 2026-07-17: `wiki/playbooks/tenant-secret-onboarding.md` + CredentialRouting
tenant paragraph; catalog-lane-promotion draft table updated.
Land a short ops-warden wiki page (and one paragraph in
`wiki/CredentialRouting.md`) that freezes:
- mount **`tenants`** + path `tenants/<tenant>/<workload>/<bundle>`
- ownership table (binky-control / railiance-platform / ops-warden / secrets-engine)
- link to WP-0026 hygiene (capabilities verify, high-risk, taint)
- **do not** put new client/tenant commercial secrets under
`platform/workloads/` or invent `secret/prod/...` as the production home
**Done when:** wiki + CredentialRouting pointer exist; binky integration doc
updated to match (or superseded with link).
### T02 — Align binky-control integration plan to production path
```task
id: WARDEN-WP-0028-T02
status: done
priority: high
state_hub_task_id: "d7d7ee9d-2ecf-5492-8a13-0b747d899456"
```
Done 2026-07-17: `binky-control/integrations/company-email-openbao.md` rewritten
to `tenants/binky/company-email/imap` + checklist (host confirm + Red provision
still open).
**Cross-repo: binky-control.** Rewrite
`integrations/company-email-openbao.md` to use
`tenants/binky/company-email/imap`, CCR + ops-warden front door as primary,
secrets-engine as optional exec wrapper on the same path. Confirm non-secret
IMAP host/port with founder (Blue). Keep Red-lane value provision as the last
step.
**Done when:** binky doc matches this workplan; checklist items re-opened for
implementation (T01 in BINKY was "prepared only").
### T03 — Enable `tenants` mount + extend CCR tooling + policy/role
```task
id: WARDEN-WP-0028-T03
status: done
priority: high
state_hub_task_id: "e0b675ef-9c08-5f89-8f13-ef4249ca2364"
```
Done 2026-07-17: `tenants/` KV v2 mount live; CCR applier allowlist extended;
CCR-2026-0007 + policy + OIDC role applied; lane-policy `read` / default
`deny` / agent boundary on tenants path; `docs/workload-kv-access-lanes.md`
tenant section.
**Cross-repo: railiance-platform (+ OpenBao admin once).**
1. **Enable KV v2 mount** `tenants/` on `bao.coulomb.social` (platform-admin /
attended; record non-secret evidence). Versioning + max versions policy
aligned with `platform` if practical.
2. **Extend** `scripts/credential-change.py` allowlist: `mount: tenants` and
`kv_path` under `tenants/` (keep existing `platform/workloads/` rules
intact for fleet lanes). Update dry-run docs and tests.
3. Author `CCR-2026-0007` (or next id) for the binky IMAP path; policy HCL on
exact data/metadata paths under `tenants/…`; OIDC role (founder group
near-term). Dry-run; founder approves; apply metadata only.
4. Document fields without values; add a **tenant lanes** section to
`docs/workload-kv-access-lanes.md` (or sibling doc).
**Done when:** mount exists; CCR tool accepts tenant paths; CCR approved +
policy/role applied; capabilities-safe positive/negative evidence (no value
reads for verify).
### T04 — ops-warden catalog + playbook + rotation
```task
id: WARDEN-WP-0028-T04
status: done
priority: high
state_hub_task_id: "9405b13d-4045-519b-8e3f-79a891323397"
```
Done 2026-07-17: catalog draft `binky-company-email-imap` (`risk: high`,
rotation, path `tenants/binky/...`); playbooks
`binky-company-email-imap.md` + onboarding. Promote to active after T05.
Add `binky-company-email-imap` to `registry/routing/catalog.yaml`:
`status: draft` until verify, then promote; `risk: high`; `exec_capable: true`;
concrete `fetch_command` for primary field; `rotation:` block (re-establish or
rotate mailbox app-password). Playbook under `wiki/playbooks/`. Scorecard
rotation coverage satisfied on promote.
**Done when:** `warden route show binky-company-email-imap --json` shows
expected pointers; after T03+T05, `resolvable: true`.
### T05 — Founder provision (Red) + first scan evidence
```task
id: WARDEN-WP-0028-T05
status: done
priority: high
state_hub_task_id: "f17bba95-bc53-5c2a-8b44-7df9e42b2341"
```
Done 2026-07-17: Founder provisioned via OpenBao UI (KV version 2; not
placeholder). Capabilities-safe verify pass; catalog + CCR promoted
active/resolvable. Optional follow-up: first email-connect read-only scan into
`binky-control/mailmeta/` (OperatingRhythm), not a custody blocker.
**Human-only value path.** Founder provisions IMAP username/password into
OpenBao via approved tool (`bao kv put … @file` or secrets-engine provision).
Agents never see the value. Run email-connect read-only scan with
`warden access … --exec` or secrets-engine exec; store **metadata-only**
evidence under `binky-control/mailmeta/`.
**Done when:** one successful read-only scan evidence note exists; lane
promoted active/resolvable; CCR verification evidence complete.
### T06 — Generalize "tenant secret onboarding" playbook
```task
id: WARDEN-WP-0028-T06
status: done
priority: medium
state_hub_task_id: "76bd01a0-1d03-5ff9-8f59-a1b44763979d"
```
Done 2026-07-17 with T01: `wiki/playbooks/tenant-secret-onboarding.md` +
CredentialRouting link.
From the binky lane, write a reusable playbook:
`wiki/playbooks/tenant-secret-onboarding.md` — steps for any new tenant:
slug, CCR template fields, policy naming, catalog entry, agent boundary,
rotation block, promotion checklist. Intended for the next Coulomb client
without redesign.
**Done when:** playbook exists and is linked from CredentialRouting + first
session notes for tenant repos.
### T07 — secrets-engine alignment decision (record only)
```task
id: WARDEN-WP-0028-T07
status: done
priority: low
state_hub_task_id: "46c8f8a9-3bbe-568f-8a45-07b690874273"
```
Done 2026-07-17: **Decision** — tenant production lanes use `mount: tenants`
and the CCR path only; secrets-engine may wrap exec delivery against that same
path later but must not store a second copy under `secret/`. Until stage roles
gain an approved `tenants/` grant, production path is CCR + ops-warden +
caller `bao`.
Record a short decision: secrets-engine catalog entries for **tenant**
production lanes **must** use `mount: tenants` and the same path as the CCR
(no second copy under `secret/`). Stage roles may need a later extension to
touch `tenants/` under an approved plan — until then, ops-warden + CCR +
`bao` as caller are the production path.
**Done when:** decision note in this workplan or `POST /decisions/` + wiki
pointer; no dual-value storage for the IMAP password.
## Suggested implementation order
1. T01 canon (unblocks everyone)
2. T02 binky doc alignment
3. T03 mount + CCR tooling + CCR apply (metadata)
4. T04 catalog draft
5. T05 founder provision + verify + promote
6. T06 generalize playbook
7. T07 secrets-engine alignment (can parallel after T01)
## Acceptance
- [ ] Documented path convention `tenants/<tenant>/…` used for ≥1 live lane (`binky`)
- [ ] CCR applier accepts `tenants/` without weakening `platform/workloads/` rules
- [ ] No secret values in any git/State Hub artifact
- [ ] Lane is capabilities-verified and agent high-risk safe
- [ ] email-connect can scan with env injection without printing password
- [ ] Next tenant can copy the onboarding playbook without redesign
## References
- ADR-0006 recursive multi-tenant identity (`net-kingdom/docs/adr/…`)
- `net-kingdom/docs/secrets-engine-security-infrastructure-boundary.md`
- `railiance-platform/docs/workload-kv-access-lanes.md`
- `railiance-platform/docs/credential-change-approval.md`
- `binky-control/integrations/company-email-openbao.md` (draft to align)
- WARDEN-WP-0026 disclosure hygiene (capabilities, high-risk, taint, rotation)
- WARDEN-WP-0027 Strand B (out of scope here)

View file

@ -0,0 +1,159 @@
---
id: WARDEN-WP-0029
type: workplan
title: "Policy front door: posture-aware access planning + founder interaction surface"
domain: infotech
repo: ops-warden
status: finished
owner: codex
topic_slug: custodian
planning_priority: high
planning_order: 29
created: "2026-07-18"
updated: "2026-07-18"
state_hub_workstream_id: "bbb3d9ec-d88d-5088-b4c0-55bfba0a10cf"
---
# WARDEN-WP-0029 — Policy front door + founder interaction surface
## Origin
Founder directive 2026-07-18 (binky-control cutover prep): agents proposed
raw credential mechanics to the founder ("attach the deploy key in the
forgejo UI", "save the PAT to /tmp/...") although a catalogued lane
(`forgejo-admin-api-token`, WARDEN-WP-0025) already covered the need — the
installed CLI's catalog was stale, and nothing forced the ask-warden-first
step. Directive: **ops-warden is the service to ask what can and needs to be
done** per NetKingdom policy; the founder is bothered only when policy makes
it absolutely necessary, and then preferably via a purpose-built interaction
surface, not CLI/file handoffs. The organization is in **build phase** and
ops-warden must know that posture. INTENT.md principles 7 and 8 (added with
this workplan) capture the direction.
## Goal
1. `warden plan "<need>"` — a policy decision front door: given a need, answer
*autonomous / founder-act-required / unroutable*, with the exact commands
or the exact founder act, posture-stated.
2. Declared **organization posture** (`build`) as a **third axis** beside env
posture (dev/test/prod) and workload maturity (M0–M3).
3. A **founder interaction surface**: local web page for the rare founder
acts (approve, OIDC-login prompt, paste-once secret capture straight into
OpenBao) — no values through CLI history or files.
4. Catalog freshness + agent guidance so ask-warden-first is the enforced
default.
## Design constraints (optimized 2026-07-18)
- **Compose, do not fork:** `warden plan` must call the routing catalog +
`expand_handoff` / `resolvable`; it must not re-implement keyword matching
or invent a second catalog.
- **Org posture is a third axis:** do not overload env posture or maturity.
- **Desk MVP is build-phase only:** localhost, OS-session trust, stdlib HTTP
server (pattern: net-kingdom `security-bootstrap-console`). No core-hub /
whynot dependency for v1.
- **Reuse map (reuse.coulomb.social):** flex-auth (policy-evaluate), key-cape
(OIDC act), net-kingdom bootstrap console (localhost desk shape),
railiance-platform (OpenBao custody). Do not rebuild those owners.
## Delivery sequence
```text
T02 (org posture) ─┐
T05 (catalog freshness) ─┼─→ T01 (warden plan composes both)
T04 (playbook sweep) ───┘ (parallel once plan shape known)
T03 (desk) ← after T01 can emit founder_required with a typed act
```
T05 is high priority: catalog staleness was the incident root cause.
## Tasks
### T02 — Declared organization posture (build phase)
```task
id: WARDEN-WP-0029-T02
status: done
priority: high
state_hub_task_id: "6c213024-6601-5116-b52f-d6711dc0587d"
```
Added `organization_posture: build` as axis C in
`registry/policy/security-posture.yaml` (relaxations + graduation triggers).
Surfaced in `warden policy list/show`, scorecard, and `warden plan` output.
### T05 — Catalog freshness + agent guidance
```task
id: WARDEN-WP-0029-T05
status: done
priority: high
state_hub_task_id: "c82d8745-4af4-5b89-ab49-06d8a902e592"
```
`Catalog.freshness()` reports source (repo/bundled/override), content hash,
mtime, package version, stale entry count, and warnings for bundled fallback.
Human `warden route list` prints catalog line; plan JSON embeds `catalog`.
Scorecard check `catalog_freshness`. AGENTS.md + credential-routing rules
require `warden plan` before founder credential steps.
### T01 — `warden plan` decision front door
```task
id: WARDEN-WP-0029-T01
status: done
priority: high
state_hub_task_id: "34db38fa-cb99-5ced-9a12-85176a7f2b44"
```
`warden plan "<need>" [--actor] [--domain] [--json]` composes catalog find +
handoff + org posture + policy gate. Verdicts: `autonomous` /
`founder_required` (`oidc_login` | `approve` | `paste_once_provision`) /
`unroutable` (CCR stub). Metadata-only audit. Module: `src/warden/plan.py`.
### T04 — Retire file-drop patterns from playbooks
```task
id: WARDEN-WP-0029-T04
status: done
priority: medium
state_hub_task_id: "717f57da-fbc4-5a4d-9f8c-c1c3fa75e0ee"
```
`wiki/playbooks/forgejo-admin-api-token.md` rewritten: plan-first, sanctioned
transports (`--exec`/`--out`/`--wrap`), desk paste-once for provision; `/tmp`
file drops marked retired. Consumer follow-ups noted for railiance-platform
and binky-control docs.
### T03 — Founder interaction surface (local web approval page)
```task
id: WARDEN-WP-0029-T03
status: done
priority: medium
state_hub_task_id: "47c781d2-768b-5777-b971-b5227fe41f5c"
```
`warden desk` — loopback `ThreadingHTTPServer`, short-lived token URL, acts
approve / oidc_login / paste_once_provision (bao kv put via stdin, dry-run
supported). Metadata-only audit open/close. Module: `src/warden/desk.py`.
## Acceptance
- [x] `warden plan "forgejo deploy key for binky-control"` → `autonomous` /
`agent-harness-forgejo-deploy` with access commands
- [x] Provision-style needs → `founder_required` with typed founder act
- [x] Posture `build` in plan + policy + scorecard
- [x] Desk approve flow (unit) with zero secrets in audit
- [x] No in-repo playbook instructs founder file drop as steady state
- [x] Catalog freshness on `warden route list` / plan JSON
## See also
- INTENT.md §7 (founder escalated, never tasked) and §8 (build-phase posture)
- WARDEN-WP-0025 (forgejo admin token lane), WARDEN-WP-0026/0027 (disclosure
hygiene, governance lockdown)
- reuse.coulomb.social: `capability.authorization.policy-evaluate`,
`capability.iam.key-cape`, `capability.security.iam-tooling-suite`
- binky-control DEC-2026-003 (first consumer: cutover without founder
mechanics)

View file

@ -0,0 +1,270 @@
---
id: WARDEN-WP-0030
type: workplan
title: "Delegation register — make gap-covering interim, visible, and retirable"
domain: infotech
repo: ops-warden
status: finished
owner: grok
topic_slug: custodian
planning_priority: high
planning_order: 30
created: "2026-08-11"
updated: "2026-08-15"
state_hub_workstream_id: "da3367d5-890c-52c6-aa54-1bdd0f277342"
---
# WARDEN-WP-0030 — Delegation register
## Origin
Founder directive 2026-08-11: **ops-warden should work with, but never replace or
duplicate, secrets-engine, tenant-engine, user-engine and other NetKingdom
components.** It is acceptable for ops-warden to cover a gap where needed security
functionality is not yet systematically provided — *provided* the gap is kept in
mind, filled, and given proper governance, after which ops-warden delegates to the
improved component.
The directive is already half-lived and nowhere written. Survey of
`registry/routing/catalog.yaml` on 2026-08-11 (24 entries):
| Execution mode | Count | Meaning |
| --- | --- | --- |
| `warden_executes: true` | 1 | ops-warden's own lane (`ssh-cert-host-access`) |
| `exec_owner:` set | 2 | Delegated — route-primary, proxy-fallback |
| `exec_capable` proxy, no `exec_owner` | 11 | **ops-warden is the de facto front door** |
| route-only | 10 | Pointer, nothing to delegate |
The delegation primitive already exists and works: `exec_owner` / `exec_command` /
`pointer_command` (`whynot-design-npm-publish` → secrets-engine, WP-0019;
`ops-warden-warden-sign-token` → railiance-platform credential broker,
RAILIANCE-WP-0005 T08). It is used by 2 of 24 lanes.
Nothing distinguishes the other eleven — "ops-warden proxies because that is the
right end state" reads identically to "ops-warden proxies because no owner front
door was ever built." Supporting evidence of the doctrine gap:
- `wiki/AccessRouting.md` does not mention secrets-engine at all and has no section
on interim positions.
- `wiki/playbooks/catalog-lane-promotion.md` gates draft→active on the lane
*working*, never on whether ops-warden should be the one running it.
## Goal
Make every ops-warden execution position **explicitly interim or explicitly
permanent**, with the intended owner and blocking condition recorded in the
machine-readable layer — so gap-covering is a tracked, retirable state rather than
silent ownership drift.
Success is not removing proxies. Success is that no proxy exists without an answer
to *"who should own this front door, and what is missing?"*
## Non-goals
- Removing or degrading any working lane. Proxies keep working until the owner's
front door exists and is proven.
- Building the missing front doors. That work belongs to secrets-engine,
tenant-engine, user-engine, and railiance-platform — this workplan produces the
register they need, not their implementations.
- A second catalog or a parallel schema. `delegation:` extends the existing entry
shape; `warden route gaps` composes the existing loader.
## Design constraints
- **Interim is the default.** A lane without a `delegation:` block is treated as
`interim` with an unknown owner, not as settled. Absence must read as a question.
- **Founder classifies, agent drafts.** Which of the eleven are legitimately
permanent is an architecture judgement (`key-cape-oidc-login` plausibly is; the
tenant/workload secret lanes plausibly are not). T02 lands a *drafted*
classification for review; the founder's answer is authoritative.
- **No restating owner procedure.** The register names the intended owner and the
blocker; it does not describe how that owner will implement their front door.
Same pointer-layer discipline as the rest of the catalog.
## Tasks
### T01 — Interim custodianship doctrine
```task
id: WARDEN-WP-0030-T01
status: done
priority: high
state_hub_task_id: "6f88876e-434c-5718-9c8d-ec6bf64ae4aa"
```
Add a doctrine section to `wiki/AccessRouting.md` stating the boundary: the only
lane ops-warden executes with its own authority is SSH issuance; every other
execution position is interim, held because the owning component does not yet cover
the need, and retired to that owner once it does. Name secrets-engine,
tenant-engine, user-engine, railiance-platform and flex-auth as the delegation
targets. Cross-link INTENT.md §9.
### T02 — `delegation:` metadata + backfill
```task
id: WARDEN-WP-0030-T02
status: done
priority: high
state_hub_task_id: "b02e8da6-57ca-5f9f-9405-9b0624498e3a"
```
Extend the catalog entry schema with:
```yaml
delegation:
mode: native | interim | permanent # native = owner already fronts it
intended_owner: <repo> # required unless mode: permanent
blocked_on: <what is missing> # required when mode: interim
reviewed: "YYYY-MM-DD"
```
Backfill all 24 entries. `exec_owner` lanes become `mode: native`.
`ssh-cert-host-access` becomes `mode: permanent` (ops-warden's own lane). The
eleven undelegated proxies are classified per the founder review below — five
`interim` now, six held pending secrets-engine's answer.
**Landed 2026-08-15.** Catalog is now 27 entries. All 27 carry `delegation:`.
- `permanent` (1): `ssh-cert-host-access`
- `native` (10): both `exec_owner` lanes plus the route-only pointers
- `interim` (16): the five founder-classified now; the six held pending
secrets-engine (still unanswered as of 2026-08-15, msg `7d55d332`) recorded as
`interim` / `intended_owner: secrets-engine` / `blocked_on` the generic-vs-per-lane
question — **not** `permanent`; plus five new-since-survey lanes drafted the same
way (`email-connect-transactional` same held question; two AppRoles;
`coulomb-social-runtime-env`; draft `scaleway-bootstrap`)
The six are not guessed permanent. Interim is the default until secrets-engine
answers. Reclassify to `permanent` only if they confirm `exec --catalog` stays
per-lane.
### T03 — `warden route gaps` + conformance test
```task
id: WARDEN-WP-0030-T03
status: done
priority: medium
state_hub_task_id: "f5e0f5af-45d8-5c82-9de2-d640d7d0a1f7"
```
`warden route gaps [--json]` lists interim lanes with intended owner, blocker, and
age since review — the queryable register. Add a routing test asserting every
`exec_capable` non-`warden_executes` entry declares `delegation`, so a new proxy
cannot be added without answering the ownership question. Surface stale interim
entries in the existing drift/stale review cadence.
### T04 — Promotion gate
```task
id: WARDEN-WP-0030-T04
status: done
priority: medium
state_hub_task_id: "b808a749-e1d7-5708-aabf-91732dd76abd"
```
Update `wiki/playbooks/catalog-lane-promotion.md`: draft→active requires a
`delegation` block. If `mode: interim`, the promotion note must state the intended
owner and the retirement condition. Add the matching question to the lane-review
checklist.
### T05 — Publish the register to the owners
```task
id: WARDEN-WP-0030-T05
status: done
priority: medium
state_hub_task_id: "b0188ec4-4860-57c8-8030-45904a132190"
```
Once T02 is reviewed, send the interim register to secrets-engine, tenant-engine,
user-engine, railiance-platform and net-kingdom as a coordination message: here is
what ops-warden currently fronts on your behalf, here is what would let us step
back. This is the artefact that converts a private ops-warden position into a
fleet-visible capability gap.
**Landed 2026-08-15.** Register sent (no secret values):
| To | Message |
| --- | --- |
| secrets-engine | `9c1d753f` — 1 native + 7 interim pending generic-exec answer |
| tenant-engine | `17c851b1` — 2 Binky tenant lanes |
| user-engine | `38aeccd5` — confirm USER_ENGINE_PROXY_SECRET ownership |
| railiance-platform | `e75793e3` — 1 native + 4 interim |
| net-kingdom | `3fea09df` — object-storage-sts remains native/draft |
| key-cape | `28ded56c` — rapp-qonto-keycape-client (founder-classified interim) |
## Acceptance
- [x] `wiki/AccessRouting.md` states the interim-custodianship boundary and names
the delegation targets
- [x] All catalog entries carry `delegation`; `warden route gaps` returns the
interim set with owner + blocker (27 entries; 15 active interim)
- [x] Routing test fails when a proxy lane omits `delegation`
- [x] Promotion playbook gates draft→active on the ownership question
- [x] Founder has reviewed the interim/permanent classification of the eleven
(2026-08-11); the six held lanes stay interim pending secrets-engine, not
marked permanent
- [x] Register delivered to the named owner repos (T05)
## Classification of the eleven (founder review, 2026-08-11)
### The test
An initial draft sorted by *subsystem* (tenant lanes → tenant-engine, workload lanes →
secrets-engine). Inspecting the entries showed that is the wrong axis. Nine of the
eleven share one `auth_method` — "caller's own OpenBao token" (operator OIDC via
key-cape, or a `workload-kv-read-*` policy) — and one `fetch_command` shape,
`bao kv get -field=X <path>`. No owner procedure is duplicated there. Contrast
`whynot-design-npm-publish`, which needs npm config and token injection into a specific
tool; that is a *procedure*, which is why WP-0019 handed it to secrets-engine.
**Test: owner-specific procedure or lifecycle → `interim`. Generic KV read →
ops-warden's thin wrapper is arguably `permanent`.**
### Interim — classify now (5)
| Lane | `intended_owner` | `blocked_on` |
| --- | --- | --- |
| `rapp-qonto-keycape-client` | key-cape | `client_secret_basic` exchange is a key-cape protocol procedure, not a KV read; rotation already `automatable: true`, so key-cape could front it today |
| `binky-company-email-imap` | tenant-engine | Custody at `tenants/binky/...` but rotation owner is `binky-control` — split lifecycle, no front door reconciling it |
| `binky-qonto-api` | tenant-engine | Same split, same tenant |
| `railiance-backup-offsite-lane` | railiance-platform | Rotation is `re-establish`, a multi-step procedure ops-warden only describes |
| `agent-harness-forgejo-deploy` | railiance-platform / agent-harness | `re-establish` plus an alternative host-local key path; two ways in, neither owner-fronted |
### Held pending secrets-engine (6)
`openbao-api-key`, `key-cape-oidc-login`, `issue-core-ingestion-api-key`,
`reuse-surface-hub-write-token`, `openrouter-llm-connect`, `forgejo-admin-api-token`.
These pass the test as `permanent` **only if `secrets-engine exec` stays per-lane and
provisioned.** If secrets-engine intends `exec --catalog <id>` to generalize over
arbitrary OpenBao lanes, the thin-wrapper argument collapses and all six become
`interim` with `intended_owner: secrets-engine` — ops-warden would then be duplicating
a front door the owner provides.
Asked directly (msg `7d55d332`, 2026-08-11): generic or per-lane; if generic, is it
near-term enough to mark now; and are there lanes they would decline, so those can be
marked permanent with the owner's position on record rather than ops-warden's inference.
Supporting detail: for `issue-core-ingestion-api-key`, `reuse-surface-hub-write-token`
and `openrouter-llm-connect`, **production never touches the proxy** — External Secrets
syncs the value into the cluster and the proxy exists for operator verification and
debugging. That weakens the case that they represent a missing front door at all.
**Do not backfill these six until the answer arrives.** A lane wrongly marked
`permanent` bakes in exactly the ownership drift this register exists to catch.
### user-engine
No lane names **user-engine** as owner — it appears only as a consumer inside
`coulomb-social-runtime-env` (`USER_ENGINE_PROXY_SECRET`, rotated at
`user-engine/user-engine-runtime`). Whether user-engine should front that lane itself
is worth confirming.
## See also
- INTENT.md §9 (cover gaps, never silently own them)
- WARDEN-WP-0019 (route to secrets-engine — the pattern this generalizes)
- WARDEN-WP-0028 (tenant secret custody pattern; front door still ops-warden's proxy)
- `history/2026-08-11-delegation-surface-assessment.md`
- `registry/routing/catalog.yaml`, `wiki/playbooks/catalog-lane-promotion.md`

View file

@ -0,0 +1,170 @@
---
id: WARDEN-WP-0031
type: workplan
title: "Calling-side identity for flex-auth, so policy.enabled can flip"
domain: infotech
repo: ops-warden
status: finished
owner: ops-warden
topic_slug: netkingdom
planning_priority: P1
depends_on_workplans:
- WARDEN-WP-0007
related_workplans:
- WARDEN-WP-0009
created: "2026-08-19"
updated: "2026-08-19"
state_hub_workstream_id: "739bad25-2345-5f4f-aaa3-cc4cd8c71f6c"
---
# WARDEN-WP-0031 — Calling-side identity for flex-auth
flex-auth shipped `flex-auth-ops-warden` (FLEX-WP-0016 T01/T02): an
independently rollable in-cluster pin carrying ops-warden's production registry
and policy package, on digest `sha256:138aa347…`, at
`flex-auth-ops-warden.flex-auth.svc.cluster.local:8080`.
It runs `callerAuth.mode: warn`, and it says why in its own logs:
```
caller authentication warning: caller is not authenticated
```
`src/warden/policy.py` posted `/v1/check` with **no `Authorization` header**.
flex-auth authenticates the caller with a Kubernetes TokenReview and binds
`resource.system: ops-warden` to `system:serviceaccount:ops-warden:ops-warden`;
an unauthenticated caller can only be served in `warn`. So the pin cannot
enforce, and per ADHOC-2026-08-17-T01 — `policy.enabled` must not flip anywhere
while `/v1/check` still answers unauthenticated callers — `policy.enabled` stays
false. The gap is ours, not flex-auth's, and this workplan closes it.
Warn is also not A2 evidence: a request that succeeds because failures are
downgraded proves nothing about the enforcing path.
## Ownership
| Concern | Owner |
| --- | --- |
| The pin, its digest, `callerAuth.mode` | flex-auth |
| Sending a caller identity on `/v1/check` | **ops-warden** (this workplan) |
| Choosing the token source on a given host | ops-warden operator |
| `policy.enabled: true` in `warden.yaml` | ops-warden operator |
## Design note — fail closed on identity too
When a caller token is configured but cannot be obtained, `check_sign_policy`
raises under `fail_closed` rather than retrying unauthenticated. Falling back to
an anonymous call is precisely the behaviour that keeps the pin in `warn`; a
gate that silently degrades to the ungated path is not a gate (ADR-0004's choke
point argument, applied to ops-warden as a caller).
## Tasks
```task
id: WARDEN-WP-0031-T01
status: done
priority: high
state_hub_task_id: "d3b7c701-bcdd-53f9-aa72-6f289bf5909b"
```
**Caller identity on the outbound policy call.** `policy.caller_auth` in
`warden.yaml` (`mode: none | file | env | command`, `token_path`, `token_env`,
`command`, `audience`); `src/warden/caller_identity.py` resolves the token at
call time and never caches, logs, or echoes it (ADR-0002); both
`check_sign_policy` and `check_fetch_policy` attach `Authorization: Bearer …`.
Whitespace-bearing and empty tokens are rejected before the call, because
flex-auth rejects them outright.
Done 2026-08-19. `mode: none` remains the default, so behaviour is unchanged
until an operator opts in. Tests in `tests/test_policy.py`.
```task
id: WARDEN-WP-0031-T02
status: done
priority: high
state_hub_task_id: "b77b3c80-a168-564a-9b7f-3063aefc3c2e"
```
**Readiness gate.** `scripts/check_policy_caller_identity.py` — read-only:
config loads, mode is not `none`, a token is actually obtainable, and with
`--url` a live `/v1/check` against a port-forward of the warn pin. Prints the
token's length and a truncated SHA-256 fingerprint only, so its output is safe
to paste into a handoff. Exit 0 ready / 1 not ready / 2 bad input. Distinguishes
401 (token not accepted — audience or binding) from 403 (authenticated but not
allowed to represent `system: ops-warden`).
Done 2026-08-19.
```task
id: WARDEN-WP-0031-T03
status: done
priority: medium
state_hub_task_id: "4245155e-6c71-5425-b574-11f61e1d4461"
```
**Docs.** `examples/warden.production.example.yaml` gains the `caller_auth`
block with both realistic sources, and its `flex_auth_url` is corrected — the
example pointed at `flex-auth.flex-auth.svc.cluster.local`, a Service that does
not exist. `wiki/PolicyGatedSigning.md` gains the caller-identity section and
the flip sequence.
Done 2026-08-19.
```task
id: WARDEN-WP-0031-T04
status: done
priority: high
state_hub_task_id: "f3834af7-2a08-51dd-bf31-8ce8550de699"
```
**Pick the token source and prove it against the warn pin.** Operator work on
the real host: `kubectl create token` (workstation) or a projected token
(in-cluster PEP), then
`python scripts/check_policy_caller_identity.py --url http://127.0.0.1:19090`
against a port-forward. Expect `effect=allow` for `agt-state-hub-bridge` while
the warn log stops printing `caller authentication warning` — the absence of
that line, not the allow, is the evidence.
Done 2026-08-19. Source is `mode: command` — `kubectl create token ops-warden
-n ops-warden --audience flex-auth --duration 10m` against the railiance01
cluster (tunnel `k3s-api-railiance01`, local `16444`; `16443` was CoulombCore's
k3s, a different cluster, which is why a `system:masters` cert 401s there —
not a port collision, as first reported).
`deploy/kubernetes/caller-identity.yaml` creates the Namespace and
ServiceAccount the binding names — no RBAC, `automountServiceAccountToken:
false`; it exists only to be TokenReviewed. Gate exits 0 live:
`HTTP 200, effect=allow, decision:f3f7c88f9585582a`, and the pin's
`caller authentication warning` count held at 4 across two authenticated runs.
Evidence: `history/2026-08-19-flex-auth-caller-identity-evidence.md`.
```task
id: WARDEN-WP-0031-T05
status: cancel
priority: high
state_hub_task_id: "3f6dc609-89db-52eb-a6f9-d2fd271be821"
```
**Sequence the flip.** Only after T04: tell flex-auth to set
`callerAuth.mode: enforce` on `flex-auth-ops-warden` (their FLEX-WP-0016 T03),
re-run the gate against the enforcing pin, and only then set
`policy.enabled: true` with `fail_closed: true`. Flipping before enforce buys
nothing; flipping before T04 401s every `warden sign`.
**Deferred 2026-08-19 under `ADR-0006`** — not blocked, decided against for now.
flex-auth did enforce (FLEX-WP-0016 T03, Helm rev 2): the pin runs
`--caller-auth-mode enforce`, the gate exits 0 against it
(`HTTP 200, effect=allow, decision:f3f7c88f9585582a`), and an anonymous
`/v1/check` is 401. Everything needed to flip was in place.
It was not flipped. `policy.enabled` is a single repo-wide boolean, and with
`fail_closed: true` it makes flex-auth a hard dependency of every `warden sign`
— including the certs the ops-bridge tunnels depend on, one of which carries
the policy call itself. Uniform enforcement across an estate being actively
rebuilt would harden the access needed to perform the rebuild. Enforcement
belongs to a zone, not to the repo; that is `ADR-0006`, and the model is
`WARDEN-WP-0032`. This task resumes as WARDEN-WP-0032-T05.
Also shipped while proving the flip: the gate URL is now a managed ops-bridge
tunnel `flex-auth-ops-warden-railiance01` (`-L 19090:10.43.1.165:8080`) instead
of a hand-run `kubectl port-forward`.

View file

@ -0,0 +1,429 @@
---
id: WARDEN-WP-0032
type: workplan
title: "Adopt security zones as a consumer — retire the global policy.enabled"
domain: infotech
repo: ops-warden
status: finished
owner: ops-warden
topic_slug: netkingdom
planning_priority: P1
depends_on_workplans:
- WARDEN-WP-0031
created: "2026-08-19"
updated: "2026-08-22"
state_hub_workstream_id: "38c6a5f3-fb0d-5230-be85-f9e3ffc850f6"
quality_dod: DoD-Ok
quality_dod_at: "2026-08-22"
quality_dod_by: codex
quality_dod_note: "All tasks are done; the global switches and trust-zone source are retired, workload references are explicit, the owner policy is total, declarations validate, live caller identity is re-proven, and the full suite passes."
---
# WARDEN-WP-0032 — Adopt security zones as a consumer
`ADR-0006` defers `policy.enabled: true` until enforcement can be scoped to a
zone. **The zone model itself is no longer ops-warden's work.** It moved to
`zone-engine` as `ZONE-WP-0001` on 2026-08-19, where it belongs under `ADR-0005`
— ops-warden implements one lane narrowly and routes the rest, and an
estate-wide enforcement model is not a lane it should absorb.
What stays here is the consumer side: ops-warden was the repo whose deferred
flip exposed the gap, it holds the controls the model must be able to express,
and it is the first consumer the model has to satisfy.
## What ops-warden owes zone-engine
Inputs, not designs. `ZONE-WP-0001-T02` derives the model from the real estate,
and most of that estate is ops-warden's:
- **27 routing catalog lanes** (`registry/routing/catalog.yaml`) with `risk`,
`status`, and `delegation` already on each — the likeliest membership inputs.
- **The actor inventory** (`adm` / `agt` / `atm`) and its TTL policy.
- **The three posture axes already shipped** — environment and maturity
(WP-0015), `organization_posture` (WP-0029) — including the honest answer to
whether `organization_posture` should be folded into zones rather than run
beside them.
- **Three controls the model must be able to express**: the flex-auth pre-sign
gate (`policy.enabled` + `fail_closed`), the agent read-boundary on
`risk: high` lanes (`ADR-0004`), and the `warden plan` escalation verdicts.
- **The compiled-registry path** (`scripts/build_flex_auth_registry.py`) — how
membership can reach flex-auth without a lookup in a latency-critical decision
path.
## Tasks
```task
id: WARDEN-WP-0032-T01
status: done
priority: high
state_hub_task_id: "b03a5caa-0bb5-5cdd-bb99-32c694b0da29"
```
**Hand the estate inputs to `ZONE-WP-0001-T02`.** Not a design proposal — the
lanes, actors, axes, and controls above, with the exceptions ops-warden already
knows do not fit cleanly (the interim delegation lanes from WP-0030 are the
obvious candidates: a lane covered on someone else's behalf may not sit in the
same zone as one ops-warden owns outright).
Include the `organization_posture` fold-in question honestly, including the case
against keeping it.
**Answered by net-kingdom 2026-08-19** (canon owner of `tenancy-posture_v0.1`,
answering `ZONE-WP-0001-T01`): **do not fold it in, and do not put it in a
per-repo declaration under any key.** `organization_posture` is a fleet-wide,
time-varying scalar describing the *estate*, not a property of a declaring
service; a per-repo copy of a global goes stale in as many places as there are
repos. It stays what WP-0029 made it — an input to stance selection that the
zone model reads. Hand it over as an input, not as a candidate axis. Environment
posture and `M0`–`M3` are per-workload and remain genuinely composable; those two
are still open for `ZONE-WP-0001-T02`.
**Done 2026-08-21.** Every input listed above is now carried in `ZONE-WP-0001`,
and `T02` there is `done` — which is the acceptance condition for this task, since
what was owed was inputs rather than a design.
What actually reached them: the 27 graded lanes with `risk`/`status`/`delegation`
(T05), the `M0`–`M3` ladder in `registry/policy/security-posture.yaml` — which
`ZONE-WP-0001-T02` adopted as the maturity ladder rather than
`.repo-classification.yaml` `category`, on the evidence that the latter grades a
production SSH CA below a documentation repo — the three controls, and the
compiled-registry path including the dormant `trust_zone` constant at
`scripts/build_flex_auth_registry.py:77`.
Two inputs were handed over as corrections to ops-warden's own earlier claims,
which is the part worth recording. `organization_posture` was offered as a
candidate axis and net-kingdom refused it (above); and ops-warden asserted no
registry carried a workload join key, which was true of its own catalog and false
of the estate — `rapp-*/declarations/rapp.yaml` carries eight
`workload_identity` declarations. `scripts/report_workload_join.py` then measured
the join rather than asserting it: **1 of 27 lanes** matches a declared workload.
That number is `ZONE-WP-0001-T03`'s critical path, and it came out of correcting
this repo's own input rather than out of supplying a new one.
```task
id: WARDEN-WP-0032-T02
status: done
priority: high
state_hub_task_id: "b3f41c85-a293-58ad-ac27-9f8110c51266"
```
**Replace `policy.enabled` with a zone-aware control.** Waits on
`ZONE-WP-0001-T03` and `T05`. Reads the zone of the actor being signed for and
that zone's failure mode. Ship deprecation and migration in the same change —
leaving both is the second-source-of-truth failure `ADR-0001` exists to prevent.
Re-run `scripts/check_policy_caller_identity.py` before enabling anything: the
WP-0031 evidence (`decision:f3f7c88f9585582a`, 2026-08-19) will be stale, and
re-establishing it is cheap precisely so this is a re-check rather than a re-do.
This closes `WARDEN-WP-0031-T05`.
**Amended by flex-auth 2026-08-19, reviewing `ZONE-WP-0001` as the PDP.** Two
constraints that change what this task can build, both argued in full at
`ZONE-WP-0001-T03` and `T04`:
1. **The zone-aware control splits across the boundary, it does not move.**
flex-auth will read compiled zone *membership* from the registry and apply the
per-zone *stance* from its policy package, returning an effect plus an
advisory annotation. What stays on the ops-warden side is the axis flex-auth
structurally cannot carry: **`fail_closed` is not expressible by a PDP.**
Fail-open describes what `warden sign` does when flex-auth is *unreachable* —
no decision is rendered, so no compiled data and no policy rule can reach it.
So the replacement for `policy.enabled` is not one zone-derived boolean
either; it is (a) stance, which arrives *in the decision*, and (b) failure
mode, which stays a local ops-warden setting declared per zone. Plan for two,
or T02 will be built expecting flex-auth to answer something it cannot.
2. **The membership compiler is ops-warden's to change, and it has a name
collision to resolve first.** `scripts/build_flex_auth_registry.py:77` already
emits `"trust_zone": "platform"` as a hardcoded constant on every
ssh-certificate resource. It is a first-class field on flex-auth's `Resource`,
it is surfaced into rego input, and **no policy package reads it**. Before
compiling zone membership, either retire that constant or deliberately
repurpose it — do not add `security_zone` beside a dormant `trust_zone` and
leave the next reader to guess which is real. That is the `ADR-0001`
second-source-of-truth failure in miniature, inside a generated artifact.
Good news for T03's cost: **no flex-auth registry schema change is required.**
`metadata`, `labels` and `attributes` already flatten into the rego input, so the
compiler can emit membership today.
**Consumer implementation landed in the working tree 2026-08-22.** The config
loader rejects the retired `policy.enabled` and global `policy.fail_closed`
keys. `scripts/build_flex_auth_registry.py` now emits the normative
`workload_id`, `security_zone`, `security_zone_declared`,
`security_zone_admission`, `security_zone_reason`, and
`security_zone_revision` attributes; the dormant `trust_zone` field is gone.
The PEP reads compiled resource membership and applies its local per-zone
failure mode. Sign and audit records retain the zone, selected failure mode,
outcome, and decision id.
The legacy `--no-policy` proxy switch remains only as a rejected compatibility
flag. It can no longer bypass the gate: unresolved credential targets take the
explicit `unknown` profile and its configured failure mode.
**Done 2026-08-22.** flex-auth package v2 (commit `e521e7b`) supplies total
stance over every v0.1 zone plus `unknown`, preserves native enforcement for
`not-applicable`, and returns `audit_only` for advisory decisions. The required
live caller check passed through the existing tunnel with command-mode caller
identity and decision `decision:f3f7c88f9585582a`. The operator config was then
migrated from the two rejected global keys to `zone_registry_path`, and the
same live check passed against that real config. Full repo tests pass and
sign/audit evidence records zone, failure mode, outcome, and decision id.
```task
id: WARDEN-WP-0032-T03
status: done
priority: medium
state_hub_task_id: "d04a737b-ecdf-5747-ba25-20dadd99d3bc"
```
**Declare ops-warden's zones** in the format `ZONE-WP-0001-T05` settles,
alongside the existing posture descriptors.
**Amended by net-kingdom 2026-08-19 — the carrier file is already settled, only
its contents are open.** Canon Decision 5.6 (`tenancy-posture_v0.1` draft-9)
rules that zone membership is declared in **`tenancy.yaml` under a reserved
top-level `zones:` key**, sibling to `tenancy:` and `provider:` and never inside
`tenancy.current`. §5.4 makes that file the repo's single posture declaration
surface, and a second root file would be the second-source-of-truth failure
`ADR-0001` exists to prevent — in ops-warden's own idiom. The key is reserved and
deliberately unconstrained in `net-kingdom/canon/schemas/tenancy-posture_v0.1.schema.json`,
so a combined declaration validates today.
Note the consequence for this repo: **ops-warden does not currently have a
`tenancy.yaml`.** Declaring zones means writing one, which means declaring the
six-axis posture vector too. That is a real and probably overdue cost, not an
accident of this amendment — canon would rather ops-warden declare both
accurately than declare a zone with no posture beside it.
Note also *why* stance is not something this declaration may set: canon Decision
5.6 rules that a declarer must not be the party that sets the stance, or an
accurately declared `exempt` becomes conformant *and* exempt. ops-warden declares
which zone a lane or actor is in. The stance of the pre-sign gate in that zone is
flex-auth's policy package; the failure mode is ops-warden's own setting, per
flex-auth's T03 amendment. Per flex-auth's T05 amendment, this
declaration must also say whether a zone rides the **actor** (a flex-auth
subject) or the **lane** (a flex-auth per-actor resource) — in the compiled
registry an actor is both, and the compiler needs to be told which record
carries membership. Carry the conformance rule:
*accuracy, not altitude*. Declaring a stricter zone than can be evidenced is the
failure mode that looks like progress.
**Done 2026-08-22.** `tenancy.yaml` now declares ops-warden as an independently
governed operational execution unit with its Kubernetes service-account
binding, and admits it to `z1-operational` at `M1`, `medium`, `internal`.
`docs/evidence/security-zone-admission-2026-08-22.md` records why this is the
highest evidenced admission rather than an aspirational `M2`. The declaration
passes net-kingdom's current `tenancy-posture_v0.1` validator.
```task
id: WARDEN-WP-0032-T04
status: done
priority: medium
state_hub_task_id: "c7879127-3dba-55c0-853c-a10775736873"
```
**Amend `ADR-0006`.** Once zones exist and enforcement scoping is owned by
`zone-engine`, ADR-0006 must say that ops-warden *follows* the model rather than
owning it — a superseding record, never an in-place edit. Update `SCOPE.md`,
`wiki/WorkloadSecurityPosture.md`, and `wiki/PolicyGatedSigning.md` with it.
**Done 2026-08-22.** ADR-0006 is marked superseded without rewriting its
decision. ADR-0009 accepts `security-zones_v0.1` as a consumer, records the
PDP/PEP split and unknown handling, and is indexed as its successor. `SCOPE.md`,
`wiki/WorkloadSecurityPosture.md`, `wiki/PolicyGatedSigning.md`, configuration
guidance, access guidance, and affected playbooks now describe the zone-aware
control and do not instruct operators to use a global bypass.
```task
id: WARDEN-WP-0032-T05
status: done
priority: high
state_hub_task_id: "1d968627-83f4-59cd-84ca-0f9f35e435ff"
```
**Grade the five exposed lanes now — do not wait for the model.**
`RISK-F-0003`: `is_high_risk` is `risk == "high"` and `risk` is optional, so the
14 ungraded lanes never reach the agent read-boundary. Five are `exec_capable`
and can therefore stream a value to an agent session: `openbao-api-key`,
`whynot-design-npm-publish`, `key-cape-oidc-login`,
`issue-core-ingestion-api-key`, `reuse-surface-hub-write-token`.
The durable answer is a maturity-derived default (`ZONE-WP-0001-T03`), and it is
the right answer. It is also months away, and this is a live control gap in a
shipped ADR. Grade these five explicitly, then the remaining nine.
Grading is judgement, not backfill — each lane's grade should be justified in the
entry, and the operator should sanction the high/standard calls rather than
having them inferred. Verify separately whether OpenBao's
`agent-high-risk-boundary` policy covers these paths; `RISK-F-0003` deliberately
does not assume it does, because those paths were graded by the same omission.
**Done 2026-08-19, operator-sanctioned.** All 14 ungraded lanes graded on merit,
each with its justification in the entry: 17 `high`, 10 `standard`, **0
ungraded**. `warden access <lane> --fetch` with `WARDEN_AGENT_ID` set now exits 7
on lanes that were silently outside the control an hour earlier.
Graded on merit, not defensively. A first pass marked
`issue-core-ingestion-api-key` and `reuse-surface-hub-write-token` `high`; the
existing test `test_high_risk_lanes_classified` asserted the opposite and was
right — ordinary internal workload secrets are `standard`. Both were regraded
down. `high` means disclosure into a logged context is damaging beyond what
rotation recovers: provider keys with spend, admin PATs, tenant commercial data,
supply-chain publish rights. `inter-hub-bootstrap-ssh` is `high` **conservatively**
— ops-warden could not establish that no key material moves in the envelope, and
that is recorded in the entry so it is regraded with evidence rather than assumed
down.
The rule is now `ADR-0007`: build-stage permissiveness stops at credential
disclosure. Not yet verified: whether OpenBao's `agent-high-risk-boundary`
policy covers these paths (T06).
```task
id: WARDEN-WP-0032-T06
status: done
priority: medium
state_hub_task_id: "4294084c-bf3f-5aa6-b84d-5173121882ff"
```
**Make absence impossible, once the model says what absence means.** Waits on
`ZONE-WP-0001-T03`. Either invert the default (absent `risk` resolves through the
lane owner's maturity, per the operator direction) or require `risk` at catalog
load and in CI. Whichever lands, the failure mode to kill is the current one:
adding a lane without a grade silently places it outside a control, with nothing
at load or in CI noticing.
Feed back to `ZONE-WP-0001-T03` whether ops-warden can supply the join it needs —
today no lane references a workload or an environment, so the `M0`–`M3` ladder
has nothing to attach to from this side.
**Enforcement half done 2026-08-20.** It did not need the zone model: `ADR-0007`
already decided absence is a defect, which is enough to make the code fail safe
and to gate CI.
The real mechanism turned out to be sharper than `RISK-F-0003` described.
`is_high_risk` was `risk == "high"`, but `risk` was **not** absent at the model
layer — `RouteEntry.risk` carried a dataclass default of `"standard"`. So an
omitted grade was not unhandled; it was actively resolved to the permissive
value. Fail-open by construction, which is why nothing warned.
Now: the default is `"ungraded"`, and `is_high_risk` returns true for anything
not in an explicit low-risk vocabulary (`standard` / `low` / `accepted`). An
omitted grade **and** a grade from a newer catalog both resolve to high, so the
boundary fails safe in both directions. `is_graded` exposes the distinction, and
`test_every_repo_catalog_lane_is_explicitly_graded` is the CI gate that stops an
ungraded lane being committed. Four regression tests cover it.
`accepted` is in the low-risk vocabulary deliberately, ready for the
maturity-derived default: an experimental-context lane may be explicitly
accepted, which is a graded decision rather than an omission.
**Second layer checked 2026-08-20 — it does not cover them.** The OpenBao side
was compared statically (policy file vs catalog): `agent-high-risk-boundary`
denies 5 data paths covering **6** of the **17** high-risk lanes. Eight
high-risk lanes with concrete KV paths are not denied — and **four of those were
already graded `high` before the regrade**, so the divergence is pre-existing
rather than something the grading introduced. It had simply never been
comparable before.
This matters more than the ops-warden half: `warden access` exits 7 for all 17,
but that only protects the ops-warden path. The OpenBao policy is what protects a
direct `bao kv get`, which is the actual 2026-07-16 vector.
Routed to `risk-nexus` as **`RISK-F-0004`**, `fix_owner: railiance-platform` —
the policy is theirs, and ops-warden does not amend another repo's control.
**Live confirmation done 2026-08-21 — and the blocker was not real.** The
token was not expired: `bao token lookup` returned a valid `platform-admin`
token from an OIDC login, and `bao policy read agent-high-risk-boundary`
succeeded. No `bao login` was needed. Worth recording as a small instance of the
lesson `.claude/rules/finding-routing.md` already states — *re-read a blocker
before trusting it*. This one was a stale claim about the world, carried for a
day in both this workplan and `RISK-F-0009`.
The verification script also did not exist. It was described here as "ready",
and nothing was committed. `scripts/check_agent_read_boundary.py` now exists,
with tests, and is the invariant `RISK-F-0009` asked for rather than a one-off
audit: exit 1 when any high-risk lane has no corresponding deny. It prefers the
deployed policy and falls back to the file only with a loud warning, because
deployment drift is the thing it exists to catch. Capabilities-only by
construction — it reads the policy document and lane metadata, never a secret
value, and never mints a token.
Three results, one against ourselves:
1. **Coverage confirmed at 6 of 17** against the live policy — the headline was
right and is no longer inferred from a checkout.
2. **Six lanes uncovered, not eight.** `RISK-F-0009` counted `openbao-api-key`
(a `<domain>/<workload>/<bundle>` pattern) and `ops-warden-warden-sign-token`
(a broker grant, not KV) among the concrete uncovered paths, while its own
prose said the first was a pattern. Neither can be expressed as a deny.
Corrected: 6 covered, 6 uncovered, 5 with no address.
3. **The deployed policy has drifted from the file.** The file denies
`platform/workloads/core-hub/runtime`; the server does not. No ops-warden
lane maps there, so our numbers are unchanged — it matters as evidence, since
`RISK-F-0009` named exactly this divergence as its unconfirmed risk.
Still not established, and not ops-warden's to establish: whether any agent
token carries `agent-high-risk-boundary`, and whether any carries it together
with a `workload-kv-read-*` policy. Confirming that means minting or inspecting
tokens — a write against railiance-platform's OpenBao. Exposure stays theoretical
to the same degree as before.
**Platform half closed 2026-08-22.** `RAILIANCE-WP-0022` consumed the generated
high-risk path input and deployed the completed deny policy. The platform-owned
invariants report **19 high-risk lanes, 14 concrete entries, 0 uncovered**. A
dedicated `coding-agent-railiance-platform` AppRole then proved deny-wins with
exactly `agent-high-risk-boundary` plus one workload read policy: data access was
denied while metadata remained readable; its single-use test identity was
self-revoked and no test tokens remained. Decision `f0955252` records the
operational identity. No KV value was read.
The local invariant agrees against the platform policy file: 19 high-risk lanes,
14 concrete entries covered, 5 without a concrete address, 0 uncovered. This
session could not independently read the deployed policy, so live-state evidence
is the platform-owned readback above rather than a claim inferred from the local
file.
**Done 2026-08-22.** The settled model makes the two layers complementary:
catalog CI continues to require an explicit `risk` field so omission cannot
silently weaken the agent boundary, while `RouteEntry.risk_for_zone` implements
the normative maturity-derived fallback for consumers evaluating unresolved or
future inputs (`z0` + satisfied + synthetic-only → `standard`; satisfied `z3`
→ `critical`; everything else → `high`). An explicit catalog grade always wins.
The platform deny policy and its live proof above close the direct-OpenBao half.
```task
id: WARDEN-WP-0032-T07
status: done
priority: medium
state_hub_task_id: "6b4bbbed-2864-5fe9-82be-22ff9543f6f4"
```
**Adopt Repo Manager's workload-reference owner interface.** Accepted with one
documented amendment against repo-manager revision `890f3b0`: managed
deployables use the exact `(rapp_id, workload_identity.name)` pair and optional
deployable name; an independently governed operational execution unit may use
an exact local `tenancy.yaml` declaration. Native actions, actors, grants,
patterns, and non-workload resources are explicitly `not-applicable`; unresolved
workload-applicable references remain `unknown`. No path, owner name, or
repository-name inference is allowed.
Every one of the 27 catalog lanes now carries an explicit `workload_ref`: 3
resolved (2 exact managed RAPP references and ops-warden's direct operational
declaration), 17 unknown with reasons, and 7 not-applicable with reasons.
`scripts/report_workload_join.py` resolves only those explicit references,
Repo Manager validates the interface and resolves both managed tuples exactly,
and regression tests reject malformed or inferred joins. This incorporates
net-kingdom's draft-12 correction received 2026-08-22: local tenancy declaration
is not an escape hatch for a managed running deployable.
## Related
- `zone-engine` `ZONE-WP-0001` — the model, and where this work is led from
- `ADR-0006` — enforcement is zone-scoped, never a global flag
- `WARDEN-WP-0031` — the deferred flip and its readiness evidence
- `net-kingdom` `tenancy-posture_v0.1` draft-9 Decisions 5.6, 8.4.1, 8.4.2 — canon's
answer to `ZONE-WP-0001-T01`; enforcement stance is a sibling standard, not a
seventh axis, and the declaration surface is `tenancy.yaml`

View file

@ -0,0 +1,344 @@
---
id: WARDEN-WP-0033
type: workplan
title: "Native lane handoff — review secrets-engine's catalog admission, and fix what it exposed"
domain: infotech
repo: ops-warden
status: finished
owner: ops-warden
topic_slug: netkingdom
planning_priority: P1
depends_on_workplans:
- WARDEN-WP-0030
created: "2026-08-21"
updated: "2026-08-28"
state_hub_workstream_id: "4627d89b-4b00-562a-81e9-76e96f90fa7e"
---
# WARDEN-WP-0033 — Native lane handoff
On 2026-08-20 `secrets-engine` accepted five of ops-warden's seven interim
proxy lanes for catalog admission (`SECRETS-WP-0006`, decision
`ae676382-1826-4e04-aa4e-bb77990c7a0d`), rejected two with reasons ops-warden
agrees with, drafted the five entries itself rather than waiting for the offered
contribution, and asked for a line-by-line review of the non-secret metadata.
In the same hour `railiance-platform` closed the `RISK-F-0009` coverage gap and
asked ops-warden for two things back.
This workplan is the response to both, plus the defect the review turned up in
ops-warden's own grading model — which is the most consequential item here and
would not have been found without the second pair of eyes.
## Why a workplan and not an adhoc
Four counterparties are waiting, one deliverable is a **cross-repo interface**
another repo will consume on a schedule, and the review carries approval
semantics — `secrets-engine` holds interim proxy ownership open until each lane
has approved native verification. The convention reserves adhocs for low-risk
work completed directly; this is none of those.
## Tasks
```task
id: WARDEN-WP-0033-T01
status: done
priority: high
state_hub_task_id: "154f2f03-387d-5fe6-a0f5-1929c46a2bd8"
```
**Review the five drafted catalog entries and reply.**
`issue-core-ingestion-api-key`, `reuse-surface-hub-write-token`,
`openrouter-llm-connect`, `forgejo-admin-api-token`,
`email-connect-transactional` at `secrets-engine@784be97`.
Review the axes they asked for: owner-repo coordinates, field grouping, workload
consumers, rotation/deactivation owners, delivery intent. Verify claims against
the authoritative `railiance-platform` CCRs — **never against a secret read**;
`bao kv get` on any of these paths is the 2026-07-16 vector and three of the five
are `risk: high`.
Accept their two rejections. `openbao-api-key` is a routing template rather than
one lane and `key-cape-oidc-login` belongs to key-cape — that is `ADR-0005`
applied to ops-warden by someone else, correctly.
```task
id: WARDEN-WP-0033-T02
status: done
priority: high
state_hub_task_id: "6996d07f-63bb-5708-a171-68c4b1bbddde"
```
**Correct ops-warden's per-path risk grading — the defect T01 exposed.**
`secrets-engine` graded `issue-core-ingestion-api-key` **high**; ops-warden grades
it **standard**, and deliberately: WP-0032-T05 regraded it *down* on 2026-08-19,
operator-sanctioned, because `test_high_risk_lanes_classified` asserted standard
and was judged right — "ordinary internal workload secrets are standard".
That reasoning was incomplete, and CCR-2026-0002 says so explicitly: the path
carries **both** `ISSUE_CORE_API_KEY` **and** `GITEA_BACKEND_TOKEN`, a Forgejo
backend token, and the CCR records keeping both as a deliberate field-set
decision.
**The root cause is structural, not a single bad call.** ops-warden grades a
*lane* by its headline field — the catalog carries one `fetch_command` naming one
field and no `fields` list at all — but the disclosure unit is the **path**: a
`bao kv get` returns every field stored there. So a lane can be graded on its
least dangerous contents. `ADR-0007` says every lane carries an explicit grade;
it does not say the grade must cover everything a read of that path would
disclose, and it should.
Fix the grade, then fix the model that produced it: record `fields` per lane and
grade on the union. Check the other 25 lanes for the same shape.
**Done 2026-08-21 — and it was two lanes, not one.** While verifying
`reuse-surface-hub-write-token` I told secrets-engine their second field was
unevidenced and asked them to confirm it. It was evidenced: `CCR-2026-0005`'s
`kv_path` block lists both fields and its notes describe the HMAC's alignment
with Forgejo org webhook id=1. A truncated grep, and a retraction sent within
twenty minutes so they would not spend time on it. That lane was under-graded on
exactly the same reasoning, and both had been regraded *down* in the same T05
pass — which is what makes this a systematic flaw rather than one bad call.
Blast radius checked and bounded: of the 8 remaining `standard` lanes, 6 have no
KV path at all (SSH, policy check, tunnel, principals, issue-sink, key-cape
login) so nothing is disclosed by a read. Two do have paths and no CCR field
evidence — `rein-openweights-openrouter-approle` and `coulomb-social-runtime-env`
(a whole k8s env Secret, which by shape almost certainly holds several fields).
Deliberately **not** regraded: `ADR-0008` §3 says an unknown field set is stated
rather than assumed, and grading is operator-sanctioned judgement, not backfill.
They are the open item on this task.
Shipped: both regrades with their evidence and the superseded reasoning kept
inline, `fields` recorded with its CCR source, the test that had been holding the
wrong answer inverted with an explanation of why it was wrong to trust it, and
**`ADR-0008`** — a lane's grade covers every field its path discloses. That is a
new binding rule, so it is a record rather than a wiki note.
```task
id: WARDEN-WP-0033-T03
status: done
priority: high
state_hub_task_id: "a736f983-94da-5a4a-aaf9-5114485518a6"
```
**Emit the generated high-risk data-path artifact for `railiance-platform`.**
They asked for a versioned artifact of concrete high-risk data paths
(`id` + `data_path` + catalog revision) to consume instead of hand-maintaining
the deny list — the half of the `RISK-F-0009` fix ops-warden explicitly declined
to build *for* them. Emitting the artifact is not the same as amending their
policy: they still own what to deny. That boundary is the point of `ADR-0002`
and must survive this task.
Must be generated, never hand-edited, and must carry the catalog revision so a
consumer can tell what it was derived from. Depends on T02 — publishing the deny
set before fixing the grades would ship the wrong list to a consumer who will
apply it.
**Done 2026-08-21.** `scripts/emit_high_risk_paths.py` →
`registry/generated/high-risk-data-paths.yaml`: 19 high-risk lanes, 14 concrete
data paths, 5 with no single KV address listed separately so a consumer does not
read the absence as an omission. Carries `catalog_revision`, its date, and a
`catalog_dirty` flag — a revision that does not describe the file it came from is
worse than none.
The T02 dependency was not theoretical. The two regrades land in this artifact,
and `check_agent_read_boundary.py` now reports **2 uncovered** against a policy
railiance-platform closed to 0 yesterday. Emitting before regrading would have
handed them a list that confirmed a coverage claim that had just stopped being
true.
`fields` is emitted as `null` where no CCR declares the set, never as a
single-element guess — `ADR-0008` §3. Two CI tests: the artifact must be current,
and every high-risk lane must appear in it, so a lane graded high after the last
emit cannot silently fail to reach the consumer.
Boundary held: the header states this is an input and not a policy, and that
railiance-platform may deny more, deny less, or dispute a grade (`ADR-0002`).
```task
id: WARDEN-WP-0033-T04
status: done
priority: medium
state_hub_task_id: "5acac140-a586-5db3-b231-bbf236710786"
```
**Route the coding-agent issuance identity question.**
`RAILIANCE-WP-0017` is blocked on defining a distinct coding-agent issuance
identity and proving deny-wins when that identity is combined with workload read.
They asked ops-warden to route ownership.
Route it; do not absorb it (`ADR-0005`, `ADR-0003`). ops-warden owns actor
identity for `adm`/`agt`/`atm` **SSH certificates** and nothing else — a coding
agent's OpenBao issuance identity is an IAM question. The likely owner is
`key-cape`, with `user-engine` and `zone-engine` both plausibly involved. Say so
with reasoning rather than guessing, and record the answer either way.
Note the interaction with `ADR-0004`: the agent read-boundary already depends on
`WARDEN_AGENT_ID` being set, which is an honour-system marker on the ops-warden
side. A real issuance identity is what would make that boundary hold on the
OpenBao side too, so ops-warden is an interested consumer, not a bystander.
**Routed 2026-08-21 to `key-cape` (msg 903b2223); waiting on accept or refuse.**
Reasoning given: it is an identity and issuance question about a principal
authenticating to OpenBao, which is key-cape/Keycloak's. The precedent is an hour
old and runs the same direction — `secrets-engine` declined `key-cape-oidc-login`
as ops-warden's to hand them, on the grounds that login and identity-token
issuance stay with key-cape. Absorbing this would contradict agreeing with them.
Named the adjacent parties explicitly rather than leaving them to inference:
`zone-engine` has an interest (a coding-agent identity is a strong candidate zone
subject) and `user-engine` is **not** involved — this is a machine principal, not
an end-user account. Stated so nobody concludes it by elimination.
Asked for a refusal-with-pointer as an equally good answer. The failure mode to
avoid is the 2026-08-17 one recorded in `.claude/rules/finding-routing.md`:
ops-warden answered a question well and never routed it, and another repo ended
up filing it.
**Followed up 2026-08-23 after the immediate enforcement gap closed.**
`RAILIANCE-WP-0022` no longer waits for this ownership decision: the platform
owner deployed a dedicated coding-agent AppRole, proved deny-wins when it is
combined with workload read, and identified an exact-bound KeyCape JWT role as
the migration target. That makes the remaining question narrower and more
important rather than obsolete: who owns issuance of the durable
KeyCape/Keycloak-backed machine identity?
Direct follow-up `d25d4604-5713-42a8-8e3a-1ea31b5a5cc7` asks `key-cape` to
accept that target identity with an authoritative workplan/interface, or refuse
and name the actual owner. T04 remains `wait` until one of those two answers is
recorded; the live AppRole is operational evidence, not an ownership answer.
**Answered — accepted. Closed 2026-08-28.** `key-cape` accepted issuance
ownership in `KEY-WP-0009-T03` (`status: done`, 2026-08-23): the non-secret static
registration `codex-railiance-platform` is published in
`key-cape/config/service-clients.example.yaml` — subject
`service:codex:railiance-platform`, tenant `tenant:coulomb`, role `coding-agent`,
scope `openbao:login`, `client_credentials`, 15-minute lifetime — with the
service-auth claims, renewal, expiry and failure semantics in
`key-cape/docs/openbao-service-auth-contract.md` (`T02`). The split is exactly the
one the routing asked for: KeyCape owns JWT issuance and client disablement,
railiance-platform owns the exact-bound OpenBao role and policy, OpenBao owns
enforcement, and no secret value enters either repository.
**The answer was found by reading their repo, not by receiving it.**
`KEY-WP-0009-T04` records replying to ops-warden; no such message exists in the
ops-warden inbox — 0 messages from `key-cape`, read or unread. The acceptance had
been sitting done for five days while this task sat `wait` on it.
That is the T05 lesson landing on T04: a blocker is a claim about the world at a
date, and this one had stopped being true. `verified: source-read` is doing real
work here — it is the difference between "we asked" and "we checked". The
mechanism is right; what it needs is for someone to run it, which is why the
lanes below were re-verified in the same pass rather than date-bumped.
Consequence for `ADR-0004`: the honour-system `WARDEN_AGENT_ID` marker now has a
real issuance identity behind it on the OpenBao side. ops-warden remains a
consumer of that identity and does not own it.
```task
id: WARDEN-WP-0033-T05
status: done
priority: medium
state_hub_task_id: "75051d17-399b-5129-860b-ae00dae91c47"
```
**Make blocker staleness a mechanism instead of an intention.**
Four stale blockers surfaced in twelve hours: an OpenBao token recorded expired
that was valid, a verification script recorded ready that had never been written,
a ten-day `secrets-engine` question answerable from their source, and
`FLEX-WP-0007` cited as blocking `policy.enabled` seven weeks after it read
`finished`. `.claude/rules/finding-routing.md` has said *re-read a blocker before
trusting it* since `RISK-F-0001`. Saying it has not worked.
`warden route gaps` had a `--stale-days` threshold defaulting to 90, which was
not loose but **inert** — the register was created 2026-08-15, so it could not
fire before November.
**Done 2026-08-21.** Two changes, and the second matters more:
1. **Split the cadences.** `DEFAULT_STALE_DAYS` (90) still governs pointer
freshness — "is this the right owner and page" genuinely is quarterly. New
`DEFAULT_BLOCKER_STALE_DAYS` (14) governs interim blockers, calibrated on the
cases that cost something: ten days for the secrets-engine lanes, one day for
`RISK-F-0001`, ~50 for `FLEX-WP-0007`. At ~15 lanes it surfaces about one a
day rather than a wall, and it fires on zero lanes on age today.
**Then scaled by risk, after risk-nexus answered.** They accepted the offer to
converge and published their convention: 14 days critical/high, 30 medium, 60
low, with fix state *read* from the owning repo rather than remembered. Their
framing — "point `warden route gaps` at those windows and the two will agree
without either of us building a shared mechanism" — is better than a joint
tool, so `blocker_stale_days()` now maps lane `risk` onto exactly those
windows. `ungraded` takes the *shortest*, not the longest: `ADR-0007` makes an
absent grade a defect, so its blocker is the least trustworthy of all.
2. **`verified:` distinguishes re-checking from re-editing.** `reviewed` records
when someone touched the entry; nothing recorded whether the claim was
re-established. `owner-confirmed` and `source-read` count;
**`asked-and-waiting` and `unverified` do not**, and a lane in those states is
stale regardless of its date. `key-cape-oidc-login` is the proof: asked today,
zero days old, correctly flagged.
Current state: 14 non-draft interim lanes — 5 `owner-confirmed`, 1
`asked-and-waiting`, 8 `unverified`. The 8 are honest; they were carried forward
this session without a check and now say so instead of hiding behind a fresh date.
`--fail-on-stale` exits 3 so a cron job or gate can act. **Deliberately not a CI
test on age**: a date-triggered failure breaks the build on a calendar day with
no code change, punishing whoever commits next rather than whoever owns the
blocker. The CI test is structural instead — every interim lane must record
*how* it was verified, which fails on the commit that introduces the omission.
```task
id: WARDEN-WP-0033-T06
status: done
priority: high
state_hub_task_id: "94f77f5a-f919-5328-832c-ba1d24c6431b"
```
**Correct first-time OpenBao administration routing.** `rapp-postgres` reported
on 2026-08-22 that `warden plan` mapped first-time
`database/config/platform-pg-2` administration to the generic
`openbao-api-key` template. It consequently proposed paste-once KV provisioning
and a workload OIDC role, neither of which can configure database connections,
policies, auth roles, or token roles. Their Desk session stopped without an
approval or secret action.
The authoritative platform runbook and rapp-postgres's live T04 evidence agree
on the missing distinction: this is one attended KeyCape-backed OpenBao identity
act at auth mount `netkingdom`, role `platform-admin`. It is not a credential
value handoff. Root stays offline break-glass authority and a workload role must
never be widened to make the operation pass.
Add an exact `openbao-platform-admin-login` lane, an operator playbook, and a
regression that makes the reported request resolve to `founder_required` with
`oidc_login` — never `paste_once_provision`. Verify the route, then return the
corrected non-secret command to rapp-postgres.
**Done 2026-08-22.** The new active lane resolves the exact reported request at
score 20, ahead of the generic KV template at 11, and returns one
`founder_required` act:
`bao login -no-print -method=oidc -path=netkingdom role=platform-admin`.
Planner output offers only `warden access openbao-platform-admin-login --fetch`
and the exact owner command; login lanes no longer inherit secret-value
transports (`--exec`, `--out`, `--wrap`). The lane is explicitly a high-risk,
non-workload human identity act, so the generated boundary artifact lists it as
having no KV address rather than inventing one.
Verification: 384 tests pass, Ruff passes, workload accounting resolves all 28
lanes (3 resolved, 17 unknown, 8 not-applicable), and the boundary invariant
reports 20 high-risk lanes, 14 concrete paths covered, 6 non-KV/pattern lanes,
0 uncovered. No login or credential action was performed.
## Related
- `secrets-engine` `SECRETS-WP-0006` — catalog admission, decision `ae676382`
- `RISK-F-0009` — closed by railiance-platform 2026-08-21; 0 uncovered
- `railiance-platform` `RAILIANCE-WP-0022` (boundary coverage), `RAILIANCE-WP-0017`
- `ADR-0002`, `ADR-0003`, `ADR-0004`, `ADR-0005`, `ADR-0007`
- `WARDEN-WP-0030` — the delegation register these five lanes retire from

View file

@ -0,0 +1,354 @@
---
id: WARDEN-WP-0034
type: workplan
title: "Layer model v0.7 conformance — state the deadline, bind the agent boundary, steward the estate's newest rule"
domain: infotech
repo: ops-warden
status: active
flavor: residual
depends_on:
- WARDEN-WP-0030
owner: ops-warden
topic_slug: netkingdom
planning_priority: P1
depends_on_workplans:
- WARDEN-WP-0030
created: "2026-08-29"
updated: "2026-09-28"
state_hub_workstream_id: "ae3ff76f-883d-5e2f-b6aa-144d61e8fdef"
---
# WARDEN-WP-0034 — Layer model v0.7 conformance
`security-layer-model_v0.7` is **accepted**. ops-warden declared Staff and PEP-shaped,
shipped the two declaration artifacts the standard now cites as estate reference forms,
and had four findings adopted into the text between v0.4 and v0.7.
The assessment in `history/2026-08-29-v07-scope-intent-assessment.md` checked every
v0.7 obligation against shipped code rather than intent. Three gaps survive, plus a
role the companion assigns that no conformance check will ever catch.
## Why a workplan and not an adhoc
T01 is a `MUST` that is currently unstated. T02 changes an enforcement boundary and
touches `ADR-0004`. T04 is a cross-repo stewardship commitment against a standard eight
repositories have yet to adopt. The convention reserves adhocs for low-risk work
completed directly; none of these qualify.
## Tasks
```task
id: WARDEN-WP-0034-T01
status: done
priority: high
state_hub_task_id: "8b3bdb9f-d2c2-5b3e-89e2-417bf3e37484"
```
**State the revocation visibility deadline (§9.7.2, a MUST).**
ops-warden states none, and the honest value is the certificate TTL: **up to 48 hours**.
A cert issued under an allow remains valid for its full TTL even if the authorizing
decision is revoked the next minute. There is no CRL, no KRL distribution, and host-side
`auth_principals` belongs to `railiance-infra`.
Add the deadline to `pep-stance.yaml` as what it is — `adm` 48h / `agt` 24h / `atm` 8h —
with the mechanism named (TTL expiry, no revocation channel) rather than implied. Assert
it against the shipped `ActorType` TTL policy by test, the same way the stance map is
asserted equal to shipped behaviour: a stated deadline free to drift from the code has
the same defect as a stated stance free to drift.
**Done when:** the deadline is published, test-bound to the TTL policy, and the absence
of a revocation channel is stated rather than left to inference.
**Not in scope:** shortening it. Whether 48h is acceptable is a joint question with
`railiance-infra` (KRL distribution) and is T05's to raise, not this task's to decide.
Completed 2026-09-04. `pep-stance.yaml` now publishes TTL expiry as the only
revocation mechanism, explicitly records that no CRL/KRL channel exists, and
states `adm=48h`, `agt=24h`, and `atm=8h`. The conformance test derives the
expected map from `ActorType`/`MAX_TTL_HOURS`, so declaration and issuance
policy cannot drift independently. The layer and stance declarations now name
the accepted v0.7 standard.
```task
id: WARDEN-WP-0034-T02
status: done
priority: high
state_hub_task_id: "3318ee1a-b5d9-5d39-baf7-9c42a8bc7b55"
```
**Bind the agent read-boundary to an issued identity (§3.4 rule 1).**
`ADR-0004`'s boundary triggers on `WARDEN_AGENT_ID` — a variable the agent sets about
itself. An agent that omits it is not recognised as one. §3.4 rule 1 now requires
authority to be *issued* per task and attributable to the principal acted for.
The identity exists as of `KEY-WP-0009-T03`: `codex-railiance-platform`, subject
`service:codex:railiance-platform`, role `coding-agent`, 15-minute lifetime, with
`railiance-platform` enforcing the OpenBao-side policy.
Key the boundary on the issued identity where one is present; keep `WARDEN_AGENT_ID` as
a fallback that fails **toward** the boundary, never away from it. State plainly in the
ADR trail which half is enforced and which is advisory: the OpenBao-side deny is real,
the ops-warden-side refusal is a courtesy that a determined caller can decline.
**Done when:** an agent presenting the issued identity is recognised without setting
`WARDEN_AGENT_ID`, the fallback still refuses on `risk: high` lanes, and the
enforced/advisory split is written down.
**Watch:** do not turn this into ops-warden validating a token — that is verifying an
identity claim, adjacent to deciding, and `ADR-0002`/§6 both point away from it. Read
the identity; do not adjudicate it.
Completed 2026-09-04. The advisory CLI guard now prefers the exact issued
KeyCape subject `service:codex:railiance-platform` from
`WARDEN_POLICY_SUBJECT`, then falls back to `WARDEN_AGENT_ID`. Tests prove the
issued subject triggers exit 7 without the legacy marker, the fallback remains,
and an operator subject does not manufacture an agent identity. ADR-0004
revision 2 records the enforced/advisory split: Warden reads the marker and
does not validate a token; OpenBao's `agent-high-risk-boundary` is the real
enforcement.
```task
id: WARDEN-WP-0034-T03
status: done
priority: medium
state_hub_task_id: "a891b32c-b0a7-59f6-a5cd-977be65c09ca"
```
**Derive an emission cadence, or defer it with a reason (§9.6).**
ops-warden's trail is attributive, so cadence is a SHOULD rather than v0.7's MUST for
load-bearing sources. It has been silent through two reviews, which is the one outcome
that is not defensible.
Derive a baseline from the existing `audit.jsonl` and signatures log. If the signal is
too bursty to support a threshold — plausible, since volume is operator-driven — record
that as the finding with the distribution that shows it, and declare the deferral in
`pep-stance.yaml`. A measured "no useful baseline" is a result; silence is not.
**Done when:** either a declared cadence with its derivation, or a declared deferral
carrying the data that justifies it.
Completed 2026-09-04 with an explicit measured deferral. The signatures log
contains three issuances across the 79-day observation window, on only two
active days: one on 2026-06-17 and two within 40 minutes on 2026-08-22. The
unified audit carries the latter two events. `pep-stance.yaml` records the
counts, dates, attributive classification, and reason that this sparse,
operator-driven burst cannot support a meaningful rate threshold.
```task
id: WARDEN-WP-0034-T04
status: done
priority: medium
state_hub_task_id: "94e73daa-f74d-51fd-8639-68896a4066ee"
```
**Answer the question the companion sends the estate here to ask.**
> *"For how to get something done — which lane, which credential, which route — ask
> `ops-warden`."*
Today the repo answers credential questions and no others. `warden route` and
`warden plan` cover lanes, owners and acts. Nothing answers *"which layer am I"*, *"how
do I declare"*, *"I am PEP-shaped, what do I owe"* — and eight of fifteen catalogued
repositories have yet to declare.
Provide the path, not the doctrine (that boundary is `ADR-0010`'s and does not move):
a routing entry and a short playbook that carry a reader from the companion to the files
to copy — `layer.yaml`, `pep-stance.yaml`, `check_layer_conformance.py`,
`test_layer_conformance.py` — and the check to run. The standard already names these in
§11 and §6.4; what is missing is the route to them.
**Done when:** `warden route find "how do I declare my layer"` resolves, and the
playbook is reachable from the catalog. **Not** a restatement of the companion — a
pointer layer, per `ADR-0001`.
Completed 2026-09-04. Catalog entry `netkingdom-layer-declaration` resolves the
exact query to `wiki/playbooks/netkingdom-layer-declaration.md`. The short
playbook points at the accepted companion/statute, the four reference artifacts
and checks, and the gate-house review path while explicitly leaving doctrine
with gate-house and declaration truth with each repository.
```task
id: WARDEN-WP-0034-T05
status: wait
blocked_on: message-from:ops-mason
blocking_reason: "Two items to route, not absorb: ops-mason has published no stance map; the second is another repository's to own."
priority: low
state_hub_task_id: "7d1b3c82-9b96-5087-a53a-496212909029"
```
**Two things to raise rather than absorb.**
Both are other repositories' to own; ops-warden's obligation is to route them, not to
fix them (`ADR-0003`, `ADR-0005`).
1. **`ops-mason` has published no stance map.** §13.1's register has one row and the
standard says that is itself the finding. `ops-mason` is catalogued PEP-shaped in
the same paragraph and is ops-warden's peer lane owner. Offer the reference form;
do not write their map.
2. **The 48-hour replay window from T01.** Once stated, raise with `railiance-infra`
whether KRL distribution is worth building, and with `access-engine` whether a
decision lifetime shorter than the cert TTL is meaningful when nothing can recall
the cert. State the question; let the owners answer.
**Done when:** both are routed with reasoning, and the answers recorded either way —
including a refusal, which is an equally good answer.
Raised 2026-09-04 and waiting for answers. Ops-mason received the stance-map
reference and classification question (message `6ddd6cc7`). Railiance-infra
received the KRL-versus-TTL question (message `3bfa306e`). Flex-auth, as the
current access-engine implementation, received the decision-lifetime semantics
question (message `02c99a74`). No implementation or policy decision was assigned
to another owner by these messages.
**One of three answered, 2026-09-09.** `flex-auth` answered the decision-lifetime
question as `FLEX-DEC-2026-004` (flex-auth commit `6a6464f`), and answered it
rather than declining it: a decision lifetime shorter than the certificate TTL is
meaningful, but only as authority to **issue**, never as authority to **use** an
already-issued certificate. The action is `sign` and it completes at issuance, so
the decision is consumed once the certificate exists and no field in the decision
record reaches the artifact. 15m against a 48h `adm` certificate was never an
inconsistency to reconcile — the question mistook a decision lifetime for a
credential lifetime.
Two obligations land on this side and are accepted: no replay of an allow past
`expires_at`, and a fresh Check per sign. The second is where the value is, and it
is real rather than nominal because claims are not cached PDP-side (deadline 0 for
the claim class), so a revoked principal stops obtaining *new* certificates
immediately.
`pep-stance.yaml` §9.7.2 is unchanged and stays here: flex-auth explicitly declined
to move the residue to their side, on the ground that a PDP recalling an issued
credential would be "the same shape of lie as fail-open". Declining it was the
right call even though absorbing it would have made their record look tidier.
The TTL lever they named — capping requested TTL per actor class or zone, already a
policy input since `ttl_out_of_bounds` is denied before OpenBao is reached — is
noted and deliberately not requested. Shortening `adm` from 48h has an operator
cost, and issuance on this lane is too sparse (3 signature records across 79 days,
`pep-stance.yaml` `emission_cadence`) to price it from intuition.
T05 stays `wait` on the remaining two: `ops-mason` (no stance map published) and
`railiance-infra` (KRL distribution versus TTL expiry). Neither has replied.
## Await gate-house's §11 precedence and case-sensitivity ruling
```task
id: WARDEN-WP-0034-T06
status: done
priority: low
state_hub_task_id: "b3b573df-2d2d-5a2c-9662-a13e435be57b"
```
ops-warden declares `layer: Staff` in `INTENT.md` frontmatter and `layer: staff`
in `layer.yaml`. §11 permits both forms and is silent on which governs when both
exist and disagree, so two conformance runs over this repository can disagree
while both follow the standard. Nine of nine repositories carrying both forms do
the same thing (flex-auth `tools/survey_layer_declarations.go`, receipt
`docs/evidence/2026-09-21-layer-declaration-survey.json`).
**RULED 2026-09-21, APPLIED 2026-09-21.** `GH-DEC-2026-017`
(`gate-house@def0af2`, amendments A9-A13) answers both questions, and the answer
to the second dissolves the first's consequence:
- **`INTENT.md` governs**; `layer.yaml` is a derived artifact that must be marked
derived, must name `INTENT.md` as its source, and must agree with it.
- **The §3 vocabulary is case-insensitive**, closed, and has four tokens
(`Taxonomy`, `Tooling`, `Engine`, `Staff`). A conformance run MUST ASCII-fold
case before comparing, and **no repository was asked to re-spell anything**.
`Staff` and `staff` are one value.
- **A declaration MUST NOT carry a standard version** — and that field is the
*reference form's*, not this repository's habit, so ops-warden was asked to
remove it here rather than each of seven adopters deciding.
Verified before editing against gate-house's own committed files rather than the
inbox message: the decision at `gate-house@def0af2`, amendments A9-A13 in
`docs/amendments/v0.8-section-11-declaration-amendments.md`, and §3/§4/§11 of
`net-kingdom/canon/standards/security-layer-model_v0.8.md`.
**What was applied.** `standard_version` removed from `layer.yaml` **and** from
`INTENT.md` frontmatter (the ruling's subject is the declaration, and `INTENT.md`
is the declaration), with the version-pinned `standard:` path de-versioned for the
same reason; `derived: true` / `derived_from: INTENT.md` added; **no layer value
changed**; `scripts/check_layer_conformance.py` rewritten to read `INTENT.md` as
governing, fold case, validate the closed four-token vocabulary, require the
derived marking, and reject a returning `standard_version` — it previously
*required* that field and so would have rejected the conforming declaration this
ruling produces; `tests/test_layer_conformance.py` asserts the **fold** rather
than equality, because an equality assertion would perform the re-spelling the
ruling declined to order. `pep-stance.yaml` untouched: a stance map is not a layer
declaration, and the schema beyond the derived marking and the version is
explicitly not ruled.
**Evidence.** `python3 scripts/check_layer_conformance.py --report` exits 0 and
prints the governing form and the derived one separately; `pytest
tests/test_layer_conformance.py` green; full suite and `ruff` green. Reasoning
and the adopter change set: `docs/layer-declaration-precedence.md` and
`wiki/playbooks/netkingdom-layer-declaration.md`.
**Left open, and not decided here.** Where the removed version now lives. A12
says version-scoped state belongs in the derived conformance record, which
"already MUST" carry the version it was derived at; ops-warden has a re-runnable
checker that emits nothing durable, not an emitted record. Whether §11 expects an
emitted artifact or a re-runnable check discharges it is asked of gate-house in
message `4220413a` and unanswered. Nothing applied above depends on the answer.
**Closed 2026-09-21 by `GH-DEC-2026-020` §4 (gate-house@104f3fc):** §5 was wrong
that the record exists; a re-runnable checker is sufficient if every run prints
the version it checks against and its scope. Applied: the checker now prints
`VALIDATED_AGAINST` and `SCOPE` on every run and enforces A12 r2 by content, not
key name (versioned `standard:` path, `companion_version`); `pep-stance.yaml`
untouched per §3; the playbook carries the adopter change set.
**Ruled the same day, and still `wait` — deliberately.** `GH-DEC-2026-017`
(message `3715e247`) landed hours later: `INTENT.md` governs, the sidecar is a
derived artifact that must be marked as such, §3's vocabulary is case-insensitive
so **nobody re-spells anything**, and `standard_version` must come out of the
declaration. Two asks land on ops-warden specifically because `layer.yaml` is the
form seven repositories copied.
This task stays `wait` rather than flipping to `progress`, because applying it
edits this repository's security-layer declaration — and the estate's reference
shape — on the strength of an instruction delivered in an inbox message. The
session that applies it should be mandated to, and should verify the decision at
`gate-house@def0af2` and amendments A9–A13 in the repository rather than from the
message text. The exact change set and its four conditions are written down in
`docs/layer-declaration-precedence.md`; the casing half of the original "done
when" above is moot rather than outstanding, since the ruling folds case.
Raised by flex-auth's corrected B1 (message `719b3e9e`, 2026-09-21), which
withdrew an earlier claim that the estate disagreed *across* repositories. The
withdrawal matters to this task: the original finding would have had ops-warden
changing a file to match an estate consensus that did not exist.
## Related
Review 2026-09-05: reviewed the pending implementation and fixed the identity
helper so an explicitly empty environment does not inherit the process identity.
Regression coverage also proves an operator subject cannot override the legacy
agent marker. Refreshed the generated high-risk path artifact after the catalog
changes. T05 remains waiting: no corresponding reply was present in the inbox,
and the checked owner workplans contained no resolution of these questions.
Validation: 415 unit tests and four SSH integration tests pass; Ruff, the
layer-conformance script, and the declaration-route CLI smoke check pass.
- `history/2026-08-29-v07-scope-intent-assessment.md` — the gap analysis behind this plan
- `history/2026-08-29-layer-model-v04-review.md`, `-v06-review.md` — the two prior reviews
- `security-layer-model_v0.7.md` §3.4, §6.4, §9.6, §9.7, §11, §13.1
- `net-kingdom/SECURITY-COMPANION.md` v0.2
- `ADR-0002`, `ADR-0003`, `ADR-0004`, `ADR-0005`, `ADR-0009`, `ADR-0010`
### 2026-09-28 loose-end review
T05 remains wait; the workplan is blocked on the two outstanding owner answers.
The local ops-mason checkout still has no published stance map, and the available
railiance-infra workplans/docs contain no KRL-versus-TTL acceptance or refusal.
The flex-auth decision-lifetime answer remains accepted. Warden's declaration,
TTL-bound tests and fresh-check behavior are complete; inventing an owner answer
would not satisfy T05. No certificate lifetime or revocation policy was changed.
> 2026-10-11: demoted to `flavor: residual` / `backlog` by founder-authorized triage — off the
> critical path (identity → authz → secrets → SecurityCase execution). Tasks are unchanged;
> promote back when demand or risk appears. See the-custodian
> `history/20261011-netkingdom-security-progress-assessment.md`.

View file

@ -0,0 +1,46 @@
---
id: WARDEN-WP-0035
type: workplan
title: "Register the Policy Nexus Forgejo source-read route"
domain: infotech
repo: ops-warden
status: finished
owner: codex
topic_slug: policy-nexus-forgejo-source-read
created: "2026-09-01"
updated: "2026-09-01"
state_hub_workstream_id: "45aec8d3-94b3-586e-b019-a47e656efafa"
---
## Register the exact high-risk lane
```task
id: WARDEN-WP-0035-T01
status: done
priority: high
state_hub_task_id: "dd84f2be-0143-540c-9c16-74f0fd129260"
```
Add the exact OpenBao path, field, OIDC role, owner pointer, and rotation
boundary from railiance-platform CCR-2026-0014. The entry must be concrete and
resolvable while remaining subject to Warden's high-risk agent read boundary.
## Verify routing and governed use
```task
id: WARDEN-WP-0035-T02
status: done
priority: high
state_hub_task_id: "1fa8f778-3e46-5f44-86c4-cab8628b7e60"
```
Pass catalog, route-selection, proxy, and policy tests; reinstall the CLI; prove
the installed route resolves and can hand the value only to a sanctioned child
transport without printing or persisting it.
Completed 2026-09-01. All 406 selected tests passed, including the generated
high-risk data-path boundary. The no-cache installed CLI resolves the exact
lane, and `warden plan` returns only sanctioned `--exec`, `--out`, and `--wrap`
transports for an agent caller. Policy Nexus Actions run 32 separately proved
the installed credential against the complete private-source fetch and release
path without exposing the value.

View file

@ -0,0 +1,131 @@
---
id: WARDEN-WP-0036
type: workplan
title: "Accept contained OpenBao login output only after helper persistence"
domain: infotech
repo: ops-warden
status: finished
owner: codex
topic_slug: attended-login-openbao-output
created: "2026-09-01"
updated: "2026-10-15"
state_hub_workstream_id: "d844c96e-152d-53fa-bff6-e072125ef66c"
---
## Repair attended-login handoff
```task
id: WARDEN-WP-0036-T01
status: done
priority: high
state_hub_task_id: "7eb8b9c9-1285-5ada-a17b-1d5bfbb8ba59"
```
Allow a successful OpenBao login to proceed when its output is fully contained
and the private mode-0600 token helper is populated. Continue failing closed on
non-zero login, missing persistence, child output, revocation failure, or cleanup
failure.
## Verify live contained operation
```task
id: WARDEN-WP-0036-T02
status: done
priority: high
state_hub_task_id: "d22bab05-c38b-561f-95de-6c146ce7c6cf"
```
Run the proxy regression suite, reinstall the CLI, and complete one governed
OpenBao platform-admin operation with deterministic self-revocation.
Completed 2026-09-01. The installed CLI completed the governed Policy Nexus
Forgejo source bootstrap with all child output contained, then revoked and
removed its isolated helper session.
### 2026-09-28 contained-result correction (existing T01/T02)
Addressed the September 21 attended-login failure/audit report under the existing
handoff repair and verification tasks. Failed envelopes now use distinct
non-zero codes 10–14 for login, child failure, child output, unconfirmed revocation
and cleanup failure. Both audit files retain the actual Warden exit code and
phase outcome. Any child bytes, including whitespace, fail closed; the access
advisory now states the silent-child requirement and the playbook documents codes,
retry limits and the WSL tunnel/browser requirements.
The pre-change checkout already raised exit 5 on ProxyError, so the reported
historical exit-zero failure was not reproduced here. The actual reproduced defect
was unconditional success auditing; the new CLI regression covers failed login
with returncode zero but missing persistence as well as non-zero login, start
failures, child output/failure, revocation and cleanup. Revocation still checks
revoke-self's exit status; it does not misinterpret an arbitrary failed lookup as
proof. The September 23 platform return confirms revoke-self is already granted.
Validation: focused proxy suite 48 passed; full suite 483 passed, 4 integration
tests deselected by repository default; changed Python files pass Ruff. No live
OIDC, credential read or production operation was used. The earlier September 1
live operation remains historical evidence, not a live validation of this change.
Automated endpoint/browser preflight and OIDC URL presentation remain optional
inbox suggestions, outside these completed handoff acceptance criteria.
### 2026-10-14 login-output fail-closed regression and fix (existing T01)
railiance-platform's RPF-WP-0017 re-acceptance review
(`railiance-platform/reviews/RPF-WP-0017-ops-warden-reacceptance-2026-10-14.json`,
decision `reject`) found that T01's commit `b4c1d39` (2026-09-01) dropped
`login_output.strip()` from the login fail-closed condition. From then until this
fix, a login that exited 0 with a populated helper proceeded to the child whatever
it wrote to stdout/stderr. Output stayed contained (criterion 3 held) but
criterion 4 (non-allowlisted output fails closed with self-revocation) and the
criterion 5 unexpected-output coverage regressed; the existing unexpected-output
test passed only through the empty-helper persistence path.
Fix: login output is still captured and never forwarded, and is now accepted only
when allowlisted by structure (values are never logged, returned, stored, or put
in exceptions/audit):
- stdout: empty, or exactly one JSON object with an `auth` object containing
`client_token`; top-level keys limited to `request_id, lease_id,
lease_duration, renewable, data, warnings, wrap_info, mount_type, auth`, auth
keys to `client_token, accessor, policies, token_policies, identity_policies,
metadata, orphan, entity_id, lease_duration, renewable, mfa_requirement,
num_uses`, each type-checked; duplicate keys rejected.
- stderr: empty, or exactly the OpenBao OIDC CLI prompt ("Complete the login via
your OIDC provider. Launching browser to:" or "... Open the following link in
your browser:", one `https://` URL, optional "Waiting for OIDC authentication to
complete..."). These prompt strings were confirmed present in the local `bao`
binary; this stderr allowlist entry is an ops-warden proposal pending
railiance-platform review under RPF-WP-0017.
Anything else (extra text, other stderr, multiple objects, unexpected keys or
types, parse/decoding failure) fails closed: self-revocation through the existing
path, helper cleanup, exit 10 / `login_failed`, no retry. Added a parametrized
populated-helper test for 13 non-allowlisted shapes and an allowlisted hand-off
test; the success test now uses a full JSON shape plus the OIDC prompt. The
September 1 T02 live operation was recorded in `9770d6a` (00:51 +0200), after
`b4c1d39` (00:13 +0200) and `8f01eef` (00:24 +0200); `proxy.py` at that time
contained the relaxed check (the installed CLI's exact revision was not recorded). No live OIDC, OpenBao call, or credential read was used for this fix.
Tightened the same day after an independent re-review (containment gap): stdout
`wrap_info` must be null or absent (a wrapping token would survive `token revoke
-self`), `data` null, absent or `{}`, and `auth.client_token` a non-empty string;
the stderr OIDC prompt must start at the first byte (no leading whitespace;
trailing newlines remain fine). The fail-closed and hand-off tests now run through
the CLI and assert sentinel absence from both `access-audit.log` and `audit.jsonl`.
### 2026-10-15 CUST81 dependency correction (existing T01/T02)
Prepared source correction after the two CUST81 attended platform-admin attempts
failed before child handoff. The strict login-output allowlist remains unchanged.
Synthesized format flags now precede auth data; the recovery-only matcher handles
OpenBao `s.` tokens; failure messages expose only fixed categories, client exit
code and helper/output-shape booleans. No credential bytes or URLs are surfaced.
Do not attribute the observed 121-second failure to the argv order: Bao 2.5.5
scans global format throughout argv. Actual callback/auth failure remains unknown.
Browser profile initialization was verified from metadata only; server revocation
remains unconfirmed and the failed attempts are retained. No login, admin token
read, policy application or provisioning was performed by this correction.
Source verification is distinct from live verification. The already-approved
CUST81 execution scope and its open attended gate remain with CUST-WP-0081-T02.
Owner evidence: `docs/evidence/2026-10-15-cust81-attended-dependency-repair-r1.json`.

View file

@ -0,0 +1,191 @@
---
id: WARDEN-WP-0037
type: workplan
title: "Repoint the whynot-design npm lane to Forgejo"
domain: infotech
repo: ops-warden
status: blocked
flavor: planning
owner: codex
topic_slug: whynot-design-forgejo-npm-lane
created: "2026-09-04"
updated: "2026-10-03"
state_hub_workstream_id: "42a097db-1c24-558e-a724-030bb2b4443e"
---
## Verify the existing credential against Forgejo
```task
id: WARDEN-WP-0037-T01
status: done
priority: high
state_hub_task_id: "afc4d325-1c6d-5c25-aaf7-2118aa8f86c6"
```
Use only the governed execution transport to test the existing publish identity
against the canonical Forgejo npm endpoint. Do not print or persist the token.
2026-09-04: `warden plan` classified the exact verification as autonomous and
selected this lane. A contained login using the documented
`whynot-design-workload-kv-read` role proved `read` on the exact OpenBao data
path, and the governed child proved the secret is present. A real 0.4.2 publish
through that credential then failed. No value was printed or persisted and each
contained session self-revoked. The lane therefore needs token rotation, not an
OpenBao policy repair.
## Repoint the catalog and playbook
```task
id: WARDEN-WP-0037-T02
status: done
priority: high
state_hub_task_id: "7ceb2f74-496c-54e8-bf5a-3c49f994ff23"
```
Replace the retired Gitea endpoint and discovery language with Forgejo while
preserving the established OpenBao path, field, and least-privilege boundary.
This delivers the npm facet of routed intake
`01a06e07-f2f9-7586-9745-b359eb0025b6`; its inter-hub SSH facet remains separate.
Completed 2026-09-04. The catalog title, discovery keywords, rotation guidance,
and worker playbook now name the canonical Forgejo registry at
`https://forgejo.coulomb.social/api/packages/coulomb/npm/`. The OpenBao path,
field, owner, native-exec pointer, and high-risk boundary are unchanged. A
regression assertion prevents the retired Gitea discovery term and guidance
from returning.
## Prove routing and publication
```task
id: WARDEN-WP-0037-T03
status: wait
priority: high
decision_id: 1591daa1-6e85-4dfd-aadd-5a9fe1ef47fa
depends_on: [SECRETS-WP-0006-T06]
blocking_reason: "Hold kept by D3 until SECRETS-WP-0006-T06 moves publishing off the legacy whynot-design npm path; then rotate and prove a fresh publish."
intervention_note: "HOLD (founder, 2026-09-21): do not rotate yet. secrets-engine exec reads the ungoverned legacy path secret/coulomb/whynot-design/npm/publish (field npm_token); this runbook writes the governed path platform/workloads/coulomb/whynot-design/npm-publish (NPM_AUTH_TOKEN). A rotation now would not reach the live publish path. Resume after SECRETS-WP-0006-T06 moves the lane. Then: rotate the dedicated Forgejo package token, and prove a fresh publish and exact npm view through this lane."
state_hub_task_id: "a8b1b855-ab34-5835-b9fd-5f48bc0b6817"
```
2026-10-11: requalified after founder decision D3 (trust root, build-mode default) resolved — see the-custodian docs/decisions/2026-10-11-security-unblock-decisions.md.
Pass catalog and routing tests, verify the checkout route, and record the exact
Forgejo package release proven through the lane.
2026-09-04: repo-local verification is complete: the checkout route ranks
`whynot-design-npm-publish` first for a Forgejo npm publish need, reports the
canonical Forgejo title and verification command, all focused
routing/access/proxy tests pass (`145 passed`).
The sibling repo's `@whynot/design@0.4.2` release (105 files; IR
`designVersion` 0.4.2; five visual tests pass) was published through the
plan-authorized `forgejo-admin-api-token` one-time recovery lane. An
authenticated exact-version lookup proved that Forgejo's remote integrity
matches the local dry-run. T03 waits only on rotating the dedicated package
token and proving the next fresh version through `whynot-design-npm-publish`;
the package migration itself is complete.
**2026-09-09 — field claim corrected, path routed.** `secrets-engine` (msg
`15f0c0ca`) corrected two things and declined a third, all correctly.
The KV field is `npm_token`. `NPM_AUTH_TOKEN` is the environment variable their
publication-scope policy injects, and ops-warden had copied the env var in as the
field name — so the catalog's `fetch_command` named a field that does not exist
and could only ever have failed. Corrected in `registry/routing/catalog.yaml` and
`wiki/playbooks/whynot-design-npm-publish.md` from the owner's statement rather
than re-derived here. This was `ADR-0001`'s failure mode rather than a typo: a
pointer layer restating an owner's procedure and getting a detail wrong.
The endpoint claim already agreed; `delivery_config.npm.registry` has been the
Forgejo URL throughout.
The **path** is routed to `railiance-platform` and the catalog is unchanged pending
their answer. Which location backs the lane for reads is custody state they own;
`secrets-engine` has no lane read authority to confirm it and refused to rewrite a
production lane pointer from a coordination message (`SECRETS-WP-0006-T06`). Their
reasoning is right, and asserting our own pointer is authoritative because it is
ours would route around it. The ask names a location only and flags that a
`bao kv get` answer would be the 2026-07-16 disclosure vector on a `risk: high`
lane.
`ready: false` is not being read as path evidence: a source checkout with no
production authority reports not-ready regardless of which path the catalog names.
T03 still waits on the human rotation of the dedicated Forgejo package token; the
path question does not block that, it determines whether the pointer is correct
once it rotates.
**2026-09-21 — field reverted to `NPM_AUTH_TOKEN`; path confirmed; a second
location found.** `railiance-platform`, who holds custody of the path, answered
both halves (msgs `c6841bdb`, `79f3e047`) and the answer reverses the 2026-09-09
change above.
An attended founder session enumerated the field names present at
`platform/workloads/coulomb/whynot-design/npm-publish`: read-only, no mutation,
no value emitted, `attended_identity` true. **Exactly one field is present, and
it is `NPM_AUTH_TOKEN`.** Receipt: `railiance-platform
docs/evidence/2026-09-10-npm-lane-field-resolution.json`, commit `9d24086`. The
both-fields reconciliation offered the day before is ruled out by the same
enumeration. `CCR-2026-0001` has carried two dated receipts naming that field
since 2026-06-28, one of them an attended founder fetch that exited zero — which
is itself proof the field exists.
So `npm_token` is wrong *for this path*, and the catalog and playbook are
reverted to `NPM_AUTH_TOKEN`. Reverting is a correction back to the evidenced
value, not a new lane change.
The lesson is the one this plan already recorded, one level in. On 2026-09-09
this file wrote that adopting a correction from `secrets-engine` rather than
re-deriving it was the right shape. It was the right *source* and the wrong
*standard of evidence*: a correction adopted from a coordination message is
still an unverified mutation, even when it is only a field name in a catalog,
and this one would have failed at the rotation T03 is waiting for — the moment
it is most expensive to discover. railiance-platform's phrasing, kept because it
is better than ours: it will break "at the moment you least want it to".
**The second location, which is not ours to dispose of.**
`secret/coulomb/whynot-design/npm/publish` exists — version 1, created
2026-07-03T15:00:44Z, never updated, five days *after* the governed lane was
verified. It sits outside this lane's exact-path policy and outside any CCR.
railiance-platform read metadata only: field names were not enumerated, the
value was not read, nothing was deleted, because a location holding real
credential material is disposed of deliberately by its owner rather than tidied
away by whoever finds it.
Their unconfirmed hypothesis, recorded because it changes what this lane's
acceptance evidence means if true: `secrets-engine`'s lowercase field may belong
to *that* path, their catalog may declare that location, and the proven pilot
publish may have been reading the duplicate all along. If so, a working
production lane has been running ungoverned and this lane's acceptance evidence
describes a path its consumer does not use. Tracked as `RPF-WP-0035-T07`;
railiance-platform has asked `secrets-engine` which location their publish
reads, and ops-warden has asked the same about the native
`secrets-engine exec` front door this catalog points at (`exec_owner`).
Recorded in the catalog and in `wiki/playbooks/whynot-design-npm-publish.md` as
an explicit non-lane rather than deleted from the record. Not routed around: the
governed path stays the pointer.
### 2026-09-27 owner follow-up
Consumed the September 21 secrets-engine source return: the active catalog
still names `secret/coulomb/whynot-design/npm/publish`, field `npm_token`.
The governed field remains `NPM_AUTH_TOKEN`; no pointer correction is needed
in Warden. RPF-WP-0035-T07 now requires native governed-consumer proof before
legacy destruction and no longer asks for comparison of secret values.
T03 remains wait and the founder's no-rotation hold remains effective.
Resume only on the SECRETS-WP-0006-T06 migration receipt, then rotate through
the dedicated package lane and prove a fresh publish plus exact npm view.
Do not treat the September 16 OpenRouter key-check receipt as npm acceptance.
### 2026-09-28 loose-end review
Workplan state corrected to blocked to match T03's wait and the September 27
owner return. The governed catalog field and path are already correct. The
founder's no-rotation hold remains binding until SECRETS-WP-0006-T06 supplies
native governed-consumer migration evidence; a dedicated token rotation and a
fresh package publish/exact-version lookup then remain. No credential fetch,
rotation or publication was attempted.
2026-10-03 (CUST-WP-0075): the gate was only in intervention_note. Encoded as blocking_reason, depends_on SECRETS-WP-0006-T06, and hub decision 1591daa1-6e85-4dfd-aadd-5a9fe1ef47fa (D3: trust root and secret custody), which also carries the matching RPF-WP-0035-T07 hold.

View file

@ -0,0 +1,149 @@
---
id: WARDEN-WP-0038
type: workplan
title: "warden plan must distinguish reading a secret from mutating one"
domain: infotech
repo: ops-warden
status: finished
owner: ops-warden
topic_slug: netkingdom
planning_priority: P1
depends_on_workplans:
- WARDEN-WP-0029
created: "2026-09-08"
updated: "2026-10-16"
state_hub_workstream_id: "751ad530-e44b-52c4-b70e-cb47568a0179"
---
# WARDEN-WP-0038 — Read versus mutate intent in the policy front door
## The defect
`warden plan` scores a need against catalog lanes by keyword overlap and returns
a verdict. It has no notion of what the caller wants to *do*. So a need that
says *generate a successor secret and CAS-write it to two custodians* matches the
lane that **reads** that path, scores well on the shared nouns, and returns:
```
verdict: autonomous
commands: warden access <lane> --exec / --out / --wrap
```
Three read transports offered as the answer to a write, under the verdict that
means "proceed without the founder". Reproduced 2026-09-08 against
`rapp-qonto-keycape-client` (`ADHOC-2026-09-08-T04`).
## Why this is worse than a bad suggestion
`warden plan` is the documented first step before drafting any founder credential
step (`.claude/rules/credential-routing.md`), and `autonomous` is consumed as an
authorization signal by agents that have no other check. `WARDEN-WP-0029` shipped
the verdict *because* instinct was not auditable; a verdict that is confidently
wrong in the permissive direction is worse than no verdict.
key-cape hit this and refused to rely on it — "we are not treating a generic
match as authorization" — which is the only reason it surfaced as a design
question rather than as an unadmitted custody mutation. The control that caught
it was a counterparty's caution. That is not a control.
`WARDEN-WP-0033-T06` is the same shape (rapp-postgres, generic
`openbao-api-key` match → `paste_once_provision` for an act that could not work)
and was fixed by adding one exact lane plus one regression. That fixed the
instance. This is the class.
## Shape of the fix (to be designed, not assumed)
- Classify need intent — read / mutate / establish — before scoring, and never
answer a mutate need with a read lane's transports.
- A mutate need against a lane ops-warden does not own is `unroutable` with a CCR
stub naming the custody owner, not `autonomous`. `ADR-0003`: cover gaps, never
silently own them; a permissive verdict on someone else's custody act is a
silent claim of ownership.
- Consider whether `rotation.steps` should be reachable from `plan` at all, given
they are advisory prose and several are explicitly not automatable.
- Regression from the reproduced case, in the manner of T06.
## Open question for design
Whether intent belongs in the matcher or in the lane. A lane could declare which
acts it admits (`read`, `rotate`, `provision`) and `plan` could refuse any act a
lane does not declare — stricter, and it fails closed on lanes that say nothing,
which is the majority today. That is a catalog schema change and needs its own
argument.
## Confirmed independently, then fixed — 2026-09-09
`railiance-platform` hit the same defect from the other side while answering the
Qonto custody routing question (msg `7c7228ac`). Having stated that the correct
verdict is `founder_required` — attended OIDC via `netkingdom` `role=platform-admin`,
through the governed `openbao-platform-admin-login` lane — they added:
> Note this is the same verdict your `warden plan` should have returned; it
> returning `autonomous` and offering read transports for a write is a real
> defect and WARDEN-WP-0038 is the right place for it. Until that is fixed, do
> not let a plan result stand in for this answer.
Two counterparties reporting the same wrong verdict in two days, one of whom is
the write authority being wrongly bypassed, moved this from proposed to shipped.
### What shipped
`_need_intent()` classifies a need as `mutate` or `read` by token match, and
`_owns_write_authority()` tests `delegation.mode == "permanent"`. A mutating need
on a lane ops-warden does not permanently own can no longer reach any branch that
returns `autonomous`:
- lane declares a `rotation` → `founder_required`, with an `approve` act naming
the write owner, the rotation method, whether it is automatable, and
`warden rotate-guide` as guidance;
- lane declares none → `unroutable` with a CCR stub whose `owner_hint` names the
write authority.
**Commands are empty of read transports either way.** That is the half that made
the wrong verdict actionable rather than merely wrong: `--out`, `--exec`,
`--wrap` and a bare `bao kv get` were being offered as the answer to a write, and
a test now asserts none of them appears.
### The design question in the plan, answered by use
The plan asked whether intent belongs in the matcher or in the lane. It went in
the matcher, with the *ownership* test doing the work that a verb list cannot:
SSH certificate issuance is itself a mutating act, and `warden sign` must stay
`autonomous`. `delegation.mode: permanent` is what separates ops-warden's own
front door from someone else's custody — not the absence of a verb from a list.
A regression asserts the SSH lane still proceeds, because a guard that refuses
our own lane would be a worse defect than the one it fixes.
The stricter alternative — lanes declaring which acts they admit, `plan` refusing
anything undeclared — is not built. It is a catalog schema change that would fail
closed on the majority of lanes, which say nothing today, and it needs its own
argument rather than arriving as a side effect of this fix.
## Recognize explicit attended OpenBao platform administration
```task
id: WARDEN-WP-0038-T01
status: done
priority: high
```
2026-10-16 source follow-up: the exact request "attended OpenBao platform
administration for reviewed flex-auth signer custody CAS0 and fixed KV metadata"
fell through to keyword ranking and selected `openbao-structure-survey` /
`approve`. The configuration override required policy/auth-role nouns even
though the caller explicitly named attended platform administration. Recognize
that explicit intent before keyword ranking. The plan now selects
`openbao-platform-admin-login` / `founder_required` / `oidc_login`; the governed
reviewed-command transport and identity gate are unchanged. Survey requests
remain metadata-only, and a missing administration lane stays unroutable.
Prepared and tested in an isolated checkout for independent review. This task
records source repair only; it neither authorizes nor claims installation,
attended login, signer provisioning or full16TSA native acceptance. Integration
and the existing platform custody work retain those next actions.
### Not claimed
This narrows what `warden plan` will assert; it does not make the tool an
authority on custody. railiance-platform's instruction stands and is the right
standing posture: a plan result does not substitute for the owner's answer.

View file

@ -0,0 +1,159 @@
---
id: WARDEN-WP-0039
type: workplan
title: "Preserve explicit policy caller refusals before credential and CA effects"
domain: infotech
repo: ops-warden
status: finished
flavor: residual
owner: codex
topic_slug: custodian
created: "2026-09-08"
updated: "2026-10-03"
origin: residual
origin_ref: HFACT-WP-0001
state_hub_workstream_id: "ae44a935-6fca-514c-a385-4550dd2b1fe8"
---
The factory critical-path review reproduced a live HTTP 403 from the reachable
PDP for the configured ops-warden caller and a railiance-platform secret read.
`check_fetch_policy` treated it as evaluator unavailability and used unknown-zone
fail-open. Caller-auth rejection is an explicit refusal, not a transport outage.
## Preserve explicit authentication and binding refusals
```task
id: WARDEN-WP-0039-T01
status: done
flavor: residual
priority: high
assignee: the-custodian
state_hub_task_id: "eaad08d9-ae47-5f50-8278-ea402a0e2869"
```
HTTP 401 and 403 stop signing and credential fetch irrespective of the outage
profile. Do not expose response bodies or alter system identity to bypass the
refusal. Preserve configured behavior for genuine evaluator failures. Prove
both permissive and closed outage profiles and refusal before child execution.
## Verify and publish the correction
```task
id: WARDEN-WP-0039-T02
status: done
flavor: residual
priority: high
assignee: the-custodian
state_hub_task_id: "ac85cf00-391c-5c63-b8f4-ed5bd57684e5"
```
Run the policy and proxy suites, retain a value-free live refusal receipt,
publish the reviewed source, and verify the installed CLI refuses before any
credential transport. A refusal is not a successful credential-read admission.
The owner installer initially reused a cached wheel despite `--force`, leaving
the pre-fix policy code installed. Both Makefile installation targets now use
`--reinstall-package ops-warden` to refresh this package from the reviewed source.
Verify the installed module as well as the checkout before the live refusal test.
## Resolve the credential proxy's admitted policy binding
```task
id: WARDEN-WP-0039-T03
status: done
flavor: residual
priority: high
assignee: the-custodian
state_hub_task_id: "8ca28b63-2e04-5333-b635-011b124c1806"
```
Consume the existing native-lane handoff (WARDEN-WP-0033 / SECRETS-WP-0006)
and flex-auth caller contract. Establish whether this interim transport needs an
admitted dedicated policy route or must finish its native handoff. Retain the
refusal until that contract yields positive and wrong-caller/owner/tenant
negative evidence. No credential read, secret generation, or policy grant is
authorized by this workplan alone. HFACT-WP-0001-T03 consumes this return.
Validation: 429 tests passed (4 integration tests deselected by the repository default); Ruff passed for changed Python files. Full tests used the declared phase-memory source and an isolated temporary memory store. The focused policy/proxy suite passed 69 tests. The existing authenticated SSH policy probe still returns HTTP 200/ALLOW, decision:f3f7c88f9585582a; the credential-owner request returns 403. No CA issue or credential read was performed by these probes.
Installed verification: source guard present after refreshed owner installation (`19bb75c`). `warden access forgejo-admin-api-token --exec --field API_TOKEN -- true` exits 4 with explicit HTTP 403 before credential transport or child execution. The previous fail-open warning is absent. The probe requested no output value; no credential was fetched. T01/T02 are complete; T03 owns the remaining exact policy-binding admission.
**T03 routed 2026-09-09.** Asked `flex-auth` whether an admitted contract exists
for a delegated credential read where the caller (ops-warden) and the resource
owner differ, since that is exactly the shape drawing the live 403. Three outcomes
were named as equally acceptable answers, with no preference stated between the
first two: an existing contract to conform to; no contract yet, making this a
design question they should raise rather than one ops-warden invents a shape for;
or that there should be **no** such contract — the interim proxy transport is
itself the defect and the answer is to finish the native handoff
(`WARDEN-WP-0033` / `SECRETS-WP-0006`).
The third is a real possibility and was not argued against. Eleven catalog lanes
are ops-warden proxies with a named intended owner precisely because no owner
front door exists; if delegated reads should not be made policy-admissible, that
shortens WP-0033 rather than blocking it.
Two fixes were explicitly ruled out in the message rather than left unmentioned:
broadening the ops-warden caller binding so it may read other systems' resources,
and relabelling `resource.system` as ops-warden so the existing binding matches.
The second is worse — it would make the audit trail assert ops-warden owns
credentials it deliberately does not, which is the claim `ADR-0002` exists to
prevent, achieved by editing a field instead of making an argument.
The refusal stays in place until an answer yields positive evidence plus
wrong-caller, wrong-owner and wrong-tenant negatives. No grant was requested.
`secrets-engine` was told this bears on SECRETS-WP-0007-T04 and on how many lanes
stay proxied; `gate-house` was told it may reach how the signing lane's
approval-consume is built (GH-DEC-2026-005).
### 2026-09-28 loose-end review
T03 remains wait and the workplan remains blocked. Re-read the owner's finished
FLEX-WP-0026: no admitted delegated-read binding exists, and its native OpenRouter
contract explicitly does not close WARDEN-WP-0039-T03. FLEX-DEC-2026-015 also
confirms resource.system is runtime policy vocabulary, not a repository rename.
Retain the explicit refusal. A native owner route's success does not grant this
interim proxy general delegated-read authority; exact positive and caller/owner/
tenant negative evidence is still required for the route that replaces it.
**T03 code side done 2026-10-03 (CUST-WP-0075, founder build-mode directive).**
access-engine answered the contract question with a dedicated fetch evaluator:
pin `flex-auth-ops-warden-fetch` (access-engine 02b15a7, `values/ops-warden-fetch.yaml`),
same caller (ops-warden SA, audience flex-auth), owner systems bound per lane,
request shape unchanged. ops-warden now sends `check_fetch_policy` only to the new
key `policy.fetch_flex_auth_url`; signing keeps `policy.flex_auth_url`. An unset or
unreachable fetch evaluator fails closed with a message naming it, with no fallback
to the signing pin and no fail-open. 401/403 keep the existing refusal text.
`warden access <need>` names the fetch evaluator on its `gate` (or `policy`) line. Tests:
`tests/test_policy_fetch_evaluator.py`, plus updated proxy and refusal tests. Full
suite: 494 passed, 4 deselected. Docs: `wiki/OpsWardenConfig.md` ("Two
evaluators", tunnel example), `wiki/CredentialRouting.md`, production example.
Still open (founder go, `needs_human`): deploy the pin (access-engine
ADHOC-2026-10-03-T02), add the ops-bridge tunnel (local 19091 → the pin's Service
ClusterIP:8080), set `fetch_flex_auth_url: http://127.0.0.1:19091` in the live
`~/.config/warden/warden.yaml`, then record exact positive evidence and
wrong-owner/caller negative evidence before T03 closes. Until the key is set,
every proxied fetch refuses with "secret-read evaluator not configured". Before
that change, those fetches got a 403 from the signing pin.
**T03 done 2026-10-10.** access-engine reported the fetch pin
`flex-auth-ops-warden-fetch` live since 2026-10-03 (Helm revision 1). The tunnel
`flex-auth-ops-warden-fetch-railiance01` (127.0.0.1:19091) is connected, and the
live `~/.config/warden/warden.yaml` carries
`fetch_flex_auth_url: http://127.0.0.1:19091`. Receipts were taken with
ops-warden's own caller token (SA `ops-warden/ops-warden`, audience
`flex-auth`, 10m). The requests are access-engine's fixtures in
`examples/ops-warden-fetch/`. Policy decisions only; no credential was read.
| Case | Result |
| --- | --- |
| Positive: `forgejo-admin-api-token`, owner railiance-platform | HTTP 200 allow `secret_read_lane_matched`, `decision:014afa428f776db5` |
| Unknown need | HTTP 200 deny `unknown_need`, `decision:d792b0d6b1cb8122` |
| Wrong owner: `resource.system` ops-warden | HTTP 403 |
| Wrong tenant: `tenant:binky` on subject and resource | HTTP 200 deny `wrong_tenant`, `decision:71da3ff8b50dad74` |
| Wrong caller: anonymous | HTTP 401 |
This is the exact positive evidence plus the caller, owner and tenant negatives
this task required. The explicit refusal stays for anything the pin does not
admit. access-engine closes its ADHOC-2026-10-03-T02 on this reply.

View file

@ -0,0 +1,240 @@
---
id: WARDEN-WP-0040
type: workplan
title: "Adopt unknown -> fail_closed behind signing-target classification coverage"
domain: infotech
repo: ops-warden
status: active
flavor: residual
depends_on:
- WARDEN-WP-0032
- WARDEN-WP-0034
owner: ops-warden
topic_slug: netkingdom
planning_priority: P1
depends_on_workplans:
- WARDEN-WP-0032
- WARDEN-WP-0034
created: "2026-09-09"
updated: "2026-09-28"
state_hub_workstream_id: "c8ee441e-1be1-5219-910c-e79ff23cc9ec"
---
# WARDEN-WP-0040 — `unknown` → `fail_closed`, in the right order
security-layer-model v0.8 §6.4 obligation 3 (proposed, 2026-09-06) rules that
`unknown` is not a zone and must resolve to `fail_closed`. ops-warden's
`pep-stance.yaml` declares `unknown: fail_open` under `ADR-0009`.
**We agree with the rule.** `history/2026-09-09-layer-model-v08-review.md` records
the assent and why our falsifier check failed: the cell governs certificate
issuance, not a §5.1 read-only diagnostic, so nothing rescues it.
**We disagree with adopting it first.** Measured 2026-09-09: of four signing
targets, zero resolve to a zone, three are `unknown`, one is `not-applicable`.
Flipping the cell today makes flex-auth a hard dependency of essentially every
certificate — `ADR-0006`'s rejected configuration, reached by a different route,
including the continuity path needed to repair flex-auth itself.
So: coverage first, then the cell. That ordering is the whole holding of
`ADR-0006` and the reason `ADR-0009` replaced a global switch with a zone map.
## Tasks
```task
id: WARDEN-WP-0040-T01
status: wait
blocked_on: message-from:ops-bridge
blocking_reason: "Warden's own declaration cannot classify the repair-path target workloads; the owner must declare the continuity actor/target and its z2-continuity resolution (see 2026-09-28 review)."
priority: high
state_hub_task_id: "611f0901-9fb5-5954-8dd0-a860c5f0cbec"
```
2026-09-30: gate stated. The 2026-09-28 review found the classification is not ours to make (target workloads belong to ops-bridge, the Inter-Hub execution owner and the backup owner), so T01 waits on the owner declaration, same as T02. Qualified per Task Wait Qualifiers v0.1.
**Classify the continuity path before anything else changes.**
The deadlock is specific and worth naming before it is designed around: flex-auth
unreachable → operator needs an SSH certificate to reach the host → target is
`unknown` because nobody classified the repair path → `fail_closed` denies it.
`z2-continuity` exists for this and its stance is `fail_open`, so the fix is
classification, not an exception. Determine which actor(s) constitute the
repair path, establish whether the declaration is ops-warden's to make (our own
`tenancy.yaml` covers ops-warden as a workload; the actors' target workloads may
not be ours), and route what is not.
Blocks T03. Converting the cell with the repair path unclassified is the one
outcome this workplan exists to prevent.
```task
id: WARDEN-WP-0040-T02
status: wait
blocked_on: message-from:ops-bridge
blocking_reason: "Owners of agt-codex-interhub-bootstrap (ops-hub) and agt-state-hub-bridge (ops-bridge) must publish authoritative workload identity declarations; atm-backup-daily declared by activity-core and compiled 2026-10-09 (z2-protected, admission unsatisfied: M1 below M2 floor)."
priority: high
state_hub_task_id: "54ad4864-7bcf-592e-a187-9239a81a0b11"
```
**Raise signing-target coverage, by asking owners — never by inferring.**
Three actor resources report `workload_resolution_absent`:
`agt-codex-interhub-bootstrap`, `agt-state-hub-bridge`, `atm-backup-daily`.
Their owners (ops-bridge and the backup execution unit among them) have not
published an authoritative workload identity declaration.
`ADR-0009` rule 3 forbids closing this with path or repository-name inference,
and that prohibition holds under pressure from this workplan specifically —
adopting a stricter stance is not a licence to manufacture the membership that
makes it survivable.
Route to each owner with the concrete consequence: while the declaration is
absent their actor cannot be issued a certificate during a flex-auth outage once
the cell converts. That is a better ask than a generic request to publish a
declaration, and it is true.
Report coverage with `scripts/report_workload_join.py`; this task is done when
coverage is stated, not when it reaches a threshold — the threshold is T03's
gate, and owners may legitimately decline.
```task
id: WARDEN-WP-0040-T03
status: wait
depends_on: [WARDEN-WP-0040-T01]
blocking_reason: "Gated on T01 and on layer model v0.8 (or successor) reaching accepted; never convert against a proposed standard."
priority: high
state_hub_task_id: "93eaf1f2-daaa-5e22-8804-85ca9433571c"
```
**Convert the cell and supersede `ADR-0009`'s unknown row.**
Gated on T01, and on v0.8 (or its successor) reaching `accepted`. Do not convert
against a `proposed` standard.
`ADR-0009` is `owner: ops-warden`, so changing it means a superseding ADR, never
an in-place edit (`.claude/rules/architecture.md`). The superseding record should
carry the v0.8 argument rather than restate it: unknown is not a zone, so a §9.3
per-zone trade was never made for that request.
`pep-stance.yaml` is asserted equal to `PolicyConfig.failure_modes` by
`tests/test_layer_conformance.py`, so the map and the code convert in one commit
or the test fails — which is the property that makes the map worth publishing.
```task
id: WARDEN-WP-0040-T04
status: done
priority: medium
state_hub_task_id: "222a8c5d-0c0f-5a1d-98d8-02be7dca3358"
```
**Return findings to gate-house and track the transition ask.**
Sent 2026-09-09 with the review. Two asks, in preference order: obligation 3
names a dated transitional state gated on coverage; or failing that, §13.1
records classification coverage alongside stance, so a register row cannot read
`unknown: fail_closed` while every target is unknown.
If both are declined, that is an answer and this workplan proceeds unchanged —
the cell stays a **declared gap** under §11 with this workplan as its route,
which is what the `declared-gap` mark (our own v0.6 finding) exists to express.
**Answered 2026-09-09 — GH-DEC-2026-011.** Ask 1 declined, ask 2 adopted;
standard amended at `net-kingdom@64394e9`.
Ask 1's refusal is correct and we are not appealing it: a sanctioned transitional
`fail_open` is indistinguishable at runtime from the stance the rule forbids, and
would make the rule optional at the only moment it costs anything. Ask 2 gave us
what we actually needed — §13.1 carries a Coverage column with this repo's
figures as its first entries, guarded so that coverage never softens a stance,
never gates one, and never makes a non-conformant cell conformant.
The reversal condition on that column binds ops-warden first, since the column
exists because we asked: if a row is ever argued conformant *because* its coverage
is low, the column comes out. Our row reads non-conformant and low-coverage, and
the second is not a defence of the first.
gate-house confirmed explicitly that they are **not** asking for the cell to be
flipped before coverage exists, and that this workplan's order is right. The cell
stays a declared gap under §11 with WP-0040 recorded as its route — the outcome
this task named as acceptable if both asks were declined, reached with one of them
adopted.
T01–T03 are unchanged and still gate the conversion. Coverage is now measured
rather than asserted (`scripts/report_coverage.py`), so T02's reporting obligation
has a tool behind it and the published figure cannot drift from the register's.
### 2026-09-28 loose-end review
T04 is done: GH-DEC-2026-011 already answered both transition asks on September 9;
the accepted coverage column and declared-gap disposition are recorded above.
No additional response is required to meet that task's acceptance criterion.
T01/T02 now wait; the workplan is blocked. Re-ran both coverage reports:
signing targets = 0 resolved / 3 unknown / 1 not-applicable; routing lanes =
3 resolved / 20 unknown / 15 not-applicable. Refreshed pep-stance.yaml's measured
date. These figures describe the checked-in registry and local owner declarations,
not a live PDP query. No zone membership was inferred or changed.
The exact missing workload resolutions are `ops-bridge-tunnel` for
`agt-state-hub-bridge`, `codex-interhub-bootstrap` for
`agt-codex-interhub-bootstrap`, and `backup-daily` for `atm-backup-daily`.
The seed inventory's `adm-example` is not an admitted repair actor.
Warden's own workload declaration cannot classify those target workloads.
T01 needs an owner-declared continuity actor/target and its authoritative
z2-continuity resolution; T02 retains the owner-routing/declaration follow-up
(ops-bridge, the Inter-Hub execution owner and the backup execution owner).
Without it, changing unknown to fail_closed would deny their issuance during a
PDP outage. Coverage reporting is complete, but owner coordination is not.
T03 also remains gated on standard acceptance: the local authoritative
net-kingdom/canon/standards/security-layer-model_v0.8.md still says proposed.
No superseding ADR or runtime stance change is warranted before these gates.
### 2026-09-30 owner asks sent
Routed the concrete-consequence ask (T01 classification + T02 declaration) to `ops-bridge` (agt-state-hub-bridge), `ops-hub` (agt-codex-interhub-bootstrap; owner unconfirmed, asked to name the owner if not theirs) and `railiance-platform` (atm-backup-daily; same caveat). Declining is an accepted answer. No membership inferred. T01/T02 stay `wait` on these replies.
### 2026-10-08 owner replies recorded
`atm-backup-daily`: railiance-platform declined in its replies of 2026-10-02
(msgs 53234ede, a3691df9). It declares only the credential lane
`railiance-backup-offsite-lane`, and no workload in railiance-platform has that
name. It names activity-core as the declaring owner, because activity-core is
the source repo of `daily-cnpg-option-a-backup`. It classifies the actor as
**ordinary**, not z2-continuity repair-path. activity-core will not declare
without founder confirmation (ACTIVITY-WP-0042-T03, decision 89f1058f). So this
actor now waits on that founder decision, not on railiance-platform. No
membership is inferred. `agt-state-hub-bridge` (ops-bridge) and
`agt-codex-interhub-bootstrap` (ops-hub) are still unanswered.
2026-10-08 (later): the founder confirmed in session that activity-core's
`daily-cnpg-option-a-backup` worker is the `atm-backup-daily` unit, and that it
is classed as ordinary. The confirmation was relayed to activity-core for
ACTIVITY-WP-0042-T03. Decision 89f1058f (D2) was deliberately left open,
because it also gates production ArgoCD go-aheads. `atm-backup-daily` now waits
only on activity-core publishing its declaration.
### 2026-10-09 atm-backup-daily compiled
activity-core published `declarations/backup-daily/tenancy.yaml` (c1533fe).
zone-engine's `resolve_zones.py` resolves it with the
`netkingdom-build-v0.1` profile:
- declared zone: `z2-protected` (ordinary, not z2-continuity)
- admission: `unsatisfied`, reason `M1_below_M2_zone_floor`
- effective zone: `unknown`
The resolved view is committed as `registry/flex-auth/zone-resolutions.json`,
and the snapshot was rebuilt from it. Resolution is no longer absent. The actor
stays `unknown` because the owner declared M1, below the M2 floor, which is
accurate rather than a gap ops-warden may close. Coverage, stated as T02
requires: signing targets 0/3 resolved, 3 unknown, 1 not-applicable. The
figure is unchanged because `report_coverage.py` counts effective zones.
`atm-backup-daily` now waits on owner M2 evidence. It does not wait on a
declaration. Still unanswered: `agt-state-hub-bridge` (ops-bridge) and
`agt-codex-interhub-bootstrap` (ops-hub).
> 2026-10-11: demoted to `flavor: residual` / `backlog` by founder-authorized triage — off the
> critical path (identity → authz → secrets → SecurityCase execution). Tasks are unchanged;
> promote back when demand or risk appears. See the-custodian
> `history/20261011-netkingdom-security-progress-assessment.md`.

View file

@ -0,0 +1,100 @@
---
id: WARDEN-WP-0041
type: workplan
title: "Draft catalog lane scaffolding with platform-owned credential rendering"
domain: infotech
repo: ops-warden
status: finished
flavor: implementation
owner: ops-warden
topic_slug: netkingdom
created: "2026-10-03"
updated: "2026-10-04"
depends_on: []
state_hub_workstream_id: "eb42b59c-1e32-5e1b-917d-34625f7a041d"
---
Accepts the ops-warden portion of P4 from CUST-WP-0075, proposal message
`79549660-4fc8-44d5-989e-074f6c7b6f07`. The platform owns its renderer and
CCR schema; no production act or approval is included here.
## Establish the catalog and platform seam
```task
id: WARDEN-WP-0041-T01
status: done
priority: medium
state_hub_task_id: "85bfd209-d2f1-59e4-aa02-e8e374598538"
```
Reviewed existing catalog validation, plan CCR stub, promotion checklist and
platform CCR rendering. The stub does not scaffold; platform rendering already
exists for reviewed CCRs. Proposed exact owner seam, inputs, fail-safe draft
outputs and offline acceptance in `wiki/CatalogLaneScaffoldContract.md`.
Platform's matching owner task is `RPF-WP-0050-T04`.
## Implement offline draft catalog and playbook generation
```task
id: WARDEN-WP-0041-T02
status: done
priority: medium
state_hub_task_id: "68bf72d8-a798-5e88-bfda-79b8910f7cda"
```
Implement the ops-warden portion of the seam proposal: deterministic staging
output, validated catalog fields, draft-only defaults, collision refusal,
explicit workload unknowns and risk, no secret inputs, no credential execution.
Add meaningful rejection/round-trip fixtures; reuse catalog validation. Do not
write platform-owned artifacts or invent its CLI. The local fragment generator
can progress independently while platform contract review is pending.
## Verify the accepted cross-owner scaffold
```task
id: WARDEN-WP-0041-T03
status: done
priority: medium
depends_on:
- WARDEN-WP-0041-T02
- RPF-WP-0050-T04
state_hub_task_id: "33587361-2b8a-5540-9c4f-d7b4f8ef25e0"
```
Integrate through reviewed owner references and demonstrate one exact KV-v2
workload-read fixture end to end. Record both owner commits and validation
results. Generation success alone does not admit or activate a lane. Update
`warden plan`'s unroutable guidance only once a real supported command exists.
## Report owner uptake and gates
```task
id: WARDEN-WP-0041-T04
status: done
priority: medium
state_hub_task_id: "4bc926af-04d4-5a27-9578-d2f83c0ecf9c"
```
Return the scoped P4 acceptance and this live owner record to the-custodian's
original proposal thread. Existing admission, approval, identity confirmation,
attended values and deployment evidence gates remain; no secret read or
production action was taken. Source/knowledge receipt is in the seam document.
## Offline delivery acceptance — 2026-10-04
T02 and T03 completed the bounded draft-generation scope. Actual-source
generator/routing/plan tests: 139 passed. Both actual owner CLIs staged the
paired synthetic fixture; lane/consumer/owner/risk/CCR pointer and manifest
digest match, unknown workload remains unknown, catalog execution is disabled,
CCR remains proposed with empty review, auth claims unconfirmed, frontdoor
pending/unresolvable and evidence not executed. Independent platform owner
verification remains separately attributed in the receipt.
Source implementation commits: ops-warden `dfe436e4e05610cae96085dadd582e5a63525580`;
railiance-platform `f5b7ff5118326f66be8ecd8330d4cda9e4d94520`.
Evidence: `docs/evidence/2026-10-04-catalog-scaffold.json`.
`warden plan` now names the supported offline command for unroutable needs.
The workplan finishes its local draft scaffold contract; CCR approval, binding
confirmation, attended values, deployment and actual capability receipts
remain separate lane-activation work, never implied by fixture acceptance.
No unfinished in-scope implementation or residual is parked by this closure.

View file

@ -0,0 +1,55 @@
---
id: WARDEN-WP-0042
type: workplan
title: "Route the OpenBao existing-structure survey to ops-mason"
domain: infotech
repo: ops-warden
status: finished
flavor: implementation
owner: claude
topic_slug: netkingdom
created: "2026-10-05"
updated: "2026-10-05"
depends_on: []
state_hub_workstream_id: "bd5edebd-2988-5685-881e-13931464f92f"
---
ops-mason construction plans need an existing-structure survey (section 2)
built from live OpenBao metadata. Founder decision: ops-warden routes,
ops-mason surveys. ops-mason implements `scripts/plan-survey.py` under its
own `ops-mason-build` grant (`ops-mason/MASON-WP-0008`). ops-warden gets no
survey code and no token, per the no-double-source rule
(`WARDEN-WP-0010`, `docs/adr/ADR-0001-catalog-is-a-pointer-layer.md`).
## Add the pointer-only catalog entry
```task
id: WARDEN-WP-0042-T01
status: done
priority: medium
state_hub_task_id: "ca8cd3e9-83ac-5732-8ec4-525e5ce1b113"
```
`registry/routing/catalog.yaml` entry `openbao-structure-survey`: owner
`ops-mason`, `warden_executes: false`, `exec_capable: false`, no `steps`,
native delegation to ops-mason, lane `ceremony` (the grant is a founder act),
`risk: high` (the grant can write policies and auth roles), `auth_method`
naming the ops-mason grant, `exec_owner`/`exec_command` pointing at the
survey script, `canon_ref` at `ops-mason/docs/construction-plan-format.md`.
Short pointer playbook `wiki/playbooks/openbao-structure-survey.md`.
## Verify routing offline
```task
id: WARDEN-WP-0042-T02
status: done
priority: medium
state_hub_task_id: "05bb75bd-2de9-55d5-8bc8-41b306ddcf16"
```
`make test` passes, including catalog validation and layer conformance.
`warden access "openbao structure survey"`, `warden access "construction
plan section 2"` and `warden route find "existing policies auth roles
survey"` resolve to the entry; `warden plan` returns `founder_required`
with the survey command; `warden access ... --exec` refuses (not
exec_capable).