Brainstorm happened; design at 2026-05-15-deployment-responsibility-design.md. Handoff doc stays as the historical framing. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
11 KiB
Handoff — brainstorm deployment responsibility (left4me vs. ckn-bw)
Status
Resolved 2026-05-15 — the brainstorming session happened and produced
docs/superpowers/specs/2026-05-15-deployment-responsibility-design.md.
Read that for the answer. The runtime-state relocation
(2026-05-15-runtime-state-relocation-design.md) shipped as a prereq;
the design lands hardening drop-ins, sudoers, sysctl, and helpers as
symlinks into the (now root-owned) /opt/left4me/src/deploy/...
checkout, while base unit bodies and per-host shape stay bw-managed.
This doc is kept as the historical framing — the question that opened the brainstorm, the operator's leaning, and the candidate options that got evaluated. The actual landed answer is the design doc.
The question
How should left4me and ckn-bw split responsibility for the host's deployment?
Not a fresh question. The original deployment design at
docs/superpowers/specs/2026-05-06-left4me-deployment-design.md
already laid out the canonical shape: deploy/files/ in the left4me
repo mirrors target filesystem paths for root-owned deployment
artifacts (systemd units, sudoers, helpers, env templates);
"production config management can own both env files directly"
(line 91). The implicit model: left4me defines the deployment
artifacts; ckn-bw integrates them onto the host. That spec also
defined a self-contained deploy/deploy-test-server.sh so the
deployment could be exercised without ckn-bw at all.
Over time, more and more of those artifacts migrated into ckn-bw's
bundles/left4me/ — specifically:
- systemd unit definitions are now emitted by the
systemd/unitsreactor in~/Projekte/ckn-bw/bundles/left4me/metadata.py(the hardening refactor we just landed reinforced this). - sysctl options ended up in ckn-bw
bundles/left4me/metadata.pydefaults(just landed too). - sudoers exists in both repos (left4me
deploy/files/.../sudoers.d/left4me- ckn-bw verbatim mirror).
- Privileged helpers moved BACK to left4me as part of deploy-dir-rethink
(commit
5284e28) —scripts/{libexec,sbin}/. Pattern works: left4me defines, ckn-bw deploys viainstall_left4me_scripts.
So the trajectory has been mixed: helpers re-converged on left4me (good, matches 2026-05-06); systemd units + sysctl drifted into ckn-bw (away from 2026-05-06). The brainstorm reconciles this.
The question: should we return to the 2026-05-06 model
end-to-end — every deployment artifact lives in left4me's
deploy/files/, ckn-bw becomes a thin integrator — or is the
current mixed shape the right answer for some artifact classes?
Operator's leaning
Security-related artifacts belong in the left4me repo, owned by the project; ckn-bw is responsible for integrating them into the host (deploying them to the right paths, restarting affected units, etc.) but doesn't author them.
Concretely the operator's preference (from session 2026-05-15): "security-related stuff should be bundled in this repo and ckn-bw is responsible for integrating it into the server."
Why we're doing this
Background from the hardening-refactor session
(docs/superpowers/specs/2026-05-15-hardening-refactor-design.md,
"Approach" section). We considered two shapes for the hardening
landing:
- A — hardening directives inline in ckn-bw's
systemd/unitsreactor (the path we took) - B — hardening as drop-in
.conffiles living in left4me'sdeploy/files/etc/systemd/system/<unit>.d/, ckn-bw deploys them (consistent with 2026-05-06'sdeploy/files/model)
We picked A for the hardening refactor because B implied a broader configmgmt responsibility reshape that deserved its own session. That session is this one.
The motivating arguments for B (this brainstorming session evaluates them seriously):
- Hardening is application knowledge. Knowing srcds is i386,
that
MemoryDenyWriteExecute=truebreaks Source's text relocations, that web's sudo path is incompatible withPrivateUsers=true— all of this is left4me's domain, not ckn-bw's. ckn-bw shouldn't need to understand the threat model. - Test-artifact = production-artifact. The Test 7 drop-in from the hardening test plan literally is the file we'd want deployed. With B, there's no translation step.
- Repo self-containment for security review. A reviewer of left4me sees the threat model in code form without needing to read the configmgmt repo.
- Easier coordination with the
build-overlay-unitrefactor (queued). That unit's hardening profile can ship in its own drop-in inline with the unit template.
The counter-argument:
- Coupling cost. A change to a directive may require redeploying
via ckn-bw, which means a cross-repo coordination cycle (edit
left4me → commit → push → ckn-bw
bw apply). Today the same is true (edit ckn-bw → push → apply); just the which repo changes.
What "security-related" likely means
Enumerate during the brainstorm. Initial candidates:
- systemd unit hardening directives — currently in
ckn-bw
bundles/left4me/metadata.pyHARDENING_COMMON/HARDENING_SERVER/HARDENING_WEB. Strong candidate for left4me. - sysctl drop-ins — currently
kernel.yama.ptrace_scope=2in ckn-bw's left4me bundledefaults(sysctl/kernel/yama/ptrace_scope). Strong candidate for left4me. - sudoers — already in
left4me/deploy/files/etc/sudoers.d/left4me- a verbatim mirror in
ckn-bw/bundles/left4me/files/etc/sudoers.d/left4me. Already mostly left4me-owned; redundancy worth resolving.
- a verbatim mirror in
- Privileged helper scripts — already in
left4me/scripts/{libexec,sbin}/, ckn-bw deploys them viainstall_left4me_scripts. Already left4me-owned. The pattern works. - systemd unit BASE definitions (
User=,ExecStart=,Restart=, resource limits) — currently in ckn-bw's reactor. Open question: is this application knowledge or infrastructure knowledge? They depend on the application's binary paths, env files, restart semantics — all application knowledge. Probably also belongs to left4me. - AppArmor profiles (if we add them later — deferred from the defenses survey). Application knowledge.
/etc/left4me/host.env/web.envtemplating — ckn-bw owns these today because they're templated via mako from node metadata (per-host overrides). Probably stays in ckn-bw.- User/group creation — kernel-side infrastructure, no application knowledge needed. Stays in ckn-bw.
- Package installation (apt). Stays in ckn-bw.
- Firewall rules — depend on per-instance port ranges
(
LEFT4ME_PORT_RANGE_*); could be either. Worth discussing. - Nginx vhost — same: depends on app-specific routes.
Mechanism: how does ckn-bw "integrate"?
Brainstorm the deploy mechanism. Candidates (already partially sketched in the hardening-refactor design doc's earlier draft, before it was reverted to the inline-in-reactor approach):
- Symlinks. ckn-bw creates symlinks like
/etc/systemd/system/left4me-server@.service.d/10-hardening.conf→/opt/left4me/src/deploy/files/etc/systemd/system/.../10-hardening.conf. Editing the file in the repo +systemctl daemon-reloadpicks it up. Cleanest for "ckn-bw doesn't author." - File copy via
filesentries. ckn-bwfiles = {...}reads from/opt/left4me/src/deploy/files/...(post-git_deploy) and copies to the target. Standard idiom. Two-place state. - Glob-walker action. A small ckn-bw action walks
deploy/files/tree and mirrors paths to root. - Bundle inclusion / left4me-as-bundle. Left4me's
deploy/becomes its own bundlewrap bundle that ckn-bw imports. Strongest decoupling; requires bundlewrap bundle conventions.
Each has different implications for: triggers (which units restart when which files change), drift detection, rollback semantics.
Migration / coexistence path
Brainstorm: how do we get from the current state to the new state without breaking things?
- Inventory: every artifact ckn-bw currently emits/ships for left4me
(the
systemd/unitsreactor entries, sysctl defaults, sudoers mirror, file deploy actions, etc.). - For each: stays, moves, or split (some in each).
- Mechanism rollout: pick one (symlinks vs. file copy vs. ...) and apply it consistently.
- Test-driven: pick one artifact as the canary (probably the sysctl drop-in — smallest), validate the mechanism end-to-end, then migrate the others.
Key sub-questions for the brainstorm
- Is the unit's BASE definition application knowledge? If yes,
ckn-bw's
systemd/unitsreactor shrinks dramatically — to maybe one line per unit ("ckn-bw, deploy this file as a unit"). If no, we have a more delicate split. - What about the user/group definitions? Infrastructure-side
today. But the application defines that
left4me(uid 980) exists; ckn-bw just creates it. Could move. - Per-host configuration (gunicorn worker count, port ranges, CPU pinning): these are per-host overrides ckn-bw computes from node metadata. Stays in ckn-bw (or whatever owns deployment-time parameterization).
- Test infrastructure:
deploy/tests/test_deploy_artifacts.pyasserts left4me's reference units match the deployed form. If left4me starts owning the deployed form, those tests get stronger (no longer "reference vs. live" drift; the file indeploy/files/is the live form). - Drift / observability: how do we know the deployed state
matches the repo? Today
bw apply+ git diff is the source of truth. Same applies; mechanism details vary. - Rollback semantics: removing a drop-in is one
rmaway; the base unit is preserved. Same applies to reverting the left4me-side commit and re-applying.
Prereqs (must land before this brainstorming session)
- uid-collapse refactor — queued in
docs/superpowers/plans/2026-05-15-uid-collapse.md. Settles the user model first so the deployment-responsibility brainstorm doesn't have to juggle a moving user definition.
Out of scope for the brainstorm
- The hardening composition itself (already settled, deployed, verified).
- The
build-overlay-unittemplate unit refactor (docs/superpowers/specs/2026-05-15-build-overlay-unit-design.md) — both this brainstorm and the build-overlay-unit refactor benefit from settling responsibility first. Sequencing TBD; the brainstorm should consider whether to land before or after build-overlay-unit. - The application code itself (
l4d2web,l4d2host) — that's always been left4me-owned.
Pointers
- Original deployment design (the model to revisit):
docs/superpowers/specs/2026-05-06-left4me-deployment-design.md - Hardening refactor design (motivation; the deferred reshape):
docs/superpowers/specs/2026-05-15-hardening-refactor-design.md - Hardening refactor plan (what got landed):
docs/superpowers/plans/2026-05-15-hardening-refactor.md - Defenses survey (mentions AppArmor, deferred):
docs/superpowers/specs/2026-05-15-hardening-defenses-survey.md - Test plan + executed results:
docs/superpowers/specs/2026-05-15-hardening-test-plan.md - uid-collapse plan (prereq):
docs/superpowers/plans/2026-05-15-uid-collapse.md - deploy-dir-rethink (recent reshape that moved scripts into left4me;
background on the current
deploy/tree):docs/superpowers/plans/2026-05-15-deploy-dir-rethink.md(or2026-05-15-deploy-dir-rethink-design.md) - Live ckn-bw bundle (the thing being rethought):
~/Projekte/ckn-bw/bundles/left4me/