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 what each settled decision does about the claim under it that failed #366
C4, the LAN Traefik serves the wildcard too (Decide how the foundation's HelmReleases are rendered and owned). Whether cert-manager still retires, with a second ACME resolver, stored state and Cloudflare token on the LAN Traefik, or stays as the one issuer that hands both proxies a Secret.
C28, Alloy holds no host privilege (same decision). Whether log-collector leaves the Node Agent catalog and Alloy is declared as an ordinary Process.
C17, the Frankfurt node has no address pool (Decide how the model meets the live needs no chapter covers). How a Process on a node with one public address and no pool is exposed at L4: stalwart's mail ports and the public Traefik's 80 and 443 both use hostPort there today, and low ports stay refused.
C33, a non-root process can bind a low port on k3s (same decision). Whether E_PRIVILEGED_PORT_UNDER_NONROOT and the four image changes stay, on a hazard this substrate does not have. The claim is read from source and should be drilled first.
C8, no workload reads a RabbitMQ dynamic credential (same decision). Whether rabbitmq still joins the grant union, given that the estate dropped the dynamic credential after an outage.
Run on the live cluster (k3s v1.35.2+k3s1, node enschede-t1000-1) on 2026-10-12, in a throwaway namespace that was deleted afterwards. One pod, busybox:1.36, with runAsUser: 1000, runAsNonRoot: true, allowPrivilegeEscalation: false, capabilities.drop: [ALL], a read-only root file system and the RuntimeDefault seccomp profile, off the host network, running httpd -f -p 80.
id printed uid=1000 gid=1000.
CapPrm, CapEff and CapBnd in /proc/self/status were all zero.
netstat showed a listener on :::80, and a request to 127.0.0.1:80 got an HTTP answer.
C33 is therefore settled by test, not only by reading the source: on this substrate a non-root process with no capability binds a port below 1024.
Decisions
C4: every tier that terminates TLS issues its own certificate. cert-manager still retires. A tier with certificates: acme derives a resolver, a reconstructible claim for the ACME state and the Cloudflare token through VSO, on that tier's proxy. Nothing is shared between the two proxies, so no Secret, Certificate or hand-written TLSStore is needed, and the earlier decision that no admission webhook is rendered stands. The estate's Platform document declares the lan tier as listener: plain with certificates: none, which is not what runs and cannot run, because every hostname is under .dev; it is corrected to tls and acme. Both proxies run one replica today, which is what a resolver requires.
C28: Alloy is an ordinary Process.log-collector leaves the Node Agent catalog, which keeps four entries, all host-privileged: gpu-device, gpu-metrics, node-metrics and l4-speaker. Alloy is declared in the observability project with a cluster-scope api read on pods and their logs, admitted by the Platform document like any other API access (0092). Its replica count is its own and not one per node. The catalog's meaning stays "host-privileged", not "runs once per node".
C17: the Platform document declares named L4 Addresses, of two kinds. A pool is a range announced on one site's network segment and renders a LoadBalancer Service with the Local traffic policy, as decided before. A node address is a node's own address and renders hostPort on the Process, with a hard pin to that node derived from the declaration. An L4 exposure names an L4 Address by its name, the same shape as mounting a node dataset, so no Application names a node and no address is written in a project file. A port and protocol used twice on one node address is refused at build. The client-facing port stays a fact of the exposure, separate from the port the container listens on. stalwart's eight ports use the Frankfurt node address. hostPort is not a second mechanism an author picks: the kind of the L4 Address decides the rendering.
A tier names the L4 Address its proxy listens on. This is a fifth tier fact. The traefik adapter derives the proxy's ports from listener, as it already derives the entry points, so ports 80 and 443 are never authored. The public tier names the Frankfurt node address and the lan tier names an address in the Enschede pool.
C33: E_PRIVILEGED_PORT_UNDER_NONROOT is cut. The hazard it guards does not exist on this substrate, as the drill shows. auth-ui, agents-ui, AdGuard and stalwart keep the ports they listen on, and the four image changes are dropped. Chapter 14 records the fact once, under its substrate facts: k3s lets a pod that is off the host network bind any port without a capability. No authored Process can use the host network (only catalog node agents do), so the one exception to that fact reaches nothing an author writes. The cut lands with chapter 11's rewrite, after emf/ is deleted, in that chapter's own pull request: it widens what the build accepts, so it does not wait for the 1.0.0 contract pull request. 0020 loses its low-port clause in the same pull request.
C8: rabbitmq still joins the grant union as a dynamic credential, on corrected grounds. It models nothing live: the estate dropped the dynamic credential after Vault revoked a user under a running pod. The cause was the Vault Agent injector running once and never renewing the lease, and the injector retires. VSO renews a lease and then replaces the credential and restarts its consumers, the cycle the estate already runs for Postgres. The reasons to keep it are that it reuses the database grant's shape with no new grant kind, gives each consumer its own role, and retires the shared KV credential. The decision carries one condition: a drill rotates the credential under a connected consumer before chapter 17 is rewritten. If the drill fails, the question reopens with that result.
C43: both uid refusals stay, for two different reasons. A uid used twice is refused because one dashboard silently overwrites the other. A dashboard file without a uid is refused because its address would be the Kubernetes UID of its object, which changes whenever the object is recreated, as a namespace Move recreates it, and every saved link then breaks. The JSON file stays the one place a uid lives; the adapter does not derive one.
Consequences for other tickets
Decide how the model meets the live needs no chapter covers. Item 2 is reversed (decision 5). Item 11 is refined (decisions 3 and 4): "an address from a Platform-declared pool" becomes "a Platform-declared L4 Address", and stalwart does not move to a LoadBalancer Service. Item 7 stands with its reason corrected and a drill as its condition (decision 6).
Decide how the foundation's HelmReleases are rendered and owned. Item 2's catalog has four entries, not five (decision 2), and "every node derives log-collector" is dropped. Item 4's cert-manager retirement stands, with a resolver on every acme tier and not on the public one alone (decision 1). alloy joins the Applications the observability project declares.
Decide which conditions on the settled decisions change the model inherits four things. The one-replica limit (C2) and the shared Let's Encrypt allowance (C3) now apply to two proxies that issue the same names. A pool-kind L4 Address already names its site, which is where C19 starts. loki-canary (C29) cannot enter the Node Agent catalog, since the catalog stays host-privileged only. The RabbitMQ lease lifetime per mount (C9) is part of what the drill below reads.
JorisJonkers-dev/estate.platform.intent.yml declares the lan tier wrongly today; it is corrected when the tier vocabulary it uses is rewritten, not before.
Activity
ExtraToast commentedon Oct 12, 2026
Resolution
Settled with the maintainer on 2026-10-12, under Re-state the goals and non-goals against the scale premise: every live need gets a concept, and a refusal stays only where its hazard is real at this scale. Input:
docs/research/unsupported-claims.md, by claim id, and one drill run for this ticket.The drill behind C33
Run on the live cluster (k3s
v1.35.2+k3s1, nodeenschede-t1000-1) on 2026-10-12, in a throwaway namespace that was deleted afterwards. One pod,busybox:1.36, withrunAsUser: 1000,runAsNonRoot: true,allowPrivilegeEscalation: false,capabilities.drop: [ALL], a read-only root file system and theRuntimeDefaultseccomp profile, off the host network, runninghttpd -f -p 80.idprinteduid=1000 gid=1000.CapPrm,CapEffandCapBndin/proc/self/statuswere all zero./proc/sys/net/ipv4/ip_unprivileged_port_startread0.netstatshowed a listener on:::80, and a request to127.0.0.1:80got an HTTP answer.C33 is therefore settled by test, not only by reading the source: on this substrate a non-root process with no capability binds a port below 1024.
Decisions
certificates: acmederives a resolver, areconstructibleclaim for the ACME state and the Cloudflare token through VSO, on that tier's proxy. Nothing is shared between the two proxies, so no Secret,Certificateor hand-writtenTLSStoreis needed, and the earlier decision that no admission webhook is rendered stands. The estate's Platform document declares thelantier aslistener: plainwithcertificates: none, which is not what runs and cannot run, because every hostname is under.dev; it is corrected totlsandacme. Both proxies run one replica today, which is what a resolver requires.log-collectorleaves the Node Agent catalog, which keeps four entries, all host-privileged:gpu-device,gpu-metrics,node-metricsandl4-speaker. Alloy is declared in theobservabilityproject with a cluster-scopeapiread on pods and their logs, admitted by the Platform document like any other API access (0092). Its replica count is its own and not one per node. The catalog's meaning stays "host-privileged", not "runs once per node".Localtraffic policy, as decided before. A node address is a node's own address and rendershostPorton the Process, with a hard pin to that node derived from the declaration. An L4 exposure names an L4 Address by its name, the same shape as mounting a node dataset, so no Application names a node and no address is written in a project file. A port and protocol used twice on one node address is refused at build. The client-facing port stays a fact of the exposure, separate from the port the container listens on. stalwart's eight ports use the Frankfurt node address.hostPortis not a second mechanism an author picks: the kind of the L4 Address decides the rendering.traefikadapter derives the proxy's ports fromlistener, as it already derives the entry points, so ports 80 and 443 are never authored. The public tier names the Frankfurt node address and thelantier names an address in the Enschede pool.E_PRIVILEGED_PORT_UNDER_NONROOTis cut. The hazard it guards does not exist on this substrate, as the drill shows. auth-ui, agents-ui, AdGuard and stalwart keep the ports they listen on, and the four image changes are dropped. Chapter 14 records the fact once, under its substrate facts: k3s lets a pod that is off the host network bind any port without a capability. No authored Process can use the host network (only catalog node agents do), so the one exception to that fact reaches nothing an author writes. The cut lands with chapter 11's rewrite, afteremf/is deleted, in that chapter's own pull request: it widens what the build accepts, so it does not wait for the 1.0.0 contract pull request. 0020 loses its low-port clause in the same pull request.rabbitmqstill joins the grant union as a dynamic credential, on corrected grounds. It models nothing live: the estate dropped the dynamic credential after Vault revoked a user under a running pod. The cause was the Vault Agent injector running once and never renewing the lease, and the injector retires. VSO renews a lease and then replaces the credential and restarts its consumers, the cycle the estate already runs for Postgres. The reasons to keep it are that it reuses the database grant's shape with no new grant kind, gives each consumer its own role, and retires the shared KV credential. The decision carries one condition: a drill rotates the credential under a connected consumer before chapter 17 is rewritten. If the drill fails, the question reopens with that result.uidrefusals stay, for two different reasons. Auidused twice is refused because one dashboard silently overwrites the other. A dashboard file without auidis refused because its address would be the Kubernetes UID of its object, which changes whenever the object is recreated, as a namespace Move recreates it, and every saved link then breaks. The JSON file stays the one place auidlives; the adapter does not derive one.Consequences for other tickets
log-collector" is dropped. Item 4's cert-manager retirement stands, with a resolver on everyacmetier and not on the public one alone (decision 1).alloyjoins the Applications theobservabilityproject declares.uidis the one in decision 7.loki-canary(C29) cannot enter the Node Agent catalog, since the catalog stays host-privileged only. The RabbitMQ lease lifetime per mount (C9) is part of what the drill below reads.JorisJonkers-dev/estate.platform.intent.ymldeclares thelantier wrongly today; it is corrected when the tier vocabulary it uses is rewritten, not before.docs/research/unsupported-claims.mdlists C33 under "Settle by drill". The drill is recorded above; the file is not edited for it.Record
This answer is recorded here, on the ticket. Nothing is written into
spec/v1,CONTEXT.mdor the register now. The chapter pull requests carry it:poolandnode) entersCONTEXT.mdwith chapter 12, and the tier's fifth fact with chapter 14.feat(model): a Process may listen below 1024, because the substrate b…