9.7 KiB
description, name, applyTo
| description | name | applyTo |
|---|---|---|
| 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. | Soteria Host Instructions | 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, andflake.nixosConfigurations."${hostname}". - Keep host-local secrets in
modules/hosts/soteria/secrets.yamland wire withsops.defaultSopsFile = ./secrets.yaml. - Keep host-specific Home Manager wiring in
flake.modules.homeManager.soteria, and keepmysops.hostSecretFilepointing 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-hostto enable host SSH cert integration. - Import
nixos.janus-caso Step client trust material and defaults remain available. - Import
nixos.restic-serverso 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:
soteria/default.niximportsnixos.step-ssh-hostand setsstep-ssh-host.hostname = "soteria"(viahostname).modules/services/step-ca/ssh-host.nix:- Enables
ssh.certificates.enable = true. - Requires
sops.secrets."janus/admin_jwk"(provisioner credential) from Soteria'ssecrets.yaml. - Defines cert paths at
/etc/ssh/ssh_host_ed25519_keyand/etc/ssh/ssh_host_ed25519_key-cert.pub. - Exposes
ssh-host-cert-renewandssh-host-cert-checkhelper binaries. - Schedules
step-ssh-host-renew.timerevery 4h with jitter.
- Enables
modules/services/ssh.nixconsumesssh.certificates.enableand 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.
- Set
nixos.janus-caprovides Step CA trust bootstrapping (/etc/step-ca/defaults.jsonand linked root CA material), allowing Step CLI operations to trust and reach the CA endpoint.
Current implementation note:
step-ssh-host-renew.servicecurrently checks renewal state viastep ssh needs-renewaland logs status. It does not directly invokessh-host-cert-renewin 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:
modules/hosts/soteria/default.niximportsnixos.mysopsand setssops.defaultSopsFile = ./secrets.yaml.- The SOPS file
modules/hosts/soteria/secrets.yamlis the canonical encrypted source for this host's system secrets. - Shared modules imported by Soteria declare required secret entries under
sops.secretsand consume them throughconfig.sops.secrets.<name>.path:modules/services/step-ca/ssh-host.nixdeclares and consumesjanus/admin_jwkas the Step provisioner password file.modules/features/forgejo.nixdeclares and consumesforgejo/secret_key,forgejo/internal_token,forgejo/jwt_secret, andforgejo/lfs_jwt_secret.
- 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
0400mode.
- Forgejo secrets are owned by
flake.modules.homeManager.soteriaimportshomeManager.mysopsand setsmysops.hostSecretFileto the same Soteria host secret file path. This drives helper tooling such asedit-secretswhile Home Manager keeps its own default SOPS context frommodules/programs/sops.nix.
Current implementation note:
restic_passwordexists inmodules/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.yamlshould 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:
modules/hosts/soteria/default.niximportsnixos.restic-serverand configures the host-localresticServeroption set with:enable = truedataDir = "/mnt/restic"privateRepos = truelistenAddress = "0.0.0.0:8000"tls.certFile = config.mtls.certFiletls.keyFile = config.mtls.keyFile
modules/features/restic.nixmaps thoseresticServeroptions intoservices.restic.serverand appends TLS flags when both TLS paths are set.- The shared module keeps the server base behavior in one place:
services.restic.server.enable = trueservices.restic.server.dataDir = cfg.dataDirservices.restic.server.listenAddress = cfg.listenAddressservices.restic.server.privateRepos = cfg.privateReposservices.restic.server.extraFlags = cfg.extraFlags ++ tlsFlags
modules/features/restic.nixalso opens the matching TCP port throughnetworking.firewall.allowedTCPPorts, deriving it fromresticServer.listenAddress.- The mTLS renewal hook includes
restic-rest-server.serviceinmtls.renew.reloadUnits, so certificate rotation reloads the REST server alongside Forgejo. loginText.extraServiceStatusexposes the running unit asrestic-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/resticis 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 separatenetworking.firewall.allowedTCPPorts = [ 8000 ]line. - If storage needs to move later, change the
resticServer.dataDirinput or the shared module contract, not ad hoc service cleanup logic. privateRepos = trueconstrains 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:8000in 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:
modules/hosts/soteria/default.niximportsnixos.forgejoand enables it with:forgejo.enable = trueforgejo.root_url = "https://forgejo.john-stream.com"forgejo.https = trueforgejo.port = 443
modules/features/forgejo.nixdefinesforgejo.portas the controlling option for the web listener. Its default is3000, but Soteria overrides it to443.- The shared module wires that option into multiple places:
services.forgejo.settings.server.HTTP_PORT = cfg.portnetworking.firewall.allowedTCPPorts = [ cfg.port ]whenopenFirewall = true- If
cfg.port < 1024, the Forgejo systemd unit getsCAP_NET_BIND_SERVICEso it can bind a privileged port.
- HTTPS on Soteria is terminated directly by Forgejo using mTLS-managed files:
services.forgejo.settings.server.PROTOCOL = "https"KEY_FILE = config.mtls.keyFileCERT_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.forgejodefaults, exposed in this config asconfig.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.nixconfirm these are the intended storage anchors because they remove exactlyconfig.services.forgejo.stateDirandconfig.services.postgresql.dataDir. - Forgejo backup dumps are generated by
forgejo-dump.serviceusing--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_jwkwithout updating all consumers. - Do not change SSH host key/cert filenames unless OpenSSH
HostKey/HostCertificatepaths are updated together. - If changing hostnames or domains, update cert principals in
step-ssh-hostand 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.