You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Decide how the model meets the live needs no chapter covers #351
How does the model meet the live estate's needs that no chapter covers today, and the live shapes its refusals would reject?
The live-estate inventory found ten needs with no chapter: host paths, host ports and LoadBalancers, GPU device requests and a runtime class, an Application's own CronJobs, init containers, Pods created at runtime, internet egress, MongoDB and S3 (Garage) as engines, priority classes, and a Service fronting an off-cluster host. It also found four refusals that today's shapes would trip (E_DUPLICATE_HOST, E_HARDENING_UNMET, E_PRIVILEGED_PORT_UNDER_NONROOT, E_PROCESS_RBAC_GRANT), credential delivery through VSO VaultDynamicSecret where chapters 10 and 16 disagree, a RabbitMQ Vault engine the grant union lacks, and live namespaces and database names the derivation rules would rename.
For each: model it as a concept, give it a bounded escape hatch, change the Application to fit the model, or leave that Application outside deploy-kit. Input: docs/research/live-estate-needs.md.
E_DUPLICATE_HOST is keyed on host and tier. Two exposures may share a hostname when their audiences land on different tiers. This is the split-horizon shape the media hosts, hermes and agents-ws already use. The same host twice on one tier is still refused, because inside one proxy the two rules have no defined winner. The live shape fits without change.
E_PRIVILEGED_PORT_UNDER_NONROOT stays. auth-ui, agents-ui, AdGuard and stalwart change their images so that every container listens above 1024. The port a client dials (53, 25, 993, …) is a separate fact, stated on the L4 exposure (item 11).
E_HARDENING_UNMET stays the default, and a new concept, the admitted capability, is added. A Process may declare a capability from a closed list, with a reason. net-admin is the first entry, for the downloads VPN sidecar. The Platform document admits which Applications may declare one, the same shape as Kubernetes API access (0092). Root stays refused. jellyfin's chown init container is replaced by the volume group the spec already derives. hermes and agents-api fix their images.
E_PROCESS_RBAC_GRANT is working as designed. The Platform document admits agents-api and kubernetes-mcp-server. Each api rule gains a scope: the Process's own namespace, which renders a Role, or the cluster, which renders a ClusterRole. agents-api's namespaced grant is then expressible at the width it has today.
Credentials and names
A database credential is delivered by VSO through a VaultDynamicSecret, as env or file. This is what chapter 16 says and what grimoire, tribelt, outline and n8n use live. Chapter 10's self is rewritten to agree. self remains only for an Application that genuinely talks to Vault itself.
The Vault Agent injector retires, and VSO becomes the only delivery path.delivery: file renders as a VSO-synced Secret mounted as files. The injector and its webhook leave the foundation once nothing uses them.
rabbitmq joins the grant union (0029) as a dynamic credential. An edge to a rabbitmq-engine Process derives a per-consumer role, delivered like the database grant. This also retires the shared RabbitMQ KV that knowledge reads.
The derivation rules win, and a mismatched name is changed through a Move during handover. This applies to the seven namespaces that are not <project>-system and to the databases that are not <project>_db. The Move machinery is the one specified in the Stable Address round (Specify the Stable Address, the derived Move and the Infrastructure model, with their decision records #323). Chapter 60's claim that adoption "renames nothing" is corrected.
Host paths become Platform-declared node datasets. The Platform document declares each dataset by name, with the node capability that holds it and its path. These are contended node facts (0004). A Process mounts a dataset by name, and its placement on that capability is derived. No author writes a host path. Tempo's WAL is not shared data, so it becomes a reconstructible claim and not a dataset.
An L4 exposure reaches a raw surface from off the cluster. It is an Application exposure that names a client-facing port, a protocol (tcp or udp) and an address from a Platform-declared pool. It renders only as a LoadBalancer Service with the Local traffic policy, which keeps the client address. stalwart moves from hostPort to it, so there is one mechanism.
The GPU request is derived from the gpu placement dimension. A Process that requires a GPU derives one device request and the runtime class. The Platform document names the resource and the runtime class for each vendor. Nothing new is authored.
A sidecar may run before the Process with before: true. It runs to completion first, under the same hardening, and gets the volume mounts that A sidecar cannot mount the Process's volumes or run before it #271 settles. auth-api's download of its profiling agent moves into its image, because a download at start is an unpinned input.
Pods created at run time come from a declared spawn template. The Application declares what its Process may create: a pinned image alias, placement, edges and egress. The render derives the namespaced api rules, the network policy and the hardening for pods that carry the template's label, and hands the Process the pinned image references. agent-runner Pods, with their agent-gateway sidecar, are the live case.
mongo and s3 join the engine vocabulary, each with a backup image. An edge to an s3 Process derives a bucket catalog: one bucket and one scoped key per consuming Project, mirroring the database catalog. grimoire is the first consumer. Mongo gets a database catalog only if a consumer needs one.
The priority class is derived from what a Process is. The Platform document declares the classes and the rule. A Process whose engine derives a backup gets the data class, the foundation's Applications get the critical class, and every other Process gets none. Nothing is authored per Process.
A provider renders a selectorless Service and an EndpointSlice when some edge resolves to it. Consumers then use a cluster DNS name, and the Stable Address applies. A provider that no edge uses renders nothing. ollama-heavy is the live case.
Corrections the rewrite carries
Chapter 10 says that a pod holds a sidecar "three times in this estate". Six pods do.
Chapter 60 says the derived namespaces rename nothing. See item 8.
Out of this ticket
The inventory found further needs this ticket did not list. They become their own tickets on the map:
the scheduling shape: weighted preferences, topology spread, pod anti-affinity, and Deployments parked at zero replicas;
HelmReleases for the foundation;
headlamp's binding of a cluster role to a human OIDC group, and Grafana content.
Record
This answer is recorded here, on the ticket. The new concepts (Node Dataset, L4 Exposure, Admitted Capability, Spawn Template, Bucket Catalog, scheduled) enter CONTEXT.md, spec/v1 and their ADRs with the spec rewrite. That rewrite is not written now, because whether it lands as a v2 or as v1 reworked in place is still open.
C8, the rabbitmq grant models something live. It does not. The agents-api annotation names a dynamic credential, but the template reads the static KV path; the dynamic credential was dropped after Vault revoked the user under a running pod. VaultDynamicSecret can deliver the engine (C7 holds), so the grant is possible, but it introduces a credential kind no workload uses today.
C17, a LoadBalancer is the one L4 mechanism, so stalwart moves off hostPort. stalwart runs on the Frankfurt node, which has no MetalLB pool and one public address, and MetalLB documents itself as generally not compatible with cloud providers. The public Traefik's Service there is <pending> and uses hostPort for the same reason. Low ports stay refused in this resolution, so stalwart has no modelled way out yet.
C33, a non-root process cannot bind a port below 1024. On k3s it can: k3s sets containerd's enable_unprivileged_ports on kernels from 4.11, for any pod off the host network. The hazard E_PRIVILEGED_PORT_UNDER_NONROOT guards does not exist on this substrate. Read from source, not yet drilled.
Six more claims here hold only with a condition (C9, C12, C30, C32, C34, C36); the file states each.
The three failed claims flagged above are decided in Decide what each settled decision does about the claim under it that failed. Item 2 is reversed: E_PRIVILEGED_PORT_UNDER_NONROOT is cut, after a drill showed a non-root process binding port 80 on this cluster, and the four image changes are dropped. Item 11 is refined: an L4 exposure names a Platform-declared L4 Address, which is a pool (a LoadBalancer Service) or a node's own address (hostPort, with the pin derived), so stalwart stays on the Frankfurt node's address. Item 7 stands, with its reason corrected and a rotation drill as its condition.
The conditions flagged on this decision are settled in Decide which conditions on the settled decisions change the model. Item 16's egress: internet excludes a fixed non-public set, the tailnet range included, and reads no Platform range. Item 18's rule gains one clause: the Platform document names further Applications of the critical class. Item 12's device request is one of four shares. Item 7's role has no lifetime of its own; the lifetime is a fact of the rabbitmq engine. Item 6 delivers no Vault token: an Application that talks to Vault takes the self path.
Activity
ExtraToast commentedon Oct 9, 2026
Resolution
Settled with the maintainer on 2026-10-09, under Re-state the goals and non-goals against the scale premise: every live need gets a model concept and nothing gets an escape hatch, and a refusal stays only where its hazard is real at 0001 scale. Input: docs/research/live-estate-needs.md.
The four refusals
E_DUPLICATE_HOSTis keyed on host and tier. Two exposures may share a hostname when their audiences land on different tiers. This is the split-horizon shape the media hosts, hermes and agents-ws already use. The same host twice on one tier is still refused, because inside one proxy the two rules have no defined winner. The live shape fits without change.E_PRIVILEGED_PORT_UNDER_NONROOTstays. auth-ui, agents-ui, AdGuard and stalwart change their images so that every container listens above 1024. The port a client dials (53, 25, 993, …) is a separate fact, stated on the L4 exposure (item 11).E_HARDENING_UNMETstays the default, and a new concept, the admitted capability, is added. A Process may declare a capability from a closed list, with a reason.net-adminis the first entry, for the downloads VPN sidecar. The Platform document admits which Applications may declare one, the same shape as Kubernetes API access (0092). Root stays refused. jellyfin's chown init container is replaced by the volume group the spec already derives. hermes and agents-api fix their images.E_PROCESS_RBAC_GRANTis working as designed. The Platform document admits agents-api and kubernetes-mcp-server. Eachapirule gains a scope: the Process's own namespace, which renders a Role, or the cluster, which renders a ClusterRole. agents-api's namespaced grant is then expressible at the width it has today.Credentials and names
VaultDynamicSecret, as env or file. This is what chapter 16 says and what grimoire, tribelt, outline and n8n use live. Chapter 10'sselfis rewritten to agree.selfremains only for an Application that genuinely talks to Vault itself.delivery: filerenders as a VSO-synced Secret mounted as files. The injector and its webhook leave the foundation once nothing uses them.rabbitmqjoins the grant union (0029) as a dynamic credential. An edge to arabbitmq-engine Process derives a per-consumer role, delivered like the database grant. This also retires the shared RabbitMQ KV that knowledge reads.<project>-systemand to the databases that are not<project>_db. The Move machinery is the one specified in the Stable Address round (Specify the Stable Address, the derived Move and the Infrastructure model, with their decision records #323). Chapter 60's claim that adoption "renames nothing" is corrected.knowledge_platform_db. hindsight's tables move into it during handover, and schemas keep them apart. In a database name, a hyphen in the Project name becomes an underscore. This settles the spelling A Project's name is unconstrained: it can derive kube-system, or a Vault name with two readings #244 asks about for estate-dashboard too.The ten needs no chapter covers
reconstructibleclaim and not a dataset.Localtraffic policy, which keeps the client address. stalwart moves fromhostPortto it, so there is one mechanism.gpuplacement dimension. A Process that requires a GPU derives one device request and the runtime class. The Platform document names the resource and the runtime class for each vendor. Nothing new is authored.lifecycle: scheduledis added. The Process authors its own cronschedule, which renders as a CronJob that never runs twice at once. It has the identity, grants and edges of any other Process. This settles the first gap in Declare the Collector in the model: a scheduled lifecycle, a cluster-wide read, and a namespace on snapshot facts #236.before: true. It runs to completion first, under the same hardening, and gets the volume mounts that A sidecar cannot mount the Process's volumes or run before it #271 settles. auth-api's download of its profiling agent moves into its image, because a download at start is an unpinned input.apirules, the network policy and the hardening for pods that carry the template's label, and hands the Process the pinned image references. agent-runner Pods, with their agent-gateway sidecar, are the live case.egress: internetwith a reason, from a closed vocabulary with no destinations. It renders as an allow to0.0.0.0/0minus the cluster and LAN ranges, which are Platform facts. No admission is required. This settles A Process cannot declare egress to the internet, so default-deny would cut every outbound call #270.mongoands3join the engine vocabulary, each with a backup image. An edge to ans3Process derives a bucket catalog: one bucket and one scoped key per consuming Project, mirroring the database catalog. grimoire is the first consumer. Mongo gets a database catalog only if a consumer needs one.ollama-heavyis the live case.Corrections the rewrite carries
Out of this ticket
The inventory found further needs this ticket did not list. They become their own tickets on the map:
Record
This answer is recorded here, on the ticket. The new concepts (Node Dataset, L4 Exposure, Admitted Capability, Spawn Template, Bucket Catalog,
scheduled) enterCONTEXT.md,spec/v1and their ADRs with the spec rewrite. That rewrite is not written now, because whether it lands as a v2 or as v1 reworked in place is still open.ExtraToast commentedon Oct 9, 2026
Decision 10 is refined by Decide how the model meets soft scheduling, spread, anti-affinity and parked replicas: a node dataset is declared on the node that holds it, not on a node capability, and mounting it derives a hard pin to that node.
ExtraToast commentedon Oct 12, 2026
Claims behind this decision that failed a source check
Find the claims behind the settled decisions that no source supports traced each third-party and live-estate claim this resolution rests on to a primary source. Three fail. This comment flags them; it does not reopen the decision. The evidence is in
docs/research/unsupported-claims.md, by claim id.rabbitmqgrant models something live. It does not. The agents-api annotation names a dynamic credential, but the template reads the static KV path; the dynamic credential was dropped after Vault revoked the user under a running pod.VaultDynamicSecretcan deliver the engine (C7 holds), so the grant is possible, but it introduces a credential kind no workload uses today.hostPort. stalwart runs on the Frankfurt node, which has no MetalLB pool and one public address, and MetalLB documents itself as generally not compatible with cloud providers. The public Traefik's Service there is<pending>and useshostPortfor the same reason. Low ports stay refused in this resolution, so stalwart has no modelled way out yet.enable_unprivileged_portson kernels from 4.11, for any pod off the host network. The hazardE_PRIVILEGED_PORT_UNDER_NONROOTguards does not exist on this substrate. Read from source, not yet drilled.Six more claims here hold only with a condition (C9, C12, C30, C32, C34, C36); the file states each.
ExtraToast commentedon Oct 12, 2026
The three failed claims flagged above are decided in Decide what each settled decision does about the claim under it that failed. Item 2 is reversed:
E_PRIVILEGED_PORT_UNDER_NONROOTis cut, after a drill showed a non-root process binding port 80 on this cluster, and the four image changes are dropped. Item 11 is refined: an L4 exposure names a Platform-declared L4 Address, which is a pool (a LoadBalancer Service) or a node's own address (hostPort, with the pin derived), so stalwart stays on the Frankfurt node's address. Item 7 stands, with its reason corrected and a rotation drill as its condition.ExtraToast commentedon Oct 12, 2026
The conditions flagged on this decision are settled in Decide which conditions on the settled decisions change the model. Item 16's
egress: internetexcludes a fixed non-public set, the tailnet range included, and reads no Platform range. Item 18's rule gains one clause: the Platform document names further Applications of the critical class. Item 12's device request is one of four shares. Item 7's role has no lifetime of its own; the lifetime is a fact of therabbitmqengine. Item 6 delivers no Vault token: an Application that talks to Vault takes theselfpath.