feat(nix): expose nixosModules.default from the flake #50

Merged
weiwen merged 2 commits from sandcastle/issue-48 into main 2026-07-06 22:09:53 +08:00
Owner

Summary

  • Adds nixos/module.nix with a services.evie NixOS option that installs evie as a hardened systemd service
  • Freeform settings attrset serialised to TOML via pkgs.formats.toml {}; tracks the Rust config surface without per-option churn
  • botTokenFile / apiKeyFile options use systemd LoadCredential so secrets never enter the Nix store; a Python ExecStartPre script patches values into the runtime config at /run/evie/config.toml
  • Service hardening: DynamicUser, ProtectSystem=strict, ProtectHome=read-only, NoNewPrivileges, PrivateTmp, StateDirectory/RuntimeDirectory managed by systemd
  • nixosModules.default exposed via the curried { self } pattern so the default package carries the pi-wrapping postInstall path without requiring an overlay
  • flake.nix merges nixosModules outside eachDefaultSystem using //
  • CONTEXT.md updated to document the new output

Decisions

  • settings-style freeform (vs full typed options): lower maintenance burden as the Rust config evolves
  • LoadCredential + Python patch: secrets stay off disk/store; Python stdlib re handles arbitrary secret characters safely
  • Curried { self } module: standard flake pattern; lets the default package be the flake's own built evie (with pi on PATH) without requiring user-side overlay configuration

Closes #48

## Summary - Adds `nixos/module.nix` with a `services.evie` NixOS option that installs evie as a hardened systemd service - Freeform `settings` attrset serialised to TOML via `pkgs.formats.toml {}`; tracks the Rust config surface without per-option churn - `botTokenFile` / `apiKeyFile` options use systemd `LoadCredential` so secrets never enter the Nix store; a Python `ExecStartPre` script patches values into the runtime config at `/run/evie/config.toml` - Service hardening: `DynamicUser`, `ProtectSystem=strict`, `ProtectHome=read-only`, `NoNewPrivileges`, `PrivateTmp`, `StateDirectory`/`RuntimeDirectory` managed by systemd - `nixosModules.default` exposed via the curried `{ self }` pattern so the default package carries the `pi`-wrapping `postInstall` path without requiring an overlay - `flake.nix` merges `nixosModules` outside `eachDefaultSystem` using `//` - `CONTEXT.md` updated to document the new output ## Decisions - **`settings`-style freeform** (vs full typed options): lower maintenance burden as the Rust config evolves - **`LoadCredential` + Python patch**: secrets stay off disk/store; Python stdlib `re` handles arbitrary secret characters safely - **Curried `{ self }` module**: standard flake pattern; lets the default package be the flake's own built evie (with `pi` on PATH) without requiring user-side overlay configuration Closes #48
feat(nix): expose nixosModules.default from the flake
All checks were successful
CI / check (pull_request) Successful in 1m28s
PR Triage — label changes-requested reviews / triage-review (pull_request) Successful in 1s
3dcff023e9
Adds nixos/module.nix providing a services.evie NixOS option that:
- Runs evie as a hardened systemd service (DynamicUser, ProtectSystem=strict,
  ProtectHome=read-only, NoNewPrivileges, PrivateTmp)
- Accepts a freeform `settings` attrset serialised to TOML via
  pkgs.formats.toml {}; tracks the Rust config surface with no per-option
  churn
- Keeps secrets out of the Nix store via botTokenFile / apiKeyFile options
  that use systemd LoadCredential; a Python preStart script patches the
  values into the runtime config at /run/evie/config.toml
- Defaults the package to self.packages.${system}.default via a curried
  { self } outer argument so pi is already on PATH (postInstall wrapping)

flake.nix merges nixosModules outside eachDefaultSystem (system-agnostic)
using the // operator. CONTEXT.md updated to document the new output.

Closes #48

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
fix(nix): correct secret escaping and fail on missing config field
All checks were successful
CI / check (pull_request) Successful in 1m17s
PR Triage — label changes-requested reviews / triage-review (pull_request) Successful in 1s
3f9f95bac9
The preStart TOML patcher passed the already-escaped secret through re.sub
as a replacement *string*, which reinterprets backslashes and thus
double-unescapes any value containing `\` — corrupting the secret it was
meant to protect. Switch to a replacement function (re.subn + lambda) so
backslashes are treated literally.

Also exit with a clear message when the target field is absent from the
config, instead of silently writing nothing and leaving evie to crash-loop
on an empty bot_token / api_key.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
weiwen merged commit 7ae8dd522e into main 2026-07-06 22:09:53 +08:00
weiwen deleted branch sandcastle/issue-48 2026-07-06 22:09:54 +08:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
weiwen/evie!50
No description provided.