feat(build): generate agent-box.nix from split sources; extract settings daemon (#140)#143
Merged
Merged
Conversation
Add `aws/lightsail-template.yaml`, a separate (not a toggle) template that runs the same services.agent-box on AWS Lightsail for one flat monthly bundle price — folding compute, SSD, static IPv4, and a multi-TB transfer allowance into a single line item (no separate EBS/IPv4 charges). Lightsail has no NixOS blueprint and CloudFormation can't boot a custom image, so the box launches the Ubuntu 24.04 blueprint and converts itself to NixOS in-place with nixos-infect (pinned by commit). PROVIDER=lightsail is first-class in nixos-infect and imports the same virtualisation/amazon-image.nix the EC2 template uses, so the platform layer is the proven one; the baked configuration.nix is the EC2 config minus Spot/NAT64 plus the Lightsail bits (amazon-image import + GRUB on nvme). selfUpdate, the web-password-hash activation script, the first-boot WaitCondition signal, and the amazon-init disable all carry over. Design notes: - No VPC/subnet/IGW/SG/EIP/IAM — Lightsail manages networking; firewall is the Networking.Ports block (443 always, 22 when DebugSsh=true). - A static IP is always attached (free while attached, stable across stop/start). It attaches after instance creation, so to avoid a dependency cycle the box discovers its settled public IPv4 at runtime for the sslip.io hostname; Outputs report the same address via GetAtt. - Debug access is root SSH with the Lightsail default key (no SSM); infect logs at /var/log/agent-box-infect.log. Validation: cfn-lint passes and the generated configuration.nix parses as valid Nix; aws-ci.yml now lints both templates. The end-to-end nixos-infect bootstrap still needs a live deploy-test (documented as a follow-up, along with S3 publish + a Launch button), so this ships as a draft. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sw7j9FYnbChZJbYArxbhuo
modules/agent-box.nix is now GENERATED from modules/agent-box.nix.in + modules/src/* by bin/assemble-module.py, which re-embeds assets via @@include:...@@ markers. The deployed fetch is unchanged — CFN user-data and agent-box-update.service still builtins.fetchurl the one self-contained modules/agent-box.nix at a pinned sha256 (issue #51). We only decouple the *source* layout from that single *fetched artifact*, so linters/editors see real files instead of Nix strings. First slice: extract the ~1930-line settings daemon to modules/src/settings-daemon.py (now a lintable, py_compile-able file). The generated module is byte-identical to the previous hand-written one except a 4-line GENERATED banner — verified by diff — so no derivation changes. - bin/assemble-module.py: dependency-free generator; --check mode diffs the committed file against a fresh assembly. - flake: `nix run .#assemble` (regenerate in place) + check module-generated-up-to-date (fails on drift); CI runs the check. - module-single-file check still guards the OUTPUT is sibling-free. - .gitattributes marks the generated file; AGENTS.md documents the workflow. Validated: module-single-file, module-generated-up-to-date, multi-user, and the settings-page VM test (daemon serves + password-change flow) all green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sw7j9FYnbChZJbYArxbhuo
This was referenced Jul 22, 2026
Closed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part 1 of issue #140. Splits the module source for maintainability while keeping the deployed fetched artifact a single self-contained file.
What changes
modules/agent-box.nixis now generated frommodules/agent-box.nix.in+modules/src/*bybin/assemble-module.py, which re-embeds assets through@@include:...@@markers (dedent on extract, re-indent + Nix-escape on assemble).This slice extracts the ~1930-line settings daemon (≈45% of the file) to
modules/src/settings-daemon.py— now a real fileruff/py_compile/an editor can see, instead of a Nix''string.What does NOT change
The deploy path is untouched. CFN user-data and
agent-box-update.servicestillbuiltins.fetchurlthe onemodules/agent-box.nixat a pinned{rev, sha256}— same dual-stackraw.githubusercontent, same single hash, no unpack step (issue #51). I deliberately did not pursue the "multi-file fetch / tarball" alternative from the issue's second checkbox: it would reintroduce the IPv4-only + hash-fragility risk that design exists to avoid. We only decouple source layout from the fetched artifact.This is also the fix for what sank PR #98 (
readFile ./sibling, resolved on-box → 404 on next rebuild): the split is now resolved at build time, and the committed file stays sibling-free.Byte-identity
The generated module is byte-identical to the previous hand-written one except a 4-line GENERATED banner (verified by
diff), so no derivation changes and behavior is provably unchanged.Tooling / guards
nix run .#assemble— regenerate in place after editing the template orsrc/.module-generated-up-to-date— re-runs the generator in--checkmode and fails with a diff if the committed file is stale (wired into CI).module-single-filecheck still guards that the OUTPUT has no sibling refs..gitattributesmarks the filelinguist-generated;AGENTS.mddocuments the workflow.Validation
Green locally:
module-single-file,module-generated-up-to-date,multi-user, and thesettings-pageVM test (daemon serves the page + password-change flow end-to-end).Follow-up (part 2)
Separate PR: split the daemon's embedded CSS/JS into their own
.css/.jsfiles (unlocks eslint/prettier/stylelint), and pull the remaining shell scripts / Caddyfile / default AGENTS.md out. The generator's nested-include + per-host-escaping support is already in place for it.🤖 Generated with Claude Code