Compare commits
9
Commits
f22e8cb1d5
...
a9ae07d5c1
| 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, ... }: {
|
||||
flake.modules.nixos.restic-server = { config, pkgs, lib, ... }: {
|
||||
services.restic.server = {
|
||||
enable = true;
|
||||
dataDir = "/mnt/restic";
|
||||
listenAddress = "0.0.0.0:8080";
|
||||
extraFlags = [ "--no-auth" ];
|
||||
flake.modules.nixos.restic-server = { config, pkgs, lib, ... }:
|
||||
let
|
||||
cfg = config.resticServer;
|
||||
port = builtins.fromJSON (lib.last (lib.splitString ":" cfg.listenAddress));
|
||||
in {
|
||||
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, ... }:
|
||||
let
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{ withSystem, self, inputs, ... }:
|
||||
let
|
||||
username = "john";
|
||||
hostname = "omen";
|
||||
hostname = "john-kde";
|
||||
in
|
||||
{
|
||||
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 {
|
||||
pkgs = inputs'.nixpkgs.legacyPackages;
|
||||
modules = [ inputs.self.modules.homeManager."${hostname}" ]; # Uses the module defined above
|
||||
|
||||
@@ -26,6 +26,7 @@ in
|
||||
desktop
|
||||
step-client
|
||||
mysops
|
||||
# zed-editor
|
||||
# myPackage
|
||||
# myStepClient
|
||||
];
|
||||
@@ -38,6 +39,7 @@ in
|
||||
home.username = "${username}";
|
||||
home.homeDirectory = "/home/${username}";
|
||||
home.packages = with pkgs; [
|
||||
nil # Nix language server
|
||||
selfPkgs.jsl-zsh
|
||||
# selfPkgs.my-neovim
|
||||
selfPkgs.ssh-certs
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{ self, inputs, ... }: {
|
||||
|
||||
flake.modules.nixos.omen = { pkgs, lib, ... }: {
|
||||
flake.modules.nixos.omen-nixos = { pkgs, lib, ... }: {
|
||||
# import any other modules from here
|
||||
imports = [
|
||||
self.modules.nixos.omenHardware
|
||||
self.modules.nixos.omen-nixos-hardware
|
||||
self.modules.nixos.base
|
||||
self.modules.nixos.greetd
|
||||
self.modules.nixos.niri
|
||||
@@ -18,7 +18,7 @@
|
||||
# Use latest kernel.
|
||||
boot.kernelPackages = pkgs.linuxPackages_latest;
|
||||
|
||||
networking.hostName = "nixos-omen"; # Define your hostname.
|
||||
networking.hostName = "omen-nixos"; # Define your hostname.
|
||||
|
||||
# Configure network proxy if necessary
|
||||
# networking.proxy.default = "http://user:password@proxy:port/";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{ self, inputs, ... }: {
|
||||
flake.nixosConfigurations.omen = inputs.nixpkgs.lib.nixosSystem {
|
||||
flake.nixosConfigurations.omen-nixos = inputs.nixpkgs.lib.nixosSystem {
|
||||
modules = [
|
||||
self.modules.nixos.omen
|
||||
self.modules.nixos.omen-nixos
|
||||
self.modules.nixos.john
|
||||
];
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{ self, inputs, ... }: {
|
||||
flake.modules.nixos.omenHardware = { config, lib, pkgs, modulesPath, ... }: {
|
||||
flake.modules.nixos.omen-nixos-hardware = { config, lib, pkgs, modulesPath, ... }: {
|
||||
imports = [
|
||||
(modulesPath + "/installer/scan/not-detected.nix")
|
||||
];
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{ 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
|
||||
username = "john";
|
||||
hostname = "soteria";
|
||||
@@ -15,7 +16,7 @@ in
|
||||
nixos.mtls
|
||||
nixos.janus-ca
|
||||
nixos.forgejo
|
||||
# nixos.restic-server
|
||||
nixos.restic-server
|
||||
# nixos.restic-envoy
|
||||
({ config, pkgs, ... }: {
|
||||
networking.hostName = hostname;
|
||||
@@ -57,17 +58,15 @@ in
|
||||
port = 443;
|
||||
};
|
||||
|
||||
networking.firewall.allowedTCPPorts = [ 8000 ];
|
||||
services.restic.server = {
|
||||
resticServer = {
|
||||
enable = true;
|
||||
dataDir = "/mnt/restic";
|
||||
privateRepos = true;
|
||||
listenAddress = "0.0.0.0:8000";
|
||||
extraFlags = [
|
||||
"--no-auth"
|
||||
"--tls"
|
||||
"--tls-cert=${config.mtls.certFile}"
|
||||
"--tls-key=${config.mtls.keyFile}"
|
||||
];
|
||||
tls = {
|
||||
certFile = config.mtls.certFile;
|
||||
keyFile = config.mtls.keyFile;
|
||||
};
|
||||
};
|
||||
|
||||
loginText.extraServiceStatus = {
|
||||
@@ -83,7 +82,7 @@ in
|
||||
# This provides the secrets at install time
|
||||
sops.defaultSopsFile = ./secrets.yaml;
|
||||
|
||||
programs.zsh.enable = true;
|
||||
# programs.zsh.enable = true;
|
||||
|
||||
home-manager.users."${username}".imports = [ inputs.self.modules.homeManager.soteria ];
|
||||
|
||||
Reference in New Issue
Block a user