Skip to content

docs: chapter 16, dependencies and the derivation map - #59

Merged
ExtraToast merged 2 commits into
v1-pre-releasefrom
docs/ch16-derivation-map
Aug 31, 2026
Merged

docs: chapter 16, dependencies and the derivation map#59
ExtraToast merged 2 commits into
v1-pre-releasefrom
docs/ch16-derivation-map

Conversation

@ExtraToast

Copy link
Copy Markdown
Contributor

Chapter 16. Stacked on #58 — review that one first; this PR's base retargets to v1-pre-release automatically when #58 merges.

Chapter 10 defined what a Service declares. This chapter defines what those declarations produce, and gives the specification its one machine-checkable property.

Direction is the interesting part

Four things derive from a dependency edge read outbound: reconcile ordering, dependency coordinates, NetworkPolicy egress, co-test membership.

A fifth class derives from the same edges read inbound, and these are the derivations a Service can never declare locally, because no Service knows its own consumers:

inbound derivation evidence
a database and owning user per consumer init-databases.sh creates auth_db, agents_db, knowledge_db, n8n_db — one per Service claiming a Postgres credential. That is all 98 lines do.
NetworkPolicy ingress only the inbound set says who to admit
co-test membership 12 of ~25 test classes exercise auth-api with its consumers
browser origin allow-lists auth-api hand-maintains nine hostnames in AUTH_CORS_ALLOWED_ORIGINS
rotation blast radius "who breaks if I rotate this" is the inbound set of a secret path

This is why ADR-0014 puts co-test declarations on the test project, and why ADR-0015 is a hard dependency of this chapter — an inbound derivation is only computable over the composed union.

The map

50 nodes, 65 edges. Dense on purpose, and not meant to be read by eye — its value is that three properties are checkable by a script over the attribution table ADR-0016 already requires every Deliverable to carry.

This PR corrects a criterion the spec previously stated wrongly

00-overview.md claimed the acceptance criterion was "any node with two inbound arrows is a bled concern." That is false. A Deployment legitimately draws on image, config, claims, health and placement. Convergence on an object is normal; convergence on the same field of an object is the defect. Superseded by three properties stated properly:

  1. Totality — no Deliverable has in-degree zero. An object reachable from no declaration is hand-written and must become derived or enter a Bidirectional Ledger. This is what kb.jorisjonkers.dev violated seven ways over.
  2. Single authority — no field has two declaring sites. Checked against the attribution table rather than the diagram, since the diagram is object-level and this property is field-level. That granularity gap is deliberate: drawing it per-field would make the map unreadable without strengthening the check.
  3. No dead declarations — no declaration has out-degree zero.

Property 3 is the one that catches ceremony, and it would have caught two live examples: rollbackTargetRetention, validated and acknowledged identically by every service and read by no renderer; and platform.layer, which fed a registry and never placement while being wrong in 7 of 7 services.

Two worked traces

One exposure declaration → six artefacts, plus retiring the two conformance tests (route-auth-conformance.test.js, gatus-route-coverage.test.js) that existed only to detect when those six disagreed. Under property 1 they have nothing left to check, because the six share a single upstream.

One credential claim → nine derivations, the ninth being the provider-side database and user, computable only over the composed union.

Left open rather than guessed

The CORS derivation shape is right but the predicate is not established. An OIDC redirect flow — which GrafanaOidc, N8nOidc and RabbitMqOidc exercise — needs no CORS entry, so it is probably "inbound edges declaring a browser surface" rather than "all inbound edges". Recorded in the chapter to confirm against auth-api's actual CORS usage before implementing.

@ExtraToast ExtraToast added type: docs Documentation-only change. area: deploy homelab-deploy, deploy-v2, manifests, and rollout flow. component: config Configuration schema, defaults, or repo settings. labels Aug 31, 2026
@ExtraToast
ExtraToast force-pushed the docs/ch16-derivation-map branch 2 times, most recently from 5b39976 to 1a4f473 Compare August 31, 2026 11:19
Defines what declarations produce, and gives the specification its one
machine-checkable property.

The dependency edge is three fields. Four things derive from it read outbound:
reconcile ordering, dependency coordinates, network policy egress, and co-test
membership. A fifth class derives from the same edges read inbound, and those
are the derivations a service can never declare locally because no service
knows its own consumers:

- one database and owning user per consumer, which is all init-databases.sh
  does in 98 lines
- NetworkPolicy ingress, since only the inbound set says who to admit
- co-test suite membership, which is why 12 of ~25 test classes exercise
  auth-api with its consumers rather than alone
- browser origin allow-lists, which auth-api hand-maintains as nine hostnames
- rotation blast radius, which is the inbound set of a secret path

Includes the derivation map: 50 nodes, 65 edges, declared on the left and
derived on the right. It is dense deliberately and is not meant to be read by
eye -- its value is that three properties are checkable by a script over the
attribution table ADR-0016 already requires.

Corrects a criterion this specification previously stated wrongly. The overview
claimed "any node with two inbound arrows is a bled concern". That is false: a
Deployment legitimately draws on image, config, claims, health and placement.
Convergence on an object is normal; convergence on the same field of an object
is the defect. The three properties are now stated properly:

1. totality -- no deliverable has in-degree zero
2. single authority -- no field has two declaring sites
3. no dead declarations -- no declaration has out-degree zero

Property 3 is the one that catches ceremony, and it would have caught two live
examples: rollbackTargetRetention, validated and acknowledged by every service
and read by no renderer; and platform.layer, which fed a registry and never
placement while being wrong in 7 of 7 services.

Two worked traces: one exposure declaration producing six artefacts plus
retiring the two conformance tests that existed only to detect when those six
disagreed; and one credential claim producing nine, the ninth computable only
over the composed union.

Leaves one question open rather than guessing it. The CORS derivation shape is
right but the predicate is not established: an OIDC redirect flow needs no CORS
entry, so it is probably inbound edges declaring a browser surface rather than
all inbound edges. Recorded in the chapter to confirm before implementing.
The rewrite changed two nodes on the map and one worked trace.

config becomes env files (base plus cluster overlay), and gains a second
outbound edge: a secret placeholder resolves to an envFrom secretRef rather
than a literal env entry, so the env file now feeds both the Deployment and
the Secret.

claims becomes secrets.yml grants, carrying access and delivery. Its
derivations are unchanged in substance -- policy, auth role, secret, network
policy, container wiring -- but the declaration site moved out of the Service
document.

health becomes readiness plus liveness as siblings.

The credential trace is rewritten to the new two-file shape and gains the
bidirectional check the single-document form could not express: a delivery: env
grant with no placeholder is a dead grant, a placeholder with no grant is an
unauthorised reference, delivery: env with tolerates: reload is impossible, and
self-roll on a path other services read needs acknowledgement.

That last check is the one nothing in the estate has today.
secret/platform/observability holds the Prometheus token, the Discord webhook
and the Grafana client secret, and one CronJob rolls one of those keys.

Adds a row to the caught-defects table: a secret granted but never referenced
is property 3 again, a declaration with out-degree zero.

Revalidated: all three mermaid blocks resolve every edge endpoint, and the map
still passes its own property 3 -- every declaration has at least one outbound
edge.
@ExtraToast
ExtraToast force-pushed the docs/ch16-derivation-map branch from 1a4f473 to 7bd8c55 Compare August 31, 2026 13:37
@ExtraToast
ExtraToast changed the base branch from docs/ch10-service-intent to v1-pre-release August 31, 2026 13:37
@ExtraToast ExtraToast closed this Aug 31, 2026
@ExtraToast ExtraToast reopened this Aug 31, 2026
@ExtraToast
ExtraToast merged commit 6f28e6e into v1-pre-release Aug 31, 2026
6 checks passed
ExtraToast added a commit that referenced this pull request Aug 31, 2026
Lands the remainder of the v1 specification. Chapters 10 and 16 merged as #58
and #59; this carries 20, 30, 40, 50 and 60 together, plus the revisions those
chapters make to earlier ADRs and the examples.

Combined rather than stacked, because the stack could not be salvaged after the
first two squash-merges: each branch was cut from the pre-squash tip of the one
below, so rebasing them onto the merged base conflicted. Splitting them again
after the fact would also have produced PRs that do not stand alone -- chapters
50 and 60 revise 30 and 40 (prune semantics, class C's home, two resolved open
items), and the glossary correction touches all of them. #60 through #63 remain
as the per-chapter review record.

Chapter 20, the Resolved Deployment. One rule carries the specification: every
assignment is a pure function of Service Intent, the pinned Cluster Context and
the pinned locks. Nothing is allocated or remembered between renders. Applying
it exposed a hole in ADR-0003 -- taken literally it forbids declaring the
Service Id -- so identity is separated from pool: identity is declared and
checked, a pool is assigned. Not one live hostname derives from a Service Id, so
an exposure carries a name. Also corrects something said repeatedly and wrongly:
layer 2 assigns a selector and an affinity, never a node.

Chapter 30, the Deliverable Set. Closes the coverage question with a
measurement: 450 objects, 39 kinds, 329 files, split 364 intent-derived, 41
pack-delivered, 45 authored. 328 of the 364 have an adapter; the 36-object gap
is 14 that need only registering and 22 that need writing.

Chapter 40, composition. 26 estate-wide invariants across identity, references,
secrets and completeness. The unit of publication is a repository rather than a
domain, which resolves the homelab-collections split question without a
decision. Order-independence forces every collision to be an error rather than
a last-write-wins merge.

Chapter 50, lifecycle, with ADR-0019. Push delivery gated by
relationship-scoped aggregators; Flux keeps the foundation, because 18 of the 41
pack-delivered objects are HelmRelease and applying one with kubectl does
nothing without helm-controller. The cluster is the inventory: a deployer label
supplies what Flux gets from a Kustomization's record. Drift correction is an
in-cluster CronJob, not an Actions schedule, because the estate measured Actions
crons at four to seven runs per day regardless of the declared interval.

Chapter 60, setup and adoption. The ordering hazard is the point: Flux prunes,
so spec.prune must be set false BEFORE a Service's manifests leave the Flux
source, or adoption deletes it.

Also: the glossary was materially stale and is corrected -- Claim Mode described
a model that no longer exists, Contended Value conflated identity with pool, and
seven terms were missing. 39 terms to 47.

Four workflows, an Aggregator declaration, a Renovate custom manager, the
generated deployer RBAC, the reapply CronJob and a negative fixture. Every
workflow is one job with many steps, because jobs bill by the minute rounded up.

Audit at commit time: 11 mermaid blocks valid across three diagram types, 18
example files parsing, no broken relative links, no stale vocabulary.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: deploy homelab-deploy, deploy-v2, manifests, and rollout flow. component: config Configuration schema, defaults, or repo settings. type: docs Documentation-only change.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant