Compare commits
9
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a9ae07d5c1 | ||
|
|
f505100bb4 | ||
|
|
aff22e3a0f | ||
|
|
b13b774738 | ||
|
|
43fe37d4e2 | ||
|
|
a98e637ce2 | ||
|
|
0e194e5d44 | ||
|
|
68737a513f | ||
|
|
e76fc00a98 |
@@ -0,0 +1,68 @@
|
|||||||
|
# Host Naming Discrepancy Report
|
||||||
|
|
||||||
|
This report compares the current `modules/hosts` tree against the naming rules for new hosts in `hosts.instructions.md`.
|
||||||
|
|
||||||
|
## Rules Checked
|
||||||
|
|
||||||
|
- The host directory slug should be the canonical host identity.
|
||||||
|
- A directory-backed host should prefer `default.nix` as its main entrypoint.
|
||||||
|
- The canonical slug should line up with the primary `hostname` binding, `flake.nixosConfigurations.<slug>`, and `networking.hostName`.
|
||||||
|
- A host-local Home Manager module should use the same slug.
|
||||||
|
- A standalone Home Manager configuration should prefer the key `"<username>@<slug>"`.
|
||||||
|
- Host-local helper module names should be prefixed by the host slug.
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
- Recently normalized: `john-kde`, `omen-nixos`.
|
||||||
|
- Hosts that fit the new rules closely: `janus`, `john-kde`, `omen-nixos`, `test-nix`.
|
||||||
|
- Hosts with notable discrepancies: `john-p14s`, `soteria`.
|
||||||
|
- Approved exception: `john-pc` directory uses immutable deployed slug `john-pc-ubuntu` for exported keys.
|
||||||
|
|
||||||
|
## Detailed Discrepancies
|
||||||
|
|
||||||
|
### `john-p14s`
|
||||||
|
|
||||||
|
- The main host export uses the canonical slug: `flake.nixosConfigurations.john-p14s`.
|
||||||
|
- The host-local helper modules are named `p14sConfiguration` and `p14sHardware`, which drop the `john-` prefix and therefore do not use the full host slug.
|
||||||
|
- `networking.hostname` uses the correct host value, but the option name differs from the dominant `networking.hostName` spelling used elsewhere in this tree.
|
||||||
|
|
||||||
|
### `john-pc`
|
||||||
|
|
||||||
|
- Approved exception for immutable deployed hostname:
|
||||||
|
- Directory slug is `john-pc`, but exported keys and hostname bindings intentionally use `john-pc-ubuntu`.
|
||||||
|
- `hostname` binding, `flake.modules.homeManager.<name>`, and `flake.homeConfigurations` are aligned to `john-pc-ubuntu`.
|
||||||
|
- Shared SSH alias intentionally remains `john-pc-ubuntu` in `modules/services/ssh.nix`.
|
||||||
|
|
||||||
|
### `omen-nixos`
|
||||||
|
|
||||||
|
- No active discrepancy after normalization:
|
||||||
|
- The directory slug, `flake.nixosConfigurations` key, host-local module keys, and `networking.hostName` are now aligned to `omen-nixos`.
|
||||||
|
|
||||||
|
### `john-kde`
|
||||||
|
|
||||||
|
- No active discrepancy after normalization:
|
||||||
|
- The directory slug, `hostname` binding, host-local Home Manager module key, and standalone Home Manager key are now aligned to `john-kde`.
|
||||||
|
|
||||||
|
### `soteria`
|
||||||
|
|
||||||
|
- Naming is mostly coherent across directory slug, `hostname`, `flake.nixosConfigurations.soteria`, and `flake.modules.homeManager.soteria`.
|
||||||
|
- The standalone Home Manager export is `flake.homeConfigurations.soteria`, which does not follow the preferred `"<username>@<slug>"` form.
|
||||||
|
- The main host entrypoint is `soteria.nix` instead of the preferred `default.nix`.
|
||||||
|
|
||||||
|
## No Discrepancy Found
|
||||||
|
|
||||||
|
### `janus`
|
||||||
|
|
||||||
|
- Directory slug, `hostname`, `flake.nixosConfigurations.janus`, and `networking.hostName` are aligned.
|
||||||
|
- The host-local helper module `janus-ca` is clearly prefixed by the host slug.
|
||||||
|
|
||||||
|
### `test-nix`
|
||||||
|
|
||||||
|
- The single-file host uses the same slug for the file stem, `hostname`, and `flake.nixosConfigurations.test-nix`.
|
||||||
|
- It does not define `networking.hostName`, but it does not contradict the slug anywhere else.
|
||||||
|
|
||||||
|
## Follow-up Candidates
|
||||||
|
|
||||||
|
- Rename `john-p14s` helper module keys to include the full host slug (`john-p14s-*`) if strict slug consistency is desired.
|
||||||
|
- Decide whether `soteria` should move to `default.nix` and whether its standalone Home Manager key should include `john@`.
|
||||||
|
- If `john-pc-ubuntu` ever becomes changeable, decide whether to rename the directory to match or keep this as a permanent exception.
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
---
|
||||||
|
description: "Use when defining or modifying hosts and host-local data under modules/hosts. Explains how this repo exports nixosConfigurations and homeConfigurations, when to create flake.modules.nixos or flake.modules.homeManager helpers, and how secrets, defaults, keys, and hardware files fit into a host definition."
|
||||||
|
applyTo: 'modules/hosts/**/*.nix, modules/hosts/**/secrets.yaml, modules/hosts/**/defaults.json, modules/hosts/**/fingerprint, modules/hosts/**/*.pub'
|
||||||
|
---
|
||||||
|
|
||||||
|
# Host Definitions
|
||||||
|
|
||||||
|
Host files under `modules/hosts` are flake entrypoints, not just loose Nix snippets. Because `modules` is auto-imported through `import-tree` and `flake-file`, files here usually export one or more of these attributes:
|
||||||
|
|
||||||
|
- `flake.nixosConfigurations.<name>` for a bootable NixOS machine or container.
|
||||||
|
- `flake.homeConfigurations.<name>` for a standalone Home Manager target.
|
||||||
|
- `flake.modules.nixos.<name>` or `flake.modules.homeManager.<name>` when a host is split into reusable host-local modules that are then assembled by a nearby entrypoint.
|
||||||
|
|
||||||
|
Follow the existing host patterns in this tree:
|
||||||
|
|
||||||
|
- Keep the host entrypoint file focused on composing modules into `nixosSystem` or `homeManagerConfiguration`.
|
||||||
|
- If a host has substantial machine-specific logic, put that logic in sibling files such as `configuration.nix` or `hardware.nix`, export them as `flake.modules.nixos.<name>`, and have the entrypoint import those modules.
|
||||||
|
- For Home Manager only hosts, define `flake.modules.homeManager.<name>` and then expose a matching `flake.homeConfigurations` entry.
|
||||||
|
- Prefer composing from shared modules in `self.modules.nixos` or `inputs.self.modules.homeManager` instead of re-implementing shared behavior inline.
|
||||||
|
|
||||||
|
Treat host-local data as part of the host definition:
|
||||||
|
|
||||||
|
- Keep hardware-specific settings in the host directory, typically in `hardware.nix`.
|
||||||
|
- Keep install-time or machine-specific configuration in the same host directory, typically in `configuration.nix`.
|
||||||
|
- Keep host-local secret references beside the host and wire them through `sops.defaultSopsFile` and `mysops.hostSecretFile` instead of pointing at unrelated locations.
|
||||||
|
- Preserve nearby auxiliary files such as `defaults.json`, fingerprints, public keys, and `secrets.yaml`; these are part of the host contract.
|
||||||
|
|
||||||
|
For host-adjacent data files:
|
||||||
|
|
||||||
|
- Keep `secrets.yaml` scoped to the host that consumes it; do not reuse another host's secret file as a shortcut.
|
||||||
|
- Treat files such as `defaults.json`, `fingerprint`, and `*.pub` as inputs consumed by the host module. Update references together with the data when changing paths or filenames.
|
||||||
|
- Avoid moving or renaming these files unless the corresponding Nix references are updated in the same change.
|
||||||
|
|
||||||
|
Naming in this tree is not perfectly uniform, so preserve existing interfaces unless the task is explicitly a rename:
|
||||||
|
|
||||||
|
- The directory name, exported flake key, and `networking.hostName` may differ.
|
||||||
|
- Some hosts export from `default.nix`, while others keep the main definition in a differently named file such as `soteria.nix`.
|
||||||
|
- Do not normalize names or move files just for consistency unless the user asks for that structural change.
|
||||||
|
|
||||||
|
Naming rules for new hosts:
|
||||||
|
|
||||||
|
- Treat the host directory name as the canonical host slug for new work.
|
||||||
|
- Prefer `modules/hosts/<slug>/default.nix` as the main entrypoint for a host directory.
|
||||||
|
- Use the same slug for the primary `hostname` binding, `flake.nixosConfigurations.<slug>`, and `networking.hostName` unless the task explicitly requires a different deployed hostname.
|
||||||
|
- If the host exports a host-local Home Manager module, name it `flake.modules.homeManager.<slug>`.
|
||||||
|
- For standalone Home Manager configurations, prefer `flake.homeConfigurations."<username>@<slug>"` so the exported key still carries the host slug.
|
||||||
|
- Name host-local helper modules with the same slug as a prefix, for example `flake.modules.nixos.<slug>-hardware` or `flake.modules.nixos.<slug>-configuration`, to make ownership obvious and avoid collisions with shared modules.
|
||||||
|
- Keep host-local secrets and auxiliary files under the same host slug directory. Do not point a new host at another host's path just because the contents are similar.
|
||||||
|
|
||||||
|
Allowed exception for immutable deployed hostnames:
|
||||||
|
|
||||||
|
- If the deployed hostname is externally constrained and cannot change, use that deployed hostname as the canonical slug for exported keys and `hostname` bindings, even if the directory name differs.
|
||||||
|
- In that case, keep the exception explicit in comments or reports so future cleanup work does not accidentally rename a live hostname contract.
|
||||||
|
|
||||||
|
When working in existing hosts that predate these rules:
|
||||||
|
|
||||||
|
- Preserve the current public names by default.
|
||||||
|
- If the user asks for a rename or cleanup, update the directory slug, exported flake keys, host-local module names, and `networking.hostName` together in one change so the host identity stays coherent.
|
||||||
|
|
||||||
|
Use nearby hosts as composition examples:
|
||||||
|
|
||||||
|
- `john-p14s` splits the reusable machine logic into `configuration.nix` and `hardware.nix`, exports `flake.modules.nixos.p14sConfiguration` and `flake.modules.nixos.p14sHardware`, and assembles them from `default.nix`.
|
||||||
|
- `janus` defines a host-local reusable module (`flake.modules.nixos.janus-ca`) in the same file that also exports the final `flake.nixosConfigurations.janus` host.
|
||||||
|
- `john-pc` is a Home Manager target, so it exports a `flake.modules.homeManager` module and a `flake.homeConfigurations` entry rather than a `nixosConfiguration`.
|
||||||
|
- `soteria` combines a NixOS host, a host-local Home Manager module, and host-local secrets in one host directory.
|
||||||
|
|
||||||
|
When adding or changing a host:
|
||||||
|
|
||||||
|
- Mirror the local style first. Small hosts may define the full configuration inline; larger hosts should split reusable modules out.
|
||||||
|
- Keep `let` bindings such as `username`, `hostname`, `flakeDir`, and package aliases near the top when the file already follows that pattern.
|
||||||
|
- Add shared behavior by importing existing modules, not by copying option blocks between hosts.
|
||||||
|
- If the change affects both system and Home Manager state for a host, update both sides in the same host area when that host already models both.
|
||||||
|
|
||||||
|
For reviews and answers, distinguish between these layers:
|
||||||
|
|
||||||
|
- host entrypoint wiring in `modules/hosts/...`
|
||||||
|
- reusable shared modules in `modules/nixos`, `modules/programs`, `modules/services`, and `modules/users`
|
||||||
|
- host-local modules exported from a host directory and then consumed by its entrypoint
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
---
|
||||||
|
description: "Use when defining or modifying reusable Nix modules outside modules/hosts. Covers flake.modules.nixos, flake.modules.homeManager, and flake.factory.user exports, and explains how shared modules differ from concrete host wiring."
|
||||||
|
applyTo: 'modules/nixos/**/*.nix, modules/users/**/*.nix, modules/features/**/*.nix, modules/programs/**/*.nix, modules/services/**/*.nix, modules/nix-tools/**/*.nix'
|
||||||
|
---
|
||||||
|
|
||||||
|
# Shared Modules
|
||||||
|
|
||||||
|
Files outside `modules/hosts` generally define reusable building blocks, not concrete machines. In this repo, those files usually export one of these surfaces:
|
||||||
|
|
||||||
|
- `flake.modules.nixos.<name>` for reusable NixOS modules.
|
||||||
|
- `flake.modules.homeManager.<name>` for reusable Home Manager modules.
|
||||||
|
- `config.flake.factory.user` for factories that generate per-user module sets.
|
||||||
|
- `flake.meta.*` when the file owns reusable metadata that other modules consume.
|
||||||
|
|
||||||
|
Keep the boundary between shared modules and hosts clear:
|
||||||
|
|
||||||
|
- Shared modules should not define `flake.nixosConfigurations.<name>` or `flake.homeConfigurations.<name>`; those belong in `modules/hosts`.
|
||||||
|
- Prefer generic options, imports, and composition over host-specific literals.
|
||||||
|
- If a setting only makes sense for one machine, keep it in that host directory instead of moving it into a shared module.
|
||||||
|
- When a host imports a shared module, treat the shared module as part of the stable interface that multiple hosts may depend on.
|
||||||
|
|
||||||
|
Follow the existing export patterns in this repo:
|
||||||
|
|
||||||
|
- Simple reusable modules may export a single module directly, such as `flake.modules.nixos.games`.
|
||||||
|
- Cross-cutting features often live under `modules/features` even when they export `flake.modules.nixos.*` or `flake.modules.homeManager.*`.
|
||||||
|
- Program and service integrations commonly export one or both module types from a single file.
|
||||||
|
- User definitions under `modules/users` may export metadata plus paired NixOS and Home Manager modules for the same user.
|
||||||
|
|
||||||
|
For user modules specifically:
|
||||||
|
|
||||||
|
- Keep reusable user facts under `flake.meta.users.<name>` when other modules need to reference them.
|
||||||
|
- Prefer deriving the NixOS side from `self.factory.user` when the file already follows that pattern.
|
||||||
|
- Keep the user-facing Home Manager module in `flake.modules.homeManager.<name>` and let the factory or host wire it into a concrete configuration.
|
||||||
|
- Put user-specific authorized keys, identity, and shared defaults here rather than duplicating them across hosts.
|
||||||
|
|
||||||
|
Design shared modules as composable interfaces:
|
||||||
|
|
||||||
|
- Import other shared modules instead of copying option blocks.
|
||||||
|
- Add options or parameters when behavior needs to vary between hosts.
|
||||||
|
- Avoid embedding host-specific paths, hostnames, addresses, or secret file locations unless the file is intentionally host-local.
|
||||||
|
- Preserve exported attribute names even when the filename is different. In this repo, the path is not always the public API name.
|
||||||
|
|
||||||
|
Use nearby files as examples:
|
||||||
|
|
||||||
|
- `modules/nixos/games.nix` is a minimal shared NixOS module with no host wiring.
|
||||||
|
- `modules/features/nixos-base.nix` defines a reusable base system module that other hosts import.
|
||||||
|
- `modules/users/john.nix` combines `flake.meta.users.john`, a reusable NixOS user module, and a reusable Home Manager user module built around the user factory.
|
||||||
|
- `modules/nix-tools/user.nix` defines the `flake.factory.user` helper that shared user modules build on.
|
||||||
|
|
||||||
|
For reviews and answers, separate these concerns clearly:
|
||||||
|
|
||||||
|
- reusable module API and option design in shared module directories
|
||||||
|
- concrete host assembly in `modules/hosts`
|
||||||
|
- whether a change increases reuse or accidentally pulls machine-specific behavior into a shared layer
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
---
|
||||||
|
description: "Use when modifying the Soteria host definition or host-local secrets under modules/hosts/soteria. Covers host wiring and SSH cert mechanics specific to Soteria."
|
||||||
|
name: "Soteria Host Instructions"
|
||||||
|
applyTo: "modules/hosts/soteria/**/*.nix, modules/hosts/soteria/secrets.yaml"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Soteria Host Instructions
|
||||||
|
|
||||||
|
Use this instruction when changing Soteria host wiring in `modules/hosts/soteria`.
|
||||||
|
|
||||||
|
## Host Intent And Shape
|
||||||
|
|
||||||
|
- Treat Soteria as a NixOS service node for Forgejo, RESTic server, mTLS, and a paired Home Manager profile for `john`.
|
||||||
|
- Keep host identity aligned: `hostname = "soteria"`, `networking.hostName = hostname`, and `flake.nixosConfigurations."${hostname}"`.
|
||||||
|
- Keep host-local secrets in `modules/hosts/soteria/secrets.yaml` and wire with `sops.defaultSopsFile = ./secrets.yaml`.
|
||||||
|
- Keep host-specific Home Manager wiring in `flake.modules.homeManager.soteria`, and keep `mysops.hostSecretFile` pointing at the Soteria secrets file.
|
||||||
|
|
||||||
|
## Required Module Composition
|
||||||
|
|
||||||
|
When editing `modules/hosts/soteria/default.nix`, preserve this composition unless the task explicitly changes architecture:
|
||||||
|
|
||||||
|
- Import `nixos.step-ssh-host` to enable host SSH cert integration.
|
||||||
|
- Import `nixos.janus-ca` so Step client trust material and defaults remain available.
|
||||||
|
- Import `nixos.restic-server` so the RESTic REST server implementation lives in the shared module rather than inline host wiring.
|
||||||
|
- Keep `step-ssh-host.hostname = hostname;` so cert principals match host naming.
|
||||||
|
|
||||||
|
## SSH Cert Mechanics On Soteria
|
||||||
|
|
||||||
|
Soteria SSH certificate behavior is the result of multiple modules. Keep this flow intact:
|
||||||
|
|
||||||
|
1. `soteria/default.nix` imports `nixos.step-ssh-host` and sets `step-ssh-host.hostname = "soteria"` (via `hostname`).
|
||||||
|
2. `modules/services/step-ca/ssh-host.nix`:
|
||||||
|
- Enables `ssh.certificates.enable = true`.
|
||||||
|
- Requires `sops.secrets."janus/admin_jwk"` (provisioner credential) from Soteria's `secrets.yaml`.
|
||||||
|
- Defines cert paths at `/etc/ssh/ssh_host_ed25519_key` and `/etc/ssh/ssh_host_ed25519_key-cert.pub`.
|
||||||
|
- Exposes `ssh-host-cert-renew` and `ssh-host-cert-check` helper binaries.
|
||||||
|
- Schedules `step-ssh-host-renew.timer` every 4h with jitter.
|
||||||
|
3. `modules/services/ssh.nix` consumes `ssh.certificates.enable` and configures OpenSSH to:
|
||||||
|
- Set `TrustedUserCAKeys = /etc/ssh/ssh_user_ca.pub`.
|
||||||
|
- Set `HostCertificate = /etc/ssh/ssh_host_ed25519_key-cert.pub`.
|
||||||
|
- Install the trusted user CA file into `/etc/ssh/ssh_user_ca.pub`.
|
||||||
|
4. `nixos.janus-ca` provides Step CA trust bootstrapping (`/etc/step-ca/defaults.json` and linked root CA material), allowing Step CLI operations to trust and reach the CA endpoint.
|
||||||
|
|
||||||
|
Current implementation note:
|
||||||
|
|
||||||
|
- `step-ssh-host-renew.service` currently checks renewal state via `step ssh needs-renewal` and logs status. It does not directly invoke `ssh-host-cert-renew` in the service script. Preserve this behavior unless the task explicitly asks to change renewal execution semantics.
|
||||||
|
|
||||||
|
## SOPS/Secrets Mechanics On Soteria
|
||||||
|
|
||||||
|
Soteria secret handling spans host-local secrets, NixOS secret materialization, and Home Manager secret tooling. Keep this flow intact:
|
||||||
|
|
||||||
|
1. `modules/hosts/soteria/default.nix` imports `nixos.mysops` and sets `sops.defaultSopsFile = ./secrets.yaml`.
|
||||||
|
2. The SOPS file `modules/hosts/soteria/secrets.yaml` is the canonical encrypted source for this host's system secrets.
|
||||||
|
3. Shared modules imported by Soteria declare required secret entries under `sops.secrets` and consume them through `config.sops.secrets.<name>.path`:
|
||||||
|
- `modules/services/step-ca/ssh-host.nix` declares and consumes `janus/admin_jwk` as the Step provisioner password file.
|
||||||
|
- `modules/features/forgejo.nix` declares and consumes `forgejo/secret_key`, `forgejo/internal_token`, `forgejo/jwt_secret`, and `forgejo/lfs_jwt_secret`.
|
||||||
|
4. Secret ownership is module-defined and must stay aligned with service users:
|
||||||
|
- Forgejo secrets are owned by `config.services.forgejo.user`.
|
||||||
|
- Step SSH provisioner secret is locked to root ownership and `0400` mode.
|
||||||
|
5. `flake.modules.homeManager.soteria` imports `homeManager.mysops` and sets `mysops.hostSecretFile` to the same Soteria host secret file path. This drives helper tooling such as `edit-secrets` while Home Manager keeps its own default SOPS context from `modules/programs/sops.nix`.
|
||||||
|
|
||||||
|
Current implementation note:
|
||||||
|
|
||||||
|
- `restic_password` exists in `modules/hosts/soteria/secrets.yaml`, but it is not currently consumed by the Soteria NixOS module graph.
|
||||||
|
- Changing, deleting, or renaming seemingly unused keys in `secrets.yaml` should be treated as a compatibility change and confirmed by the task.
|
||||||
|
|
||||||
|
## Restic REST Server On Soteria
|
||||||
|
|
||||||
|
Soteria's RESTic REST server is implemented by the shared `nixos.restic-server` module and configured by host-local `resticServer` values. Trace it this way:
|
||||||
|
|
||||||
|
1. `modules/hosts/soteria/default.nix` imports `nixos.restic-server` and configures the host-local `resticServer` option set with:
|
||||||
|
- `enable = true`
|
||||||
|
- `dataDir = "/mnt/restic"`
|
||||||
|
- `privateRepos = true`
|
||||||
|
- `listenAddress = "0.0.0.0:8000"`
|
||||||
|
- `tls.certFile = config.mtls.certFile`
|
||||||
|
- `tls.keyFile = config.mtls.keyFile`
|
||||||
|
2. `modules/features/restic.nix` maps those `resticServer` options into `services.restic.server` and appends TLS flags when both TLS paths are set.
|
||||||
|
3. The shared module keeps the server base behavior in one place:
|
||||||
|
- `services.restic.server.enable = true`
|
||||||
|
- `services.restic.server.dataDir = cfg.dataDir`
|
||||||
|
- `services.restic.server.listenAddress = cfg.listenAddress`
|
||||||
|
- `services.restic.server.privateRepos = cfg.privateRepos`
|
||||||
|
- `services.restic.server.extraFlags = cfg.extraFlags ++ tlsFlags`
|
||||||
|
4. `modules/features/restic.nix` also opens the matching TCP port through `networking.firewall.allowedTCPPorts`, deriving it from `resticServer.listenAddress`.
|
||||||
|
5. The mTLS renewal hook includes `restic-rest-server.service` in `mtls.renew.reloadUnits`, so certificate rotation reloads the REST server alongside Forgejo.
|
||||||
|
6. `loginText.extraServiceStatus` exposes the running unit as `restic-rest-server.service`, which is the service name to keep in mind for status and reload behavior.
|
||||||
|
|
||||||
|
### Storage Location
|
||||||
|
|
||||||
|
- Soteria explicitly sets `resticServer.dataDir = "/mnt/restic"`.
|
||||||
|
- The shared module maps that value into `services.restic.server.dataDir`, so `/mnt/restic` is now the intended repository storage location for this host.
|
||||||
|
- The shared module also derives the firewall port from `resticServer.listenAddress`, so Soteria no longer needs a separate `networking.firewall.allowedTCPPorts = [ 8000 ]` line.
|
||||||
|
- If storage needs to move later, change the `resticServer.dataDir` input or the shared module contract, not ad hoc service cleanup logic.
|
||||||
|
- `privateRepos = true` constrains repo exposure behavior, but it does not define storage location by itself.
|
||||||
|
|
||||||
|
Current implementation note:
|
||||||
|
|
||||||
|
- The REST server is TLS-protected but currently passed `--no-auth`, so client access control relies on transport/security model rather than rest-server password auth.
|
||||||
|
- The shared module default listen address is not Soteria's deployed value; Soteria deliberately overrides it to `0.0.0.0:8000` in the host.
|
||||||
|
|
||||||
|
## Forgejo Implementation On Soteria
|
||||||
|
|
||||||
|
Soteria's Forgejo setup is split between host-local option values and the shared `nixos.forgejo` module. Trace it this way:
|
||||||
|
|
||||||
|
1. `modules/hosts/soteria/default.nix` imports `nixos.forgejo` and enables it with:
|
||||||
|
- `forgejo.enable = true`
|
||||||
|
- `forgejo.root_url = "https://forgejo.john-stream.com"`
|
||||||
|
- `forgejo.https = true`
|
||||||
|
- `forgejo.port = 443`
|
||||||
|
2. `modules/features/forgejo.nix` defines `forgejo.port` as the controlling option for the web listener. Its default is `3000`, but Soteria overrides it to `443`.
|
||||||
|
3. The shared module wires that option into multiple places:
|
||||||
|
- `services.forgejo.settings.server.HTTP_PORT = cfg.port`
|
||||||
|
- `networking.firewall.allowedTCPPorts = [ cfg.port ]` when `openFirewall = true`
|
||||||
|
- If `cfg.port < 1024`, the Forgejo systemd unit gets `CAP_NET_BIND_SERVICE` so it can bind a privileged port.
|
||||||
|
4. HTTPS on Soteria is terminated directly by Forgejo using mTLS-managed files:
|
||||||
|
- `services.forgejo.settings.server.PROTOCOL = "https"`
|
||||||
|
- `KEY_FILE = config.mtls.keyFile`
|
||||||
|
- `CERT_FILE = config.mtls.certFile`
|
||||||
|
|
||||||
|
### Storage Locations
|
||||||
|
|
||||||
|
- This repo does not override Forgejo's primary state directory in the Soteria host or in `modules/features/forgejo.nix`.
|
||||||
|
- That means Forgejo storage follows the underlying NixOS `services.forgejo` defaults, exposed in this config as `config.services.forgejo.stateDir`.
|
||||||
|
- PostgreSQL storage is also left at the underlying module default and is referenced as `config.services.postgresql.dataDir`.
|
||||||
|
- The cleanup helpers in `modules/features/forgejo.nix` confirm these are the intended storage anchors because they remove exactly `config.services.forgejo.stateDir` and `config.services.postgresql.dataDir`.
|
||||||
|
- Forgejo backup dumps are generated by `forgejo-dump.service` using `--work-path ${config.services.forgejo.dump.backupDir}`, so dump staging/output follows the Forgejo module's backup directory setting rather than a Soteria-specific path override.
|
||||||
|
|
||||||
|
Current implementation note:
|
||||||
|
|
||||||
|
- If you need to change Forgejo storage location on Soteria, do it by setting the underlying Forgejo or PostgreSQL service directory options explicitly, not by changing cleanup scripts alone.
|
||||||
|
- If you need to change the external Forgejo endpoint, update `forgejo.root_url`, `forgejo.port`, and the Soteria mTLS SAN list together.
|
||||||
|
|
||||||
|
## Change Safety Rules
|
||||||
|
|
||||||
|
- Do not rename the secret key `janus/admin_jwk` without updating all consumers.
|
||||||
|
- Do not change SSH host key/cert filenames unless OpenSSH `HostKey`/`HostCertificate` paths are updated together.
|
||||||
|
- If changing hostnames or domains, update cert principals in `step-ssh-host` and corresponding SSH client targets together.
|
||||||
|
- If adding or removing `nixos.step-ssh-host`, explain impact on SSH cert issuance, renewal, and trust in the change summary.
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
---
|
||||||
|
description: "Use when modifying modules/services/step-ca/ssh-host.nix. Covers Step SSH host certificate wiring, secret key contract, and host-side expectations required by consumers."
|
||||||
|
name: "Step SSH Host Module Instructions"
|
||||||
|
applyTo: "modules/services/step-ca/ssh-host.nix"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Step SSH Host Module Instructions
|
||||||
|
|
||||||
|
Use this instruction when changing the shared NixOS module in modules/services/step-ca/ssh-host.nix.
|
||||||
|
|
||||||
|
## Module Contract
|
||||||
|
|
||||||
|
- Keep this module reusable across hosts; do not hard-code host-local file paths beyond SSH system paths under /etc/ssh.
|
||||||
|
- Preserve the option interface unless the task explicitly changes it:
|
||||||
|
- step-ssh-host.hostname (required host identity used for cert principals)
|
||||||
|
- step-ssh-host.provisioner (default: "admin")
|
||||||
|
- Keep imports = [ inputs.self.modules.nixos.ssh ] so OpenSSH host certificate settings stay composed through the shared SSH module.
|
||||||
|
|
||||||
|
## Secret Key Contract
|
||||||
|
|
||||||
|
- This module currently consumes `config.sops.secrets."janus/admin_jwk"` as the Step provisioner password file.
|
||||||
|
- Treat `janus/admin_jwk` as a public contract key name for current consumers. If you rename it, update all consumers and host secret files in the same change.
|
||||||
|
- Keep secret permissions strict (root ownership and 0400 mode).
|
||||||
|
- Do not assume a local sops file path in this module. The consuming host must define sops.defaultSopsFile.
|
||||||
|
|
||||||
|
## Host Expectations
|
||||||
|
|
||||||
|
When this module is enabled by a host, the host is expected to provide:
|
||||||
|
|
||||||
|
- A working Step trust/bootstrap path (for example via janus-ca or equivalent trust material).
|
||||||
|
- `sops.defaultSopsFile` containing `janus.admin_jwk` (or equivalent if contract is intentionally changed everywhere).
|
||||||
|
- `step-ssh-host.hostname` matching deployed host identity and DNS naming expectations.
|
||||||
|
|
||||||
|
## SSH Certificate Paths And Principals
|
||||||
|
|
||||||
|
- Preserve the default host key path contract at `/etc/ssh/ssh_host_ed25519_key` unless task scope requires coordinated changes.
|
||||||
|
- Keep host certificate path aligned to `${sshKeyPath}-cert.pub`.
|
||||||
|
- Keep principal issuance based on hostname and hostname.john-stream.com unless domain policy is intentionally migrated.
|
||||||
|
- If changing key/cert filenames or principal naming, update `modules/services/ssh.nix` integration and host rollout notes in the same change.
|
||||||
|
|
||||||
|
## Renewal Behavior
|
||||||
|
|
||||||
|
- Maintain timer/service cadence unless explicitly requested:
|
||||||
|
- timer OnUnitActiveSec = 4h
|
||||||
|
- randomized delay enabled
|
||||||
|
- Keep explicit handling for step ssh needs-renewal return codes to avoid silent failures.
|
||||||
|
- If changing renewal semantics, document whether the service now only checks state or also performs certificate issuance.
|
||||||
|
|
||||||
|
## Validation Checklist
|
||||||
|
|
||||||
|
After editing this module, validate at least one consuming host with:
|
||||||
|
|
||||||
|
1. nix eval .#nixosConfigurations.janus.config.system.build.toplevel.drvPath
|
||||||
|
2. nix build .#nixosConfigurations.janus.config.system.build.toplevel --no-link --dry-run
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
---
|
||||||
|
description: "Use when modifying jsl-zsh, home-manager programs.zsh settings, or shell-tools wiring. Explains when to use wrapped jsl-zsh versus pkgs.zsh and how shell-tools composes into user and host configs."
|
||||||
|
applyTo: 'modules/programs/zsh.nix, modules/features/shell-tools.nix, modules/users/**/*.nix, modules/nix-tools/user.nix, modules/services/ssh.nix, modules/hosts/**/*.nix'
|
||||||
|
---
|
||||||
|
|
||||||
|
# jsl-zsh and shell-tools wiring
|
||||||
|
|
||||||
|
This repo intentionally uses two zsh variants for different layers:
|
||||||
|
|
||||||
|
- `pkgs.zsh` is the base system shell used for user login shell declarations.
|
||||||
|
- `inputs.self.packages.<system>.jsl-zsh` is the wrapped interactive shell used by Home Manager and remote shell entrypoints.
|
||||||
|
|
||||||
|
Treat these as distinct roles, not interchangeable defaults.
|
||||||
|
|
||||||
|
## Canonical definitions
|
||||||
|
|
||||||
|
The canonical wrapped shell is defined in `modules/programs/zsh.nix`:
|
||||||
|
|
||||||
|
- `perSystem.packages.jsl-zsh` wraps zsh via `wrapperModules.zsh.apply`.
|
||||||
|
- Wrapper settings, aliases, history behavior, devenv hook, and extra package PATH come from this package definition.
|
||||||
|
- `binName = "jsl-zsh"` is part of the external contract; keep it stable unless performing an explicit repo-wide rename.
|
||||||
|
|
||||||
|
The Home Manager zsh module in the same file is the canonical consumer:
|
||||||
|
|
||||||
|
- `flake.modules.homeManager.zsh` sets `programs.zsh.package` to `self.packages.<system>.jsl-zsh`.
|
||||||
|
- Keep Home Manager interactive zsh bound to the wrapped package, not plain `pkgs.zsh`.
|
||||||
|
|
||||||
|
## shell-tools composition
|
||||||
|
|
||||||
|
`modules/features/shell-tools.nix` is a composition module, not just a package list:
|
||||||
|
|
||||||
|
- `flake.modules.homeManager.shell-tools` imports `homeManager.zsh` and `homeManager.files`.
|
||||||
|
- It adds the wrapped `shell-tools` package to `home.packages`.
|
||||||
|
- `home.shell.enableShellIntegration = true` is expected to stay with this module.
|
||||||
|
|
||||||
|
If a user module imports `homeManager.shell-tools` (for example `modules/users/john.nix`), that user already receives the Home Manager zsh wiring transitively.
|
||||||
|
|
||||||
|
## System-layer expectations
|
||||||
|
|
||||||
|
`modules/nix-tools/user.nix` keeps system login semantics separate from Home Manager interactive behavior:
|
||||||
|
|
||||||
|
- `users.users.<name>.shell = pkgs.zsh` remains the login shell declaration.
|
||||||
|
- `programs.zsh.enable = true` enables system zsh support.
|
||||||
|
- `environment.shells` includes both `${lib.getExe pkgs.zsh}` and `${lib.getExe self.packages.<system>.jsl-zsh}` so wrapped shell paths are recognized when needed.
|
||||||
|
|
||||||
|
Do not replace login shell declarations with `jsl-zsh` unless the task is explicitly changing system login policy.
|
||||||
|
|
||||||
|
## Host and SSH usage contracts
|
||||||
|
|
||||||
|
Hosts may include `selfPkgs.jsl-zsh` in `environment.systemPackages` or `home.packages` so the wrapped shell is available directly.
|
||||||
|
|
||||||
|
`modules/services/ssh.nix` includes host entries that use `RemoteCommand = "~/.nix-profile/bin/jsl-zsh"` (and commented variants). This path-level usage means:
|
||||||
|
|
||||||
|
- keep the wrapper executable name stable (`jsl-zsh`),
|
||||||
|
- update SSH remote command references in the same change if shell invocation paths are changed.
|
||||||
|
|
||||||
|
## Change guidelines
|
||||||
|
|
||||||
|
When editing this area:
|
||||||
|
|
||||||
|
- Keep wrapper behavior changes in `packages.jsl-zsh` focused and centralized.
|
||||||
|
- Keep Home Manager zsh package selection pointed at wrapped `jsl-zsh`.
|
||||||
|
- Keep system login shell semantics (`pkgs.zsh`) and interactive wrapper semantics (`jsl-zsh`) separated unless intentionally redesigning both layers.
|
||||||
|
- If modifying `shell-tools`, verify it still composes zsh and tool packages for users importing the module.
|
||||||
|
- If changing executable names or invocation paths, update all dependent consumers (including SSH remote commands and host package lists) in one coherent change.
|
||||||
@@ -1,12 +1,67 @@
|
|||||||
{ self, inputs, ... }: {
|
{ self, inputs, ... }: {
|
||||||
flake.modules.nixos.restic-server = { config, pkgs, lib, ... }: {
|
flake.modules.nixos.restic-server = { config, pkgs, lib, ... }:
|
||||||
services.restic.server = {
|
let
|
||||||
enable = true;
|
cfg = config.resticServer;
|
||||||
dataDir = "/mnt/restic";
|
port = builtins.fromJSON (lib.last (lib.splitString ":" cfg.listenAddress));
|
||||||
listenAddress = "0.0.0.0:8080";
|
in {
|
||||||
extraFlags = [ "--no-auth" ];
|
options.resticServer = {
|
||||||
|
enable = lib.mkEnableOption "Enable the RESTic REST server";
|
||||||
|
|
||||||
|
dataDir = lib.mkOption {
|
||||||
|
description = "Storage directory for RESTic repositories served by this host.";
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "/mnt/restic";
|
||||||
|
};
|
||||||
|
|
||||||
|
listenAddress = lib.mkOption {
|
||||||
|
description = "Listen address for the RESTic REST server.";
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "0.0.0.0:8080";
|
||||||
|
};
|
||||||
|
|
||||||
|
privateRepos = lib.mkOption {
|
||||||
|
description = "Whether the RESTic server should use private repository mode.";
|
||||||
|
type = lib.types.bool;
|
||||||
|
default = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
extraFlags = lib.mkOption {
|
||||||
|
description = "Additional flags to pass to the RESTic REST server before TLS flags are appended.";
|
||||||
|
type = lib.types.listOf lib.types.str;
|
||||||
|
default = [ "--no-auth" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
tls = {
|
||||||
|
certFile = lib.mkOption {
|
||||||
|
description = "Path to the TLS certificate file for the RESTic REST server, or null to disable TLS.";
|
||||||
|
type = lib.types.nullOr lib.types.str;
|
||||||
|
default = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
keyFile = lib.mkOption {
|
||||||
|
description = "Path to the TLS private key file for the RESTic REST server, or null to disable TLS.";
|
||||||
|
type = lib.types.nullOr lib.types.str;
|
||||||
|
default = null;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkIf cfg.enable {
|
||||||
|
networking.firewall.allowedTCPPorts = [ port ];
|
||||||
|
|
||||||
|
services.restic.server = {
|
||||||
|
enable = true;
|
||||||
|
inherit (cfg) dataDir listenAddress privateRepos;
|
||||||
|
extraFlags =
|
||||||
|
cfg.extraFlags
|
||||||
|
++ lib.optionals (cfg.tls.certFile != null && cfg.tls.keyFile != null) [
|
||||||
|
"--tls"
|
||||||
|
"--tls-cert=${cfg.tls.certFile}"
|
||||||
|
"--tls-key=${cfg.tls.keyFile}"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
|
||||||
|
|
||||||
flake.modules.homeManager.restic = { config, pkgs, lib, ... }:
|
flake.modules.homeManager.restic = { config, pkgs, lib, ... }:
|
||||||
let
|
let
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{ withSystem, self, inputs, ... }:
|
{ withSystem, self, inputs, ... }:
|
||||||
let
|
let
|
||||||
username = "john";
|
username = "john";
|
||||||
hostname = "omen";
|
hostname = "john-kde";
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
flake.modules.homeManager."${hostname}" = { config, pkgs, lib, ... }:
|
flake.modules.homeManager."${hostname}" = { config, pkgs, lib, ... }:
|
||||||
@@ -62,7 +62,7 @@ in
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
flake.homeConfigurations."john@omen" = withSystem "x86_64-linux" (ctx@{ system, inputs', ... }:
|
flake.homeConfigurations."john@john-kde" = withSystem "x86_64-linux" (ctx@{ system, inputs', ... }:
|
||||||
inputs.home-manager.lib.homeManagerConfiguration {
|
inputs.home-manager.lib.homeManagerConfiguration {
|
||||||
pkgs = inputs'.nixpkgs.legacyPackages;
|
pkgs = inputs'.nixpkgs.legacyPackages;
|
||||||
modules = [ inputs.self.modules.homeManager."${hostname}" ]; # Uses the module defined above
|
modules = [ inputs.self.modules.homeManager."${hostname}" ]; # Uses the module defined above
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ in
|
|||||||
desktop
|
desktop
|
||||||
step-client
|
step-client
|
||||||
mysops
|
mysops
|
||||||
|
# zed-editor
|
||||||
# myPackage
|
# myPackage
|
||||||
# myStepClient
|
# myStepClient
|
||||||
];
|
];
|
||||||
@@ -38,6 +39,7 @@ in
|
|||||||
home.username = "${username}";
|
home.username = "${username}";
|
||||||
home.homeDirectory = "/home/${username}";
|
home.homeDirectory = "/home/${username}";
|
||||||
home.packages = with pkgs; [
|
home.packages = with pkgs; [
|
||||||
|
nil # Nix language server
|
||||||
selfPkgs.jsl-zsh
|
selfPkgs.jsl-zsh
|
||||||
# selfPkgs.my-neovim
|
# selfPkgs.my-neovim
|
||||||
selfPkgs.ssh-certs
|
selfPkgs.ssh-certs
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
{ self, inputs, ... }: {
|
{ self, inputs, ... }: {
|
||||||
|
|
||||||
flake.modules.nixos.omen = { pkgs, lib, ... }: {
|
flake.modules.nixos.omen-nixos = { pkgs, lib, ... }: {
|
||||||
# import any other modules from here
|
# import any other modules from here
|
||||||
imports = [
|
imports = [
|
||||||
self.modules.nixos.omenHardware
|
self.modules.nixos.omen-nixos-hardware
|
||||||
self.modules.nixos.base
|
self.modules.nixos.base
|
||||||
self.modules.nixos.greetd
|
self.modules.nixos.greetd
|
||||||
self.modules.nixos.niri
|
self.modules.nixos.niri
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
# Use latest kernel.
|
# Use latest kernel.
|
||||||
boot.kernelPackages = pkgs.linuxPackages_latest;
|
boot.kernelPackages = pkgs.linuxPackages_latest;
|
||||||
|
|
||||||
networking.hostName = "nixos-omen"; # Define your hostname.
|
networking.hostName = "omen-nixos"; # Define your hostname.
|
||||||
|
|
||||||
# Configure network proxy if necessary
|
# Configure network proxy if necessary
|
||||||
# networking.proxy.default = "http://user:password@proxy:port/";
|
# networking.proxy.default = "http://user:password@proxy:port/";
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{ self, inputs, ... }: {
|
{ self, inputs, ... }: {
|
||||||
flake.nixosConfigurations.omen = inputs.nixpkgs.lib.nixosSystem {
|
flake.nixosConfigurations.omen-nixos = inputs.nixpkgs.lib.nixosSystem {
|
||||||
modules = [
|
modules = [
|
||||||
self.modules.nixos.omen
|
self.modules.nixos.omen-nixos
|
||||||
self.modules.nixos.john
|
self.modules.nixos.john
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{ self, inputs, ... }: {
|
{ self, inputs, ... }: {
|
||||||
flake.modules.nixos.omenHardware = { config, lib, pkgs, modulesPath, ... }: {
|
flake.modules.nixos.omen-nixos-hardware = { config, lib, pkgs, modulesPath, ... }: {
|
||||||
imports = [
|
imports = [
|
||||||
(modulesPath + "/installer/scan/not-detected.nix")
|
(modulesPath + "/installer/scan/not-detected.nix")
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
{ withSystem, self, inputs, lib, ... }:
|
{ withSystem, self, inputs, lib, ... }:
|
||||||
|
# Intent: NixOS service node for Forgejo + restic with mTLS, host-local secrets, and a paired Home Manager profile for john.
|
||||||
let
|
let
|
||||||
username = "john";
|
username = "john";
|
||||||
hostname = "soteria";
|
hostname = "soteria";
|
||||||
@@ -15,7 +16,7 @@ in
|
|||||||
nixos.mtls
|
nixos.mtls
|
||||||
nixos.janus-ca
|
nixos.janus-ca
|
||||||
nixos.forgejo
|
nixos.forgejo
|
||||||
# nixos.restic-server
|
nixos.restic-server
|
||||||
# nixos.restic-envoy
|
# nixos.restic-envoy
|
||||||
({ config, pkgs, ... }: {
|
({ config, pkgs, ... }: {
|
||||||
networking.hostName = hostname;
|
networking.hostName = hostname;
|
||||||
@@ -57,17 +58,15 @@ in
|
|||||||
port = 443;
|
port = 443;
|
||||||
};
|
};
|
||||||
|
|
||||||
networking.firewall.allowedTCPPorts = [ 8000 ];
|
resticServer = {
|
||||||
services.restic.server = {
|
|
||||||
enable = true;
|
enable = true;
|
||||||
|
dataDir = "/mnt/restic";
|
||||||
privateRepos = true;
|
privateRepos = true;
|
||||||
listenAddress = "0.0.0.0:8000";
|
listenAddress = "0.0.0.0:8000";
|
||||||
extraFlags = [
|
tls = {
|
||||||
"--no-auth"
|
certFile = config.mtls.certFile;
|
||||||
"--tls"
|
keyFile = config.mtls.keyFile;
|
||||||
"--tls-cert=${config.mtls.certFile}"
|
};
|
||||||
"--tls-key=${config.mtls.keyFile}"
|
|
||||||
];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
loginText.extraServiceStatus = {
|
loginText.extraServiceStatus = {
|
||||||
@@ -83,7 +82,7 @@ in
|
|||||||
# This provides the secrets at install time
|
# This provides the secrets at install time
|
||||||
sops.defaultSopsFile = ./secrets.yaml;
|
sops.defaultSopsFile = ./secrets.yaml;
|
||||||
|
|
||||||
programs.zsh.enable = true;
|
# programs.zsh.enable = true;
|
||||||
|
|
||||||
home-manager.users."${username}".imports = [ inputs.self.modules.homeManager.soteria ];
|
home-manager.users."${username}".imports = [ inputs.self.modules.homeManager.soteria ];
|
||||||
|
|
||||||
Reference in New Issue
Block a user