35 Commits
Author SHA1 Message Date
John Lancaster 73950c72ed home-manager to inputs in john 2026-03-26 18:04:18 -05:00
John Lancaster bdf187b4f9 amd hardware updates 2026-03-26 18:03:56 -05:00
John Lancaster 336166d22d fix 2026-03-26 17:56:54 -05:00
John Lancaster ac525dda78 typo 2026-03-26 17:56:15 -05:00
John Lancaster 4cd6972302 added sudo and john nixos modules to p14s 2026-03-26 17:55:09 -05:00
John Lancaster 76be519c0f added graphics options to p14s hardware 2026-03-26 17:52:41 -05:00
John Lancaster c578bf00a1 formatting 2026-03-26 17:50:19 -05:00
John Lancaster 429d1b7f11 config 2026-03-26 17:39:03 -05:00
John Lancaster 942cc13cf0 displayManager settings 2026-03-26 17:38:12 -05:00
John Lancaster f1e395fd30 enabled touchpad support 2026-03-26 17:37:59 -05:00
John Lancaster 0ebd91234e enabled gnome desktopmanager 2026-03-26 17:31:42 -05:00
John Lancaster 4a235fe1e1 formatting 2026-03-26 17:31:22 -05:00
John Lancaster 4465fd0f90 insecure openssl-1.1.1w 2026-03-26 17:25:03 -05:00
John Lancaster 9c733a135a onepassword 2026-03-26 17:22:18 -05:00
John Lancaster 193e5ee40d pkgs 2026-03-26 17:12:51 -05:00
John Lancaster 71a04c627a desktop fixes 2026-03-26 17:11:33 -05:00
John Lancaster 338ef34212 typo 2026-03-26 16:58:22 -05:00
John Lancaster 1f5319252e lib 2026-03-26 16:57:51 -05:00
John Lancaster 29853f4a35 passing thru pkgs 2026-03-26 16:56:52 -05:00
John Lancaster 9f2a55cfd6 fixes 2026-03-26 16:56:17 -05:00
John Lancaster 1d8e721171 gnome on p14s 2026-03-26 16:53:32 -05:00
John Lancaster 731277aad8 gnome consolidation 2026-03-26 16:49:14 -05:00
John Lancaster bab4dfa131 added ripgrep to shell-tools 2026-03-25 22:32:01 -05:00
John Lancaster 2c8e7e3b63 added niri/noctalia 2026-03-25 22:09:25 -05:00
John Lancaster 2ca7dcc464 reformat 2026-03-25 21:56:59 -05:00
John Lancaster 8b4a2a36b6 pruning 2026-03-25 18:59:17 -05:00
John Lancaster fae745af5a prune 2026-03-25 18:46:02 -05:00
John Lancaster 0d39ac7ca2 prune 2026-03-25 18:44:45 -05:00
John Lancaster 5bbf20aabd prune 2026-03-25 18:44:37 -05:00
John Lancaster 3da3cc2dc6 cleanup 2026-03-25 18:33:54 -05:00
John Lancaster 18b6cc16ad more pruning 2026-03-25 18:17:07 -05:00
John Lancaster d8ced840ca pruning 2026-03-25 18:07:19 -05:00
John Lancaster 2fad88f8a0 added root_ca content 2026-03-25 18:07:08 -05:00
John Lancaster 7b0f419454 WIP CA bootstrap rework 2026-03-25 17:55:31 -05:00
John Lancaster f8c09878a1 started p14s transition 2026-03-25 17:29:37 -05:00
71 changed files with 1104 additions and 3763 deletions
@@ -1,68 +0,0 @@
# 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.
@@ -1,78 +0,0 @@
---
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
@@ -1,54 +0,0 @@
---
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
@@ -1,139 +0,0 @@
---
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.
@@ -1,54 +0,0 @@
---
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
@@ -1,65 +0,0 @@
---
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.
-2
View File
@@ -1,2 +0,0 @@
result/
.devenv/
-6
View File
@@ -9,12 +9,6 @@ creation_rules:
- age:
- *john-pc
- *soteria
- *test-nix
- path_regex: john-p14s/secrets\.yaml$
key_groups:
- age:
- *john-p14s
- *john-pc
- path_regex: john-pc/secrets\.yaml$
key_groups:
- age:
-16
View File
@@ -12,22 +12,6 @@ home-manager switch --flake .#desktop
nix flake show --all-systems
```
```shell
nix run nixpkgs#nh home switch -- --configuration john@john-pc-ubuntu .
```
## Host Intent
| Host key | Type | Intent | Entrypoint |
| -------- | ---- | ------ | ---------- |
| `janus` | NixOS (LXC) | Base homelab/service host with local step-ca trust bootstrap (`janus-ca`) and mTLS support. | `modules/hosts/janus/default.nix` |
| `john-kde` | Home Manager (generic Linux) | KDE-focused user environment profile for `john` with desktop + docker + step client tooling. | `modules/hosts/john-kde/default.nix` |
| `john-p14s` | NixOS (laptop) | Main ThinkPad P14s NixOS workstation using hardware presets and desktop/dev modules. | `modules/hosts/john-p14s/default.nix` |
| `john-pc-ubuntu` | Home Manager (generic Linux) | Ubuntu-hosted home profile for `john`, includes desktop/dev tooling and restic backup client settings. | `modules/hosts/john-pc/default.nix` |
| `omen-nixos` | NixOS | Omen machine wired through shared `omen-nixos` + `john` modules. | `modules/hosts/omen-nixos/default.nix` |
| `soteria` | NixOS + Home Manager | Service node running Forgejo + restic server with mTLS and host-local secrets. | `modules/hosts/soteria/soteria.nix` |
| `test-nix` | NixOS (LXC) | Minimal sandbox/test target for validating module composition and remote push/switch flows. | `modules/hosts/test-nix.nix` |
## Layout
- Everything under `./modules` gets auto-imported by `import-tree`
Generated
+44 -140
View File
@@ -1,28 +1,12 @@
{
"nodes": {
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1777699697,
"narHash": "sha256-Eg9b/rq/ECYwNwEXs5i9wHyhxNI0JrYx2srdI2uZMaQ=",
"ref": "refs/heads/main",
"rev": "382052b74656a369c5408822af3f2501e9b1af81",
"revCount": 94,
"type": "git",
"url": "https://git.lix.systems/lix-project/flake-compat.git"
},
"original": {
"type": "git",
"url": "https://git.lix.systems/lix-project/flake-compat.git"
}
},
"flake-file": {
"locked": {
"lastModified": 1781217157,
"narHash": "sha256-N3q/SP2Ropk336e9KSgLh7kpROY6P70dprYdbPIfd5c=",
"lastModified": 1773554778,
"narHash": "sha256-keH0VNsci9e0Uwt3Msp/N+pltaP8Lb6lt09Q3WvDPw4=",
"owner": "vic",
"repo": "flake-file",
"rev": "ce63eaf7ebfe04a176653f66385a7f0a36380cee",
"rev": "f4780a86bd4c756475d839b286f8a40aabdbc802",
"type": "github"
},
"original": {
@@ -34,36 +18,15 @@
"flake-parts": {
"inputs": {
"nixpkgs-lib": [
"nixpkgs"
"nixpkgs-lib"
]
},
"locked": {
"lastModified": 1778716662,
"narHash": "sha256-m1Yf0wZ8j1OHjTc2UwHwyQRSnNeSgLJOd7q5Y45hzi4=",
"lastModified": 1772408722,
"narHash": "sha256-rHuJtdcOjK7rAHpHphUb1iCvgkU3GpfvicLMwwnfMT0=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "f7c1a2d347e4c52d5fb8d10cb4d94b5884e546fb",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"flake-parts_2": {
"inputs": {
"nixpkgs-lib": [
"nvf",
"nixpkgs"
]
},
"locked": {
"lastModified": 1778716662,
"narHash": "sha256-m1Yf0wZ8j1OHjTc2UwHwyQRSnNeSgLJOd7q5Y45hzi4=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "f7c1a2d347e4c52d5fb8d10cb4d94b5884e546fb",
"rev": "f20dc5d9b8027381c474144ecabc9034d6a839a3",
"type": "github"
},
"original": {
@@ -95,11 +58,11 @@
"nixpkgs": "nixpkgs"
},
"locked": {
"lastModified": 1782881387,
"narHash": "sha256-HvlS1KDGXDjd1bNzzPvH1mkDJATlDAfVYfTh//wJBEA=",
"lastModified": 1773608492,
"narHash": "sha256-QZteyExJYSQzgxqdsesDPbQgjctGG7iKV/6ooyQPITk=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "d32537981c036473cf6990ae03176a5d84c43b29",
"rev": "9a40ec3b78fc688d0908485887d355caa5666d18",
"type": "github"
},
"original": {
@@ -110,11 +73,11 @@
},
"import-tree": {
"locked": {
"lastModified": 1778781969,
"narHash": "sha256-Jjuz5CmSkur8KvLDoGa+vylEp+RkQtv4mt/qcMznpH0=",
"lastModified": 1773554199,
"narHash": "sha256-6apV5N1F5tTD8JY9AUGnkWmy56HqDPn4MNFRsq4Rg+s=",
"owner": "vic",
"repo": "import-tree",
"rev": "d321337efd0f23a9eb14a42adb7b2c29313ab274",
"rev": "c6ebc59c85ee54cfb68163d06d1a3149ce0fe431",
"type": "github"
},
"original": {
@@ -123,21 +86,6 @@
"type": "github"
}
},
"mnw": {
"locked": {
"lastModified": 1780772958,
"narHash": "sha256-VKKe8r4pwCGWZ3Yr9CPN129R4S3CKLSrlYqdYz3vKpM=",
"owner": "Gerg-L",
"repo": "mnw",
"rev": "0871dbf63a53610c95db04439ed8ea4d6ec9c160",
"type": "github"
},
"original": {
"owner": "Gerg-L",
"repo": "mnw",
"type": "github"
}
},
"nixgl": {
"inputs": {
"flake-utils": "flake-utils",
@@ -162,11 +110,11 @@
"nixos-hardware": {
"flake": false,
"locked": {
"lastModified": 1782562157,
"narHash": "sha256-a7+T6QSeowynwZ1ZJJbP8T8ntAytvrui8kFGJmIZt2c=",
"lastModified": 1774465523,
"narHash": "sha256-4v7HPm63Q90nNn4fgkgKsjW1AH2Klw7XzPtHJr562nM=",
"owner": "NixOS",
"repo": "nixos-hardware",
"rev": "a9cf7546a938c737b079e738de73934a13de9784",
"rev": "de895be946ad1d8aafa0bb6dfc7e7e0e9e466a29",
"type": "github"
},
"original": {
@@ -177,54 +125,46 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1782175435,
"narHash": "sha256-EMzXKmnOtBQ2MnvpiNOm7E+kOMvdPrIKaeg52Tip2Uk=",
"lastModified": 1773389992,
"narHash": "sha256-wvfdLLWJ2I9oEpDd9PfMA8osfIZicoQ5MT1jIwNs9Tk=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "89570f24e97e614aa34aa9ab1c927b6578a43775",
"rev": "c06b4ae3d6599a672a6210b7021d699c351eebda",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1782821651,
"narHash": "sha256-OBddwHNJ+qxOHaKih0bE2PXJeZHlzUC8WOHZrUcnFwY=",
"rev": "e52c192be9d7b2c4bd4aed326c8731b35f8bb75c",
"lastModified": 1773507054,
"narHash": "sha256-yzDBkI1CpeZrAt4l1nGvTOs3OFtXCS7a7Gi5Y1h878w=",
"rev": "e80236013dc8b77aa49ca90e7a12d86f5d8d64c9",
"type": "tarball",
"url": "https://releases.nixos.org/nixpkgs/nixpkgs-26.11pre1025128.e52c192be9d7/nixexprs.tar.xz"
"url": "https://releases.nixos.org/nixpkgs/nixpkgs-26.05pre963414.e80236013dc8/nixexprs.tar.xz"
},
"original": {
"type": "tarball",
"url": "https://channels.nixos.org/nixpkgs-unstable/nixexprs.tar.xz"
}
},
"nvf": {
"inputs": {
"flake-compat": "flake-compat",
"flake-parts": "flake-parts_2",
"mnw": "mnw",
"nixpkgs": [
"nixpkgs"
],
"systems": "systems_2"
},
"nixpkgs_3": {
"locked": {
"lastModified": 1782841360,
"narHash": "sha256-S1f/S5Vv7wURVUtzPkmAFlKPVciaPLXxoeg7/GCnl28=",
"owner": "notashelf",
"repo": "nvf",
"rev": "6fad9e9a998dd3a7783c839ce2928877d3e5a883",
"lastModified": 1773840656,
"narHash": "sha256-9tpvMGFteZnd3gRQZFlRCohVpqooygFuy9yjuyRL2C0=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "9cf7092bdd603554bd8b63c216e8943cf9b12512",
"type": "github"
},
"original": {
"owner": "notashelf",
"repo": "nvf",
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
@@ -237,10 +177,11 @@
"nixgl": "nixgl",
"nixos-hardware": "nixos-hardware",
"nixpkgs": "nixpkgs_2",
"nvf": "nvf",
"nixpkgs-lib": [
"nixpkgs"
],
"sops-nix": "sops-nix",
"wrapper-modules": "wrapper-modules",
"wrappers": "wrappers"
"wrapper-modules": "wrapper-modules"
}
},
"sops-nix": {
@@ -250,11 +191,11 @@
]
},
"locked": {
"lastModified": 1782165805,
"narHash": "sha256-478kKQBvK6SYTOdN2h9jhKJv94nbXRbFMfuL1WshErg=",
"lastModified": 1773550941,
"narHash": "sha256-wa/++bL2QeMUreNFBZEWluQfOYB0MnQIeGNMuaX9sfs=",
"owner": "Mic92",
"repo": "sops-nix",
"rev": "56b24064fdcaedca53553b1a6d607fd23b613a24",
"rev": "c469b6885f0dcd5c7c56bd935a0f08dbcd9e79e1",
"type": "github"
},
"original": {
@@ -278,33 +219,16 @@
"type": "github"
}
},
"systems_2": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"wrapper-modules": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
"nixpkgs": "nixpkgs_3"
},
"locked": {
"lastModified": 1782135443,
"narHash": "sha256-vAmbArdCyjqpVW+37aCy/PMBOLIqukUXLQuEKLwUhA4=",
"lastModified": 1774400689,
"narHash": "sha256-hHABVYL0rxhVIzc7hZEpvE0eiCKPnevte73Tn6FRolo=",
"owner": "BirdeeHub",
"repo": "nix-wrapper-modules",
"rev": "6e7f66fa2cdf4d63162580b438f7fcf87c28a46f",
"rev": "54645978682ff99594a1d1666dde6fac6a8134f2",
"type": "github"
},
"original": {
@@ -312,26 +236,6 @@
"repo": "nix-wrapper-modules",
"type": "github"
}
},
"wrappers": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1782375095,
"narHash": "sha256-bRepNpAluz2i0IIk7Tmzw/4BkyOrwABvc66NEgXPEhA=",
"owner": "lassulus",
"repo": "wrappers",
"rev": "cd780e92c0fa66c2e721e1d91894f3db13b9f2b4",
"type": "github"
},
"original": {
"owner": "lassulus",
"repo": "wrappers",
"type": "github"
}
}
},
"root": "root",
+3 -13
View File
@@ -7,7 +7,7 @@
flake-file.url = "github:vic/flake-file";
flake-parts = {
url = "github:hercules-ci/flake-parts";
inputs.nixpkgs-lib.follows = "nixpkgs";
inputs.nixpkgs-lib.follows = "nixpkgs-lib";
};
home-manager.url = "github:nix-community/home-manager";
import-tree.url = "github:vic/import-tree";
@@ -20,21 +20,11 @@
flake = false;
};
nixpkgs.url = "https://channels.nixos.org/nixpkgs-unstable/nixexprs.tar.xz";
nvf = {
url = "github:notashelf/nvf";
inputs.nixpkgs.follows = "nixpkgs";
};
nixpkgs-lib.follows = "nixpkgs";
sops-nix = {
url = "github:Mic92/sops-nix";
inputs.nixpkgs.follows = "nixpkgs";
};
wrapper-modules = {
url = "github:BirdeeHub/nix-wrapper-modules";
inputs.nixpkgs.follows = "nixpkgs";
};
wrappers = {
url = "github:lassulus/wrappers";
inputs.nixpkgs.follows = "nixpkgs";
};
wrapper-modules.url = "github:BirdeeHub/nix-wrapper-modules";
};
}
-2
View File
@@ -4,6 +4,4 @@
# https://github.com/vic/flake-file/tree/main/modules/dendritic
inputs.flake-file.flakeModules.dendritic
];
systems = [ "x86_64-linux" "aarch64-linux" ];
}
-19
View File
@@ -1,19 +0,0 @@
{ self, inputs, ... }: {
flake.modules.nixos.audio = { config, pkgs, lib, ... }: {
# Enable sound with pipewire.
services.pulseaudio.enable = false;
security.rtkit.enable = true; # PulseAudio server uses this to acquire realtime priority.
services.pipewire = {
enable = true;
alsa.enable = true;
alsa.support32Bit = true;
pulse.enable = true;
# If you want to use JACK applications, uncomment this
#jack.enable = true;
# use the example session manager (no others are packaged yet so this is enabled by default,
# no need to redefine it in your config for now)
# media-session.enable = true;
};
};
}
+3 -7
View File
@@ -1,19 +1,15 @@
# This module is for programs with GUIs that run in a desktop environment
{ self, inputs, ... }: {
flake.modules.homeManager.desktop = { config, pkgs, lib, ... }: {
nixpkgs.config.problems.handlers.sublimetext4.broken = "ignore";
{ inputs, ... }:
{
flake.modules.homeManager.desktop = { pkgs, ... }: {
imports = with inputs.self.modules.homeManager; [
brave
ghostty
onepassword
vscode
];
home.packages = with pkgs; [
mangohud
sublime4
proton-vpn
joplin-desktop
discord
];
};
}
-147
View File
@@ -1,147 +0,0 @@
{ self, inputs, ... }: {
flake.modules.nixos.forgejo = {config, pkgs, lib, ... }:
let
cfg = config.forgejo;
needsPrivilegedPort = cfg.port < 1024;
in
{
options.forgejo = {
enable = lib.mkEnableOption "Enable Forgejo backed with PostgreSQL";
root_url = lib.mkOption {
type = lib.types.str;
};
port = lib.mkOption {
type = lib.types.port;
default = 3000;
description = "TCP port for the Forgejo web interface.";
};
openFirewall = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Open the Forgejo web interface port in the firewall.";
};
https = lib.mkEnableOption "Open the Forgejo web interface port in the firewall.";
};
config = lib.mkIf cfg.enable {
networking.firewall.allowedTCPPorts = lib.optionals cfg.openFirewall [ cfg.port ];
systemd.services.forgejo.serviceConfig = lib.mkIf needsPrivilegedPort {
AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ];
CapabilityBoundingSet = [ "CAP_NET_BIND_SERVICE" ];
PrivateUsers = lib.mkForce false;
};
sops.secrets = {
"forgejo/secret_key".owner = config.services.forgejo.user;
"forgejo/internal_token".owner = config.services.forgejo.user;
"forgejo/jwt_secret".owner = config.services.forgejo.user;
"forgejo/lfs_jwt_secret".owner = config.services.forgejo.user;
};
services = {
forgejo = {
enable = true;
lfs.enable = true;
database.type = "postgres";
settings = {
DEFAULT = {
RUN_MODE = "dev";
};
server = lib.mkMerge [
{
HTTP_PORT = cfg.port;
DISABLE_SSH = true;
ROOT_URL = cfg.root_url;
}
(lib.mkIf cfg.https {
PROTOCOL = "https";
COOKIE_SECURE = true;
KEY_FILE = config.mtls.keyFile;
CERT_FILE = config.mtls.certFile;
})
];
oauth2_client = {
ENABLE_AUTO_REGISTRATION = true;
USERNAME = "nickname";
UPDATE_AVATAR = true;
ACCOUNT_LINKING = "login";
REGISTER_EMAIL_CONFIRM = false;
};
repository = {
ENABLE_PUSH_CREATE_USER = true;
};
ui.SHOW_USER_EMAIL = false;
markup = {
ENABLED = true;
};
};
secrets = {
security = {
SECRET_KEY = lib.mkForce config.sops.secrets."forgejo/secret_key".path;
INTERNAL_TOKEN = lib.mkForce config.sops.secrets."forgejo/internal_token".path;
};
oauth2.JWT_SECRET = lib.mkForce config.sops.secrets."forgejo/jwt_secret".path;
server.LFS_JWT_SECRET = lib.mkForce config.sops.secrets."forgejo/lfs_jwt_secret".path;
};
dump = {
enable = true;
type = "tar";
interval = "*-*-* 00/12:00:00";
};
};
postgresql = {
enable = true;
enableTCPIP = false;
};
};
# https://forgejo.org/docs/latest/admin/command-line/#dump
systemd.services.forgejo-dump.serviceConfig.ExecStart = lib.mkForce ''
${lib.getExe config.services.forgejo.package} dump --verbose \
--type ${config.services.forgejo.dump.type} \
--database postgres \
--work-path ${config.services.forgejo.dump.backupDir}
'';
environment.systemPackages =
let
systemctl = lib.getExe' pkgs.systemd "systemctl";
clean-forgejo = (pkgs.writeShellScriptBin "clean-forgejo" ''
set -euo pipefail
sudo ${systemctl} stop forgejo.service
${lib.getExe' pkgs.coreutils "echo"} "Stopped Forgejo"
sudo ${lib.getExe' pkgs.coreutils "rm"} -rf ${config.services.forgejo.stateDir}
${lib.getExe' pkgs.coreutils "echo"} "Removed ${config.services.forgejo.stateDir}"
'');
clean-postgres = (pkgs.writeShellScriptBin "clean-postgres" ''
set -euo pipefail
sudo ${systemctl} stop postgresql.service
${lib.getExe' pkgs.coreutils "echo"} "Stopped PostgreSQL"
sudo ${lib.getExe' pkgs.coreutils "rm"} -rf ${config.services.postgresql.dataDir}
${lib.getExe' pkgs.coreutils "echo"} "Removed ${config.services.postgresql.dataDir}"
'');
in [
clean-forgejo
clean-postgres
(pkgs.writeShellScriptBin "clean-all" ''
set -euo pipefail
GREEN_CHECK="\e[32m\e[0m"
YELLOW_BANG="\e[33m!\e[0m"
${lib.getExe' pkgs.coreutils "echo"} -n -e "$YELLOW_BANG Remove everything related to Forgejo and the PostgreSQL database behind it?"
read -p " (y/n) " -n 1 -r
if [[ $REPLY =~ ^[Yy]$ ]]; then
${lib.getExe clean-forgejo}
${lib.getExe clean-postgres}
${lib.getExe' pkgs.coreutils "echo"} -e "$GREEN_CHECK Removed everything related to forgejo"
fi
'')
];
};
};
}
+39 -82
View File
@@ -4,103 +4,60 @@
desktopManager.gnome.enable = true;
displayManager.gdm = {
enable = true;
wayland = true;
banner = "Welcome to John's NixOS implementation";
};
udev.packages = [
pkgs.gnome-settings-daemon # For gnome systray icons
];
};
# Workaround for GNOME autologin: https://github.com/NixOS/nixpkgs/issues/103746#issuecomment-945091229
systemd.services."getty@tty1".enable = false;
systemd.services."autovt@tty1".enable = false;
};
flake.modules.homeManager.gnome = { config, pkgs, ... }:
let
# `gnome-extensions list` for a list
extensions = with pkgs.gnomeExtensions; [
appindicator # For gnome systray icons
dash-to-panel
gtile
space-bar
switcher
tactile
vitals
];
enabledExtensions = map (ext: ext.extensionUuid) extensions;
in
{
flake.modules.homeManager.gnome = { pkgs, ... }: {
dconf.settings = {
"org/gnome/desktop/interface" = {
color-scheme = "prefer-dark";
};
"org/gnome/shell" = {
disable-user-extensions = false;
# `gnome-extensions list` for a list
enabled-extensions = with pkgs.gnomeExtensions; [
appindicator.extensionUuid
gtile.extensionUuid
space-bar.extensionUuid
switcher.extensionUuid
tactile.extensionUuid
vitals.extensionUuid
dash-to-panel.extensionUuid
];
};
"org/gnome/desktop/wm/preferences" = {
button-layout = ":minimize,close";
};
};
gtk = {
enable = true;
theme = {
name = "Orchis-Dark";
package = pkgs.orchis-theme;
};
gtk4.theme = config.gtk.theme;
};
home.packages = [ pkgs.gnome-tweaks ] ++ extensions;
dconf.settings = {
"org/gnome/desktop/interface" = {
color-scheme = "prefer-dark";
};
"org/gnome/shell" = {
disable-user-extensions = false;
enabled-extensions = enabledExtensions;
};
"org/gnome/desktop/wm/preferences" = {
button-layout = ":minimize,close";
};
"org/gnome/desktop/wm/keybindings" = {
"switch-to-workspace-1" = ["<Super>1"];
"switch-to-workspace-2" = ["<Super>2"];
"switch-to-workspace-3" = ["<Super>3"];
"switch-to-workspace-4" = ["<Super>4"];
"move-to-workspace-1" = ["<Shift><Super>1"];
"move-to-workspace-2" = ["<Shift><Super>2"];
"move-to-workspace-3" = ["<Shift><Super>3"];
"move-to-workspace-4" = ["<Shift><Super>4"];
};
"org/gnome/settings-daemon/plugins/media-keys" = {
custom-keybindings = [
"/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/edit-nix/"
"/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/launch-browser/"
"/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/ad-dev/"
"/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/shutdown/"
];
};
"org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/edit-nix" = {
binding = "<Primary><Alt>n";
command = "code /etc/nixos";
name = "Edit Nix config";
};
"org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/launch-browser" = {
binding = "<Primary><Alt>b";
command = "brave";
name = "Launch Brave browser";
};
"org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/ad-dev" = {
binding = "<Primary><Alt>d";
command = ''code --file-uri "vscode-remote://ssh-remote+ad-nix/etc/nixos/ad-nix.code-workspace"'';
name = "Launch AppDaemon Development over Tailscale";
};
"org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/ws1" = {
binding = "<Super>1";
command = "wmctrl -s 0";
name = "Switch to workspace 1";
};
"org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/shutdown" = {
binding = "<Primary><Alt>p";
command = "gnome-session-quit --power-off --force";
name = "Shutdown immediately";
};
};
home.packages = with pkgs; [
gnome-tweaks
gnomeExtensions.appindicator # For gnome systray icons
gnomeExtensions.dash-to-panel
gnomeExtensions.gtile
gnomeExtensions.space-bar
gnomeExtensions.switcher
gnomeExtensions.tactile
gnomeExtensions.vitals
];
};
}
+2 -15
View File
@@ -1,24 +1,11 @@
# https://github.com/glabrie/dotfiles/blob/main/modules/system/settings/greetd.nix
{ inputs, ... }: {
flake.modules.nixos.greetd = { pkgs, lib, config, ... }:
let
niriPackage = if config.programs.niri.enable then config.programs.niri.package else pkgs.niri;
niriExe = lib.getExe niriPackage;
regreetExe = lib.getExe config.programs.regreet.package;
greeterNiriConfig = pkgs.writeText "niri-greeter.kdl" ''
spawn-at-startup "${regreetExe}"
hotkey-overlay {
skip-at-startup
}
'';
in {
programs.regreet.enable = true;
flake.module.nixos.greetd = { pkgs, lib, ... }: {
services.greetd = {
enable = true;
settings = {
default_session = {
command = "${pkgs.dbus}/bin/dbus-run-session -- ${niriExe} --config ${greeterNiriConfig}";
command = "${lib.getExe pkgs.tuigreet} --time --remember --cmd niri-session";
user = "greeter";
};
};
-445
View File
@@ -1,445 +0,0 @@
{ self, inputs, lib, ... }:
let
# Options that will be in common between the nixos module and the home-manager module.
mkOpts = config: let cfg = config.mtls; in {
enable = lib.mkEnableOption "Enable mTLS";
subject = lib.mkOption {
description = "The Common Name, DNS Name, or IP address that will be set as the Subject Common Name for the certificate. If no Subject Alternative Names (SANs) are configured (via the --san flag) then the subject will be set as the only SAN.";
type = lib.types.str;
};
certDir = lib.mkOption {
description = "String path to the directory where the certs will be stored";
type = lib.types.str;
};
caFile = lib.mkOption {
description = "String path for the root CA file";
type = lib.types.str;
default = "${cfg.certDir}/root_ca.crt";
};
keyFile = lib.mkOption {
description = "String path for the private key";
type = lib.types.str;
default = "${cfg.certDir}/key.pem";
};
certFile = lib.mkOption {
description = "String path for the public cert";
type = lib.types.str;
default = "${cfg.certDir}/cert.pem";
};
bundleFile = lib.mkOption {
description = "String path for the mTLS key bundle";
type = lib.types.str;
default = "${cfg.certDir}/mtls.pem";
};
san = lib.mkOption {
description = "List of SAN to give the mTLS cert";
type = lib.types.listOf lib.types.str;
default = [ ];
};
provisioner = lib.mkOption {
type = lib.types.str;
default = "admin";
};
lifetime = lib.mkOption {
type = lib.types.str;
default = "24h";
};
renew = {
enable = lib.mkOption {
description = "Enable automatic mTLS certificate renewal using a systemd timer.";
type = lib.types.bool;
default = cfg.enable;
};
onCalendar = lib.mkOption {
description = "systemd OnCalendar schedule for mTLS certificate renewal checks.";
type = lib.types.str;
default = "*:1/15";
};
randomizedDelaySec = lib.mkOption {
description = "Randomized delay added to renewal timer runs to avoid synchronized renewals.";
type = lib.types.str;
default = "5m";
};
user = lib.mkOption {
description = "User account to run the mTLS renewal service as.";
type = lib.types.str;
default = "root";
};
group = lib.mkOption {
description = "Group to run the mTLS renewal service as. Defaults to the configured renewal user.";
type = lib.types.nullOr lib.types.str;
default = "mtls";
};
reloadUnits = lib.mkOption {
description = "systemd units to try-reload-or-restart after a successful certificate renewal.";
type = lib.types.listOf lib.types.str;
default = [ ];
};
postCommands = lib.mkOption {
description = "Shell commands to run after a successful certificate renewal.";
type = lib.types.listOf lib.types.lines;
default = [ ];
};
};
};
mkMtlsGenerateScript = {
pkgs,
subject,
provisioner,
san,
certFile,
keyFile,
bundleFile,
lifetime,
user,
group,
}:
let
sanArgs = lib.concatMapStringsSep " " (s: "--san \"${s}\"") san;
in
pkgs.writeShellApplication {
name = "mtls-generate";
runtimeInputs = with pkgs; [ coreutils step-cli ];
text = ''
set -euo pipefail
step ca certificate ${subject} ${certFile} ${keyFile} \
--provisioner ${provisioner} \
--not-before=-5m --not-after=${lifetime} \
${sanArgs} \
"$@"
(umask 077; cat ${certFile} ${keyFile} > ${bundleFile})
chown ${user}:${group} ${certFile} ${keyFile} ${bundleFile}
chmod 640 ${certFile} ${keyFile} ${bundleFile}
printf '\033[32m\033[0m \033[1mmTLS Bundle:\033[0m %s\n' ${lib.escapeShellArg bundleFile}
'';
};
in
{
flake.modules.nixos.mtls = { config, lib, pkgs, ... }:
let
cfg = config.mtls;
mtlsRenewWrapper = inputs.self.wrappers.mtlsRenew.apply {
inherit pkgs;
inherit (cfg) certDir keyFile certFile bundleFile;
inherit (cfg.renew) user group reloadUnits postCommands;
systemd = {
after = [ "network-online.target" ];
wants = [ "network-online.target" ];
serviceConfig = {
Type = "oneshot";
User = cfg.renew.user;
Group = cfg.renew.group;
};
} // lib.optionalAttrs cfg.renew.enable {
startAt = cfg.renew.onCalendar;
};
};
in
{
options.mtls = (mkOpts config) // {
certDir = lib.mkOption {
description = "String path to where the mtls certs will be stored.";
type = lib.types.str;
default = "/etc/step-ca/certs";
};
certReaders = lib.mkOption {
description = "";
type = lib.types.listOf lib.types.str;
default = [ ];
};
};
config = lib.mkIf cfg.enable {
users.groups.certReaders = {
name = cfg.renew.group;
members = cfg.certReaders;
};
environment.systemPackages = with pkgs; lib.optionals cfg.enable [
# step-cli
(mkMtlsGenerateScript {
inherit pkgs;
inherit (cfg) subject provisioner san certFile keyFile bundleFile lifetime;
inherit (cfg.renew) user group;
})
(inputs.self.wrappers.mtlsCheck.apply {
inherit pkgs;
inherit (cfg) bundleFile;
}).wrapper
mtlsRenewWrapper.wrapper
];
systemd.tmpfiles.rules = [
"d ${cfg.certDir} 0750 ${cfg.renew.user} ${cfg.renew.group} -"
];
systemd.packages = lib.mkIf cfg.renew.enable [
mtlsRenewWrapper.outputs.systemd-system
];
systemd.timers.mtls-renew = lib.mkIf cfg.renew.enable {
wantedBy = [ "timers.target" ];
timerConfig = {
Persistent = true;
AccuracySec = "1us";
RandomizedDelaySec = cfg.renew.randomizedDelaySec;
};
};
};
};
flake.modules.homeManager.mtls = { config, lib, pkgs, ... }:
let
cfg = config.mtls;
mtlsRenewWrapper = inputs.self.wrappers.mtlsRenew.apply {
inherit pkgs;
inherit (cfg) certDir keyFile certFile bundleFile;
inherit (cfg.renew) reloadUnits postCommands;
systemctlArgs = [ "--user" ];
systemd = lib.optionalAttrs cfg.renew.enable {
startAt = cfg.renew.onCalendar;
};
};
in
{
options.mtls = (mkOpts config) // {
certDir = lib.mkOption {
description = "String path to where the mtls certs will be stored.";
type = lib.types.str;
default = "${config.home.homeDirectory}/.step/certs";
};
};
config = {
home.packages = with pkgs; lib.optionals cfg.enable [
# step-cli
(mkMtlsGenerateScript {
inherit pkgs;
inherit (cfg) keyFile certFile bundleFile;
inherit (cfg) subject provisioner san lifetime;
inherit (cfg.renew) user group;
})
(inputs.self.wrappers.mtlsCheck.apply {
inherit pkgs;
inherit (cfg) bundleFile;
}).wrapper
];
systemd.user.tmpfiles.rules = lib.mkIf cfg.enable [
"d ${cfg.certDir} 0700 - - -" # Ensure the cert directory exists and is writable by the user
];
# Create the systemd service files for the user.
xdg.configFile = lib.mkIf cfg.renew.enable {
"systemd/user/mtls-renew.service".source =
"${mtlsRenewWrapper.outputs.systemd-user}/systemd/user/mtls-renew.service";
"systemd/user/mtls-renew.timer".source =
"${mtlsRenewWrapper.outputs.systemd-user}/systemd/user/mtls-renew.timer";
"systemd/user/mtls-renew.timer.d/override.conf".text = ''
[Timer]
Persistent=true
AccuracySec=1us
RandomizedDelaySec=${cfg.renew.randomizedDelaySec}
'';
};
# Ensure the timer gets started
home.activation.mtlsRenewTimer = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
if [ -n "$XDG_RUNTIME_DIR" ] && [ -S "$XDG_RUNTIME_DIR/systemd/private" ]; then
if [ "${lib.boolToString (cfg.enable && cfg.renew.enable)}" = "true" ]; then
run ${pkgs.systemd}/bin/systemctl --user daemon-reload
if ${pkgs.systemd}/bin/systemctl --user cat mtls-renew.timer >/dev/null 2>&1; then
run ${pkgs.systemd}/bin/systemctl --user enable --now mtls-renew.timer
else
verboseEcho "mtls-renew.timer unit file is not available; skipping enable"
fi
else
run ${pkgs.systemd}/bin/systemctl --user disable --now mtls-renew.timer || true
run ${pkgs.systemd}/bin/systemctl --user daemon-reload || true
fi
fi
'';
};
};
flake.wrappers = {
mtlsCheck = inputs.wrappers.lib.wrapModule ({ config, lib, wlib, ... }: {
options = {
bundleFile = lib.mkOption {
description = "String path for the mTLS key bundle";
type = lib.types.str;
};
};
config = {
binName = "mtls-check";
# This pattern is necessary to wrap packages like openssl that provide more than one binary
package = config.pkgs.symlinkJoin {
name = "openssl";
paths = [ config.pkgs.openssl.bin config.pkgs.openssl.man ];
meta.mainProgram = "openssl";
};
args = [
"x509"
"-noout"
"-in" config.bundleFile
"-subject"
"-issuer"
"-ext" "subjectAltName,extendedKeyUsage"
"-enddate"
];
};
});
mtlsNeedsRenewal = inputs.wrappers.lib.wrapModule ({ config, lib, wlib, ... }: {
options = {
certFile = lib.mkOption {
description = "String path for the public cert";
type = lib.types.str;
};
};
config = {
binName = "mtls-needs-renewal";
package = config.pkgs.step-cli;
preHook = ''
echo "Checking renewal status..."
'';
args = [ "certificate" "needs-renewal" "${config.certFile}" ];
};
});
mtlsRenew = inputs.wrappers.lib.wrapModule ({ config, lib, wlib, ... }: {
imports = [ wlib.modules.systemd ];
options = {
certDir = lib.mkOption {
description = "String path to the directory where the certs will be stored";
type = lib.types.str;
};
keyFile = lib.mkOption {
description = "String path for the private key";
type = lib.types.str;
default = "${config.certDir}/key.pem";
};
certFile = lib.mkOption {
description = "String path for the public cert";
type = lib.types.str;
default = "${config.certDir}/cert.pem";
};
bundleFile = lib.mkOption {
description = "String path for the mTLS key bundle";
type = lib.types.str;
default = "${config.certDir}/mtls.pem";
};
user = lib.mkOption {
description = "User that should own the renewed certificate files.";
type = lib.types.nullOr lib.types.str;
default = null;
};
group = lib.mkOption {
description = "Group that should own the renewed certificate files.";
type = lib.types.nullOr lib.types.str;
default = null;
};
reloadUnits = lib.mkOption {
description = "systemd units to try-reload-or-restart after a successful renewal.";
type = lib.types.listOf lib.types.str;
default = [ ];
};
postCommands = lib.mkOption {
description = "Shell commands to run after a successful renewal.";
type = lib.types.listOf lib.types.lines;
default = [ ];
};
systemctlArgs = lib.mkOption {
description = "Additional arguments to pass to systemctl when reloading units.";
type = lib.types.listOf lib.types.str;
default = [ ];
};
};
config = {
binName = "mtls-renew";
package = let
systemctlCmd = "systemctl ${lib.escapeShellArgs config.systemctlArgs}";
hasReloadUnits = config.reloadUnits != [ ];
renewReloadScript = lib.concatMapStringsSep "\n" (unit: ''
if ${systemctlCmd} --quiet is-active "${unit}"; then
${systemctlCmd} try-reload-or-restart "${unit}"
fi
'') config.reloadUnits;
hasPostCommands = config.postCommands != [ ];
renewPostCommands = lib.concatStringsSep "\n" config.postCommands;
hasOwnership = config.user != null && config.group != null;
in
config.pkgs.writeShellApplication {
name = "mtls-renew";
runtimeInputs = with config.pkgs; [ coreutils step-cli systemd ];
text = ''
set -euo pipefail
YELLOW_BANG="\e[33m!\e[0m"
force=0
while [[ $# -gt 0 ]]; do
case $1 in
--force)
force=1
shift
;;
*)
echo -e "$YELLOW_BANG Warning: ignoring unrecognized argument '$1'"
exit 1
;;
esac
done
if [[ $force -eq 0 ]] && ! step certificate needs-renewal "${config.certFile}"; then
echo "Skipping renew"
exit 0
fi
echo "Renewing mTLS certificate"
step ca renew --force "${config.certFile}" "${config.keyFile}"
(umask 077; cat "${config.certFile}" "${config.keyFile}" > "${config.bundleFile}")
${lib.optionalString hasOwnership ''
chown ${config.user}:${config.group} ${config.certFile} ${config.keyFile} ${config.bundleFile}
chmod 640 ${config.certFile} ${config.keyFile} ${config.bundleFile}
''}
${lib.optionalString hasReloadUnits ''
echo "Reloading units: ${lib.concatStringsSep ", " config.reloadUnits}"
${renewReloadScript}
''}
${lib.optionalString hasPostCommands ''
echo "Running post commands"
${renewPostCommands}
''}
'';
};
extraPackages = [
config.pkgs.step-cli
];
systemd = {
description = "Renew the mTLS certificate when Smallstep marks it ready";
documentation = [
"https://smallstep.com/docs/step-ca/certificate-authority-server-production"
];
startLimitIntervalSec = 0;
after = [ "network-online.target" ];
wants = [ "network-online.target" ];
serviceConfig.Type = lib.mkDefault "oneshot";
# serviceConfig.ExecCondition = "";
};
};
});
};
}
-156
View File
@@ -1,156 +0,0 @@
{ self, inputs, ... }: {
flake.modules.nixos.niri = { pkgs, lib, ... }:
let
niriPkg = self.packages.${pkgs.stdenv.hostPlatform.system}.myNiri;
in
{
programs.niri = {
enable = true;
package = niriPkg;
};
systemd.user.services.niri.enableDefaultPath = false;
};
perSystem = { pkgs, lib, self', ... }:
let
nixGLPackage = inputs.nixgl.packages.${pkgs.stdenv.hostPlatform.system}.nixGLIntel;
noctaliaPkg = self'.packages.myNoctaliaNixGL;
terminalPkg = pkgs.ghostty;
myNiriUnwrapped = inputs.wrapper-modules.wrappers.niri.wrap {
inherit pkgs;
settings = {
spawn-at-startup = [
(lib.getExe noctaliaPkg)
];
xwayland-satellite.path = lib.getExe pkgs.xwayland-satellite;
input.keyboard.xkb.layout = "us,ua";
layout.gaps = 5;
binds = {
"Mod+Return".spawn-sh = lib.getExe terminalPkg;
# "Mod+Ctrl+Return".spawn-sh = "${lib.getExe noctaliaPkg} ipc call launcher toggle";
"Mod+Space".spawn-sh = "${lib.getExe noctaliaPkg} ipc call launcher toggle";
"Mod+B".spawn-sh = "${lib.getExe pkgs.brave}";
"Mod+E".spawn-sh = "xdg-open .";
"Mod+Alt+L".spawn-sh = "loginctl lock-session";
"Alt+F4".spawn-sh = "${lib.getExe' pkgs.coreutils "true"}";
"Mod+Shift+Q".close-window = { };
"Mod+S".spawn-sh = "${lib.getExe noctaliaPkg} ipc call launcher toggle";
"Mod+1".focus-workspace = 1;
"Mod+2".focus-workspace = 2;
"Mod+3".focus-workspace = 3;
"Mod+4".focus-workspace = 4;
"Mod+5".focus-workspace = 5;
"Mod+6".focus-workspace = 6;
"Mod+7".focus-workspace = 7;
"Mod+8".focus-workspace = 8;
"Mod+9".focus-workspace = 9;
"Mod+Tab".focus-workspace-previous = { };
# "Mod+WheelScrollDown".focus-workspace-down = { };
# "Mod+WheelScrollUp".focus-workspace-up = { };
# "Mod+Up".focus-window-up = { };
# "Mod+Down".focus-window-down = { };
"Mod+Up".focus-workspace-up = { };
"Mod+Down".focus-workspace-down = { };
"Mod+Left".focus-column-left = { };
"Mod+Right".focus-column-right = { };
"Mod+K".focus-window-up = { };
"Mod+J".focus-window-down = { };
"Mod+H".focus-column-left = { };
"Mod+L".focus-column-right = { };
"Mod+Home".focus-column-first = { };
"Mod+End".focus-column-last = { };
# "Mod+Shift+Left".focus-monitor-left = { };
# "Mod+Shift+Right".focus-monitor-right = { };
# "Mod+Shift+Up".focus-monitor-up = { };
# "Mod+Shift+Down".focus-monitor-down = { };
"Mod+Shift+1".move-column-to-workspace = 1;
"Mod+Shift+2".move-column-to-workspace = 2;
"Mod+Shift+3".move-column-to-workspace = 3;
"Mod+Shift+4".move-column-to-workspace = 4;
"Mod+Shift+5".move-column-to-workspace = 5;
"Mod+Shift+6".move-column-to-workspace = 6;
"Mod+Shift+7".move-column-to-workspace = 7;
"Mod+Shift+8".move-column-to-workspace = 8;
"Mod+Shift+9".move-column-to-workspace = 9;
"Mod+Ctrl+Up".move-window-up = { };
"Mod+Ctrl+Down".move-window-down = { };
"Mod+Ctrl+Left".move-column-left = { };
"Mod+Ctrl+Right".move-column-right = { };
"Mod+Ctrl+K".move-window-up = { };
"Mod+Ctrl+J".move-window-down = { };
"Mod+Ctrl+H".move-column-left = { };
"Mod+Ctrl+L".move-column-right = { };
"Mod+Ctrl+Home".move-column-to-first = { };
"Mod+Ctrl+End".move-column-to-last = { };
"Mod+Shift+Ctrl+Left".move-column-to-monitor-left = { };
"Mod+Shift+Ctrl+Right".move-column-to-monitor-right = { };
"Mod+Shift+Ctrl+Up".move-column-to-monitor-up = { };
"Mod+Shift+Ctrl+Down".move-column-to-monitor-down = { };
"Mod+Ctrl+F".expand-column-to-available-width = { };
"Mod+C".center-column = { };
"Mod+Ctrl+C".center-visible-columns = { };
"Mod+Minus".set-column-width = "-10%";
"Mod+Equal".set-column-width = "+10%";
"Mod+Shift+Minus".set-window-height = "-10%";
"Mod+Shift+Equal".set-window-height = "+10%";
"Mod+T".toggle-window-floating = { };
"Mod+F".fullscreen-window = { };
"Mod+W".toggle-column-tabbed-display = { };
"Mod+O".toggle-overview = { };
"Ctrl+Shift+1".screenshot = { };
"Ctrl+Shift+2".screenshot-screen = { };
"Ctrl+Shift+3".screenshot-window = { };
"Mod+Shift+Slash".show-hotkey-overlay = { };
"Mod+Escape".toggle-keyboard-shortcuts-inhibit = { };
"Mod+Shift+P".power-off-monitors = { };
"Ctrl+Alt+Delete".quit = { };
"Mod+WheelScrollRight".focus-column-right = { };
"Mod+WheelScrollLeft".focus-column-left = { };
"Mod+Ctrl+WheelScrollRight".move-column-right = { };
"Mod+Ctrl+WheelScrollLeft".move-column-left = { };
"Mod+Shift+WheelScrollDown".focus-column-right = { };
"Mod+Shift+WheelScrollUp".focus-column-left = { };
# "Mod+Ctrl+Shift+WheelScrollDown".move-column-right = { };
# "Mod+Ctrl+Shift+WheelScrollUp".move-column-left = { };
};
};
};
in {
packages.myNiri = myNiriUnwrapped;
packages.myNiriNixGL = pkgs.symlinkJoin {
name = "my-niri-nixgl";
paths = [ myNiriUnwrapped ];
nativeBuildInputs = [ pkgs.makeWrapper ];
meta.mainProgram = "niri";
postBuild = ''
for exe in "$out"/bin/*; do
if [[ -f "$exe" ]]; then
base="$(basename "$exe")"
mv "$exe" "$out/bin/.''${base}-real"
makeWrapper ${lib.getExe nixGLPackage} "$exe" \
--add-flags "$out/bin/.''${base}-real"
fi
done
'';
};
};
}
-63
View File
@@ -1,63 +0,0 @@
{ self, inputs, ... }: {
flake.modules.nixos.base = { config, pkgs, lib, ... }: {
imports = [
self.modules.nixos.audio
];
nix.settings.experimental-features = [ "nix-command" "flakes" ];
nixpkgs.config = {
permittedInsecurePackages = [ "openssl-1.1.1w" ];
allowUnfree = true;
problems.handlers.sublimetext4.broken = "ignore";
};
# Set your time zone.
time.timeZone = "US/Central";
# Select internationalisation properties.
i18n = {
defaultLocale = "en_US.UTF-8";
extraLocaleSettings = {
LC_ADDRESS = "en_US.UTF-8";
LC_IDENTIFICATION = "en_US.UTF-8";
LC_MEASUREMENT = "en_US.UTF-8";
LC_MONETARY = "en_US.UTF-8";
LC_NAME = "en_US.UTF-8";
LC_NUMERIC = "en_US.UTF-8";
LC_PAPER = "en_US.UTF-8";
LC_TELEPHONE = "en_US.UTF-8";
LC_TIME = "en_US.UTF-8";
};
};
fonts.packages = with pkgs; [
nerd-fonts.hack
nerd-fonts.sauce-code-pro
];
# Need for less pain-in-the-ass for doing normal-ish stuff
# This is needed for VSCode remote support. Read: https://nixos.wiki/wiki/Visual_Studio_Code
programs.nix-ld.enable = true;
# Configure network connections interactively with nmcli or nmtui.
networking.networkmanager.enable = true;
services.openssh.enable = true;
security.polkit.enable = true; # polkit
services.gnome.gnome-keyring.enable = true; # secret service
security.pam.services.swaylock = {};
# Enable sound with pipewire.
services.pulseaudio.enable = false;
security.rtkit.enable = true; # PulseAudio server uses this to acquire realtime priority.
services.pipewire = {
enable = true;
alsa.enable = true;
alsa.support32Bit = true;
pulse.enable = true;
# If you want to use JACK applications, uncomment this
#jack.enable = true;
};
};
}
-34
View File
@@ -1,34 +0,0 @@
{ self, inputs, ... }: {
perSystem = { pkgs, lib, ... }:
let
nixGLPackage = inputs.nixgl.packages.${pkgs.stdenv.hostPlatform.system}.nixGLIntel;
myNoctaliaUnwrapped = inputs.wrapper-modules.wrappers.noctalia-shell.wrap {
inherit pkgs;
runtimePkgs = [
pkgs.qt6.qtwebsockets
];
# settings =
# (builtins.fromJSON
# (builtins.readFile ./noctalia.json)).settings;
};
in {
packages.myNoctalia = myNoctaliaUnwrapped;
packages.myNoctaliaNixGL = pkgs.symlinkJoin {
name = "my-noctalia-nixgl";
paths = [ myNoctaliaUnwrapped ];
nativeBuildInputs = [ pkgs.makeWrapper ];
meta.mainProgram = "noctalia-shell";
postBuild = ''
for exe in "$out"/bin/*; do
if [[ -f "$exe" ]]; then
base="$(basename "$exe")"
mv "$exe" "$out/bin/.''${base}-real"
makeWrapper ${lib.getExe nixGLPackage} "$exe" \
--add-flags "$out/bin/.''${base}-real"
fi
done
'';
};
};
}
-83
View File
@@ -1,83 +0,0 @@
# This module provides all the shell options
{ self, inputs, ... }:
{
flake.modules.homeManager.shell-tools =
{ config, pkgs, ... }:
{
imports = with inputs.self.modules.homeManager; [
# bash
zsh
files
];
home.packages = with pkgs; [
btop
uv
xclip
inputs.self.packages.${pkgs.stdenv.hostPlatform.system}.shell-tools
];
home.shell.enableShellIntegration = true;
};
perSystem =
{
system,
pkgs,
self',
...
}:
{
packages.shell-tools = inputs.wrappers.lib.wrapPackage {
inherit pkgs;
# binName = "show-tools";
package = (
pkgs.symlinkJoin {
name = "show-tools";
meta.mainProgram = "show-tools";
paths = with pkgs; [
nh
ripgrep
fd
jq
wget
curl
dig
bat
self'.packages.gdu
self'.packages.my-eza
self'.packages.yazi
hostname
iproute2
direnv
(writeShellApplication {
name = "show-tools";
text = ''
IFS=':' read -r -a path_dirs <<< "''${PATH:-}"
for dir in "''${path_dirs[@]}"; do
[[ "$dir" == */bin ]] || continue
[[ -d "$dir" ]] || continue
printf '%s\n' "$dir"/*
done
'';
})
];
}
);
};
packages.gdu = inputs.wrappers.lib.wrapPackage {
inherit pkgs;
package = pkgs.gdu;
args = [
"-x"
"--si"
"--collapse-path"
"--mouse"
"$@"
];
};
};
}
-100
View File
@@ -1,100 +0,0 @@
{ self, inputs, ... }:
let
mkPrincipalArgs = principals:
builtins.concatLists (map (principal: [ "--principal" principal ]) principals);
in
{
perSystem = { system, self', pkgs, lib, ... }: {
packages.ssh-certs = inputs.wrappers.lib.wrapPackage {
inherit pkgs;
package = (pkgs.symlinkJoin {
name = "ssh-certs";
meta.mainProgram = "sign-ssh-user-cert";
paths = [
(inputs.self.wrappers.signUserWrapper.apply {
inherit pkgs;
provisioner = "admin";
overwrite = true;
validUsers = [ "john" "root" "appdaemon" ];
}).wrapper
(inputs.self.wrappers.signHostWrapper.apply {
inherit pkgs;
provisioner = "admin";
overwrite = true;
# extraPrincipals = [ "home-pc" ];
}).wrapper
];
});
};
};
flake.wrappers.signHostWrapper = inputs.wrappers.lib.wrapModule ({config, lib, wlib, ... }: {
options = {
provisioner = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = "admin";
};
extraPrincipals = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
};
overwrite = lib.mkEnableOption "Overwrite existing cert file?";
};
config = {
binName = "sign-ssh-host-cert";
package = config.pkgs.step-cli;
extraPackages = with config.pkgs; [ hostname iproute2 systemd ];
preHook = ''
HOSTNAME=$(hostname -s)
IP_ADDRESS=$(ip -4 -o addr show scope global | while read -r _ _ _ addr _; do
case "$addr" in
192.168.1.*/*)
printf '%s\n' "''${addr%%/*}"
break
;;
esac
done)
echo "Signing SSH host cert for $HOSTNAME at $IP_ADDRESS"
'';
args =
[
"ssh" "certificate"
"--host" "--sign"
"--principal" "$HOSTNAME"
"--principal" "$IP_ADDRESS"
]
++ lib.optionals (config.provisioner != null) [ "--provisioner" "${config.provisioner}" ]
++ lib.optionals config.overwrite [ "-f" ]
++ mkPrincipalArgs config.extraPrincipals;
postHook = ''
systemctl reload-or-restart sshd
'';
};
});
flake.wrappers.signUserWrapper = inputs.wrappers.lib.wrapModule ({config, lib, wlib, ... }: {
options = {
provisioner = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = "admin";
};
validUsers = lib.mkOption {
description = "A list of the user names that this cert will be valid for";
type = lib.types.listOf lib.types.str;
default = [ ];
};
overwrite = lib.mkEnableOption "Overwrite existing cert file?";
};
config = {
binName = "sign-ssh-user-cert";
package = config.pkgs.step-cli;
args = [ "ssh" "certificate" "--sign" ]
++ lib.optionals (config.provisioner != null) [ "--provisioner" "${config.provisioner}" ]
++ lib.optionals config.overwrite [ "-f" ]
++ mkPrincipalArgs config.validUsers;
};
});
}
-51
View File
@@ -1,51 +0,0 @@
{ self, inputs, ... }: {
flake.modules.homeManager.step-client = { config, pkgs, lib, ... }: {
home.file.".step/config/defaults.json".text = builtins.toJSON {
ca-url = "https://janus.john-stream.com/";
fingerprint = "2036c44f7b5901566ff7611ea6c927291ecc6d2dd00779c0eead70ec77fa10d6";
root = ../hosts/janus/root_ca.crt;
};
home.packages = [
inputs.self.packages.${pkgs.stdenv.hostPlatform.system}.step-bootstrap
];
# sops.secrets."step-ca-defaults" = {
# sopsFile = ../hosts/janus/defaults.json;
# format = "json";
# key = ""; # This causes it to decode the whole file
# path = "${config.home.homeDirectory}/defaults.json";
# mode = "0400";
# };
};
perSystem = { system, pkgs, lib, ... }: {
packages.step-bootstrap = (inputs.self.wrappers.stepBootstrap.apply {
inherit pkgs;
ca-url = "https://janus.john-stream.com";
fingerprint = "2036c44f7b5901566ff7611ea6c927291ecc6d2dd00779c0eead70ec77fa10d6";
install = true;
}).wrapper;
};
flake.wrappers.stepBootstrap = inputs.wrappers.lib.wrapModule ({config, lib, wlib, ... }: {
options = {
ca-url = lib.mkOption {
type = lib.types.str;
};
fingerprint = lib.mkOption {
type = lib.types.str;
};
install = lib.mkEnableOption "Install the cert to the system trust store";
};
config = {
binName = "step-bootstrap";
package = config.pkgs.step-cli;
args = [
"ca" "bootstrap"
"--ca-url" config.ca-url
"--fingerprint" config.fingerprint
]
++ lib.optional config.install "--install";
};
});
}
+12 -44
View File
@@ -4,63 +4,28 @@ let
hostname = "janus";
in
{
flake.modules.nixos.janus-ca =
{ config, lib, ... }:
let
cfg = config.janus-ca;
johnHome = lib.attrByPath [ "users" "users" username "home" ] "/home/${username}" config;
johnGroup = lib.attrByPath [ "users" "users" username "group" ] username config;
cfgInEtc = lib.hasPrefix "/etc/" cfg.certDir;
certDirEtcPath =
if cfgInEtc then
lib.removePrefix "/etc/" cfg.certDir
else
cfg.certDir;
mkStepRules = home: user: group: [
"d ${home}/.step 0700 ${user} ${group} -"
"d ${home}/.step/config 0700 ${user} ${group} -"
"d ${home}/.step/certs 0700 ${user} ${group} -"
"L+ ${home}/.step/config/defaults.json - - - - /etc/step-ca/defaults.json"
"L+ ${home}/.step/certs/root_ca.crt - - - - ${cfg.certDir}/root_ca.crt"
];
in
{
options.janus-ca = {
certDir = lib.mkOption {
description = "String path to where the mtls certs will be stored.";
type = lib.types.str;
default = "/etc/step-ca/certs";
};
};
config = {
environment.etc = lib.mkIf cfgInEtc {
"step-ca/defaults.json".text = builtins.toJSON {
ca-url = "https://janus.john-stream.com/";
fingerprint = "2036c44f7b5901566ff7611ea6c927291ecc6d2dd00779c0eead70ec77fa10d6";
root = ./root_ca.crt;
};
};
systemd.tmpfiles.rules =
mkStepRules johnHome username johnGroup
++ mkStepRules "/root" "root" "root";
};
flake.modules.homeManager.janus-ca = { config, ... }: {
home.file.".step/config/defaults.json".text = builtins.toJSON {
"ca-url" = "https://janus.john-stream.com/";
fingerprint = "2036c44f7b5901566ff7611ea6c927291ecc6d2dd00779c0eead70ec77fa10d6";
root = "${config.home.homeDirectory}/.step/certs/root_ca.crt";
};
home.file.".step/certs/root_ca.crt".source = ./root_ca.crt;
};
flake.nixosConfigurations."${hostname}" = inputs.nixpkgs.lib.nixosSystem {
modules = with inputs.self.modules; [
nixos.lxc
nixos.mysops
nixos.step-ssh-host
nixos.janus-ca
inputs.home-manager.nixosModules.home-manager
nixos."${username}"
nixos.zsh
nixos.docker
nixos.login-text
nixos.mtls
{
networking.hostName = hostname;
sops.defaultSopsFile = ../../../keys/secrets.yaml;
step-ssh-host = {
hostname = hostname;
};
@@ -75,8 +40,11 @@ in
home-manager.users."${username}" = {
imports = with inputs.self.modules.homeManager; [
mysops
sops
step-ssh-user
janus-ca
];
shell.program = "zsh";
docker.enable = true;
};
}
-1
View File
@@ -1 +0,0 @@
2036c44f7b5901566ff7611ea6c927291ecc6d2dd00779c0eead70ec77fa10d6
-76
View File
@@ -1,76 +0,0 @@
{ withSystem, self, inputs, ... }:
let
username = "john";
hostname = "john-kde";
in
{
flake.modules.homeManager."${hostname}" = { config, pkgs, lib, ... }:
let
selfPkgs = inputs.self.packages.${pkgs.stdenv.hostPlatform.system};
flakeDir = "${config.xdg.configHome}/home-manager";
in
{
imports = with inputs.self.modules.homeManager; [
rebuild
john
# mtls
# restic
docker
desktop
step-client
# mysops
# myPackage
# myStepClient
];
# TODO: make this more restrictive, rather than allowing all unfree packages
nixpkgs.config.allowUnfree = true;
nixpkgs.config.permittedInsecurePackages = [ "openssl-1.1.1w" ];
targets.genericLinux.enable = true;
home.username = "${username}";
home.homeDirectory = "/home/${username}";
home.packages = with pkgs; [
selfPkgs.jsl-zsh
selfPkgs.my-neovim
selfPkgs.ssh-certs
# selfPkgs.step-bootstrap
# selfPkgs.wg-platform
# self'.packages.myWrappedPackage
# (inputs.self.wrappers.test-push.apply {
# inherit pkgs flakeDir;
# host = testHost;
# target = testTarget;
# }).wrapper
];
homeManagerFlakeDir = flakeDir;
docker.enable = true;
ssh = {
certificates.enable = true;
knownHosts = [
"fded:fb16:653e:25da:be24:11ff:fea0:753f ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJ9ZqiWPrCwHjxFCiu0lT4rlQs7KyMapxKJQQ5PJP1eh"
];
matchSets = {
certs = true;
appdaemon = true;
homelab = true;
dev = true;
tailscale = true;
};
};
};
flake.homeConfigurations."john@john-kde" = inputs.home-manager.lib.homeManagerConfiguration {
pkgs = import inputs.nixpkgs.outPath {
localSystem.system = "x86_64-linux";
config = {
allowUnfree = true;
permittedInsecurePackages = [ "openssl-1.1.1w" ];
problems.handlers.sublimetext4.broken = "ignore";
};
};
modules = [ inputs.self.modules.homeManager."${hostname}" ]; # Uses the module defined above
};
}
+91 -41
View File
@@ -1,38 +1,87 @@
{ self, inputs, ... }:
{
flake.modules.nixos.p14sConfiguration = { config, pkgs, lib, ... }:
flake.modules.nixos.p14sConfiguration = { pkgs, lib, ... }:
let
hostname = "john-p14s";
homeDirectory = config.home-manager.users.john.home.homeDirectory;
flakeDir = "${homeDirectory}/Documents/dendritic";
selfPkgs = inputs.self.packages.${pkgs.stdenv.hostPlatform.system};
in
{
imports = [
self.modules.nixos.base
self.modules.nixos.p14sHardware
self.modules.nixos.onepassword
];
rebuild.flakeDir = flakeDir;
nix.settings.experimental-features = [ "nix-command" "flakes" ];
nixpkgs.config = {
allowUnfree = true;
permittedInsecurePackages = [ "openssl-1.1.1w" ];
};
networking.hostName = hostname;
networking = {
hostName = hostname;
networkmanager.enable = true;
};
# Enable automatic login for the user.
services.displayManager.autoLogin.enable = true;
services.displayManager.autoLogin.user = "john";
# Define a user account. Don't forget to set a password with 'passwd'.
users.users.john = {
isNormalUser = true;
description = "John Lancaster";
extraGroups = [
"networkmanager"
"wheel"
];
shell = pkgs.zsh;
};
programs.zsh.enable = true;
services.openssh.enable = true;
services.tailscale.enable = true;
# List packages installed in system profile. To search, run:
# $ nix search wget
environment.systemPackages = with pkgs; [
wget
cacert
busybox
dig
samba
selfPkgs.my-neovim
selfPkgs.wg-platform
selfPkgs.jsl-zsh
];
security.pam.services.swaylock = {};
security.pam.services.swaylock.fprintAuth = true;
programs._1password.enable = true;
programs._1password-gui = {
enable = true;
# Certain features, including CLI integration and system authentication support,
# require enabling PolKit integration on some desktop environments (e.g. Plasma).
polkitPolicyOwners = [ "john" ];
# TODO this should not be a hardcoded username
};
# This is needed for VSCode remote support. Read: https://nixos.wiki/wiki/Visual_Studio_Code
programs.nix-ld.enable = true;
programs.steam = {
enable = true;
gamescopeSession.enable = true;
remotePlay.openFirewall = true; # Open ports in the firewall for Steam Remote Play
dedicatedServer.openFirewall = true; # Open ports in the firewall for Source Dedicated Server
localNetworkGameTransfers.openFirewall = true; # Open ports in the firewall for Steam Local Network Game Transfers
};
# progams.gamemode.enable = true;
# Some programs need SUID wrappers, can be configured further or are
# started in user sessions.
# programs.mtr.enable = true;
# programs.gnupg.agent = {
# enable = true;
# enableSSHSupport = true;
# };
# This value determines the NixOS release from which the default
# settings for stateful data, like file locations and database versions
# on your system were taken. It's perfectly fine and recommended to leave
@@ -41,40 +90,41 @@
# (e.g. man configuration.nix or on https://nixos.org/nixos/options.html).
system.stateVersion = "24.05"; # Did you read the comment?
# Set your time zone.
time.timeZone = "America/Chicago";
# Select internationalisation properties.
i18n.defaultLocale = "en_US.UTF-8";
i18n.extraLocaleSettings = {
LC_ADDRESS = "en_US.UTF-8";
LC_IDENTIFICATION = "en_US.UTF-8";
LC_MEASUREMENT = "en_US.UTF-8";
LC_MONETARY = "en_US.UTF-8";
LC_NAME = "en_US.UTF-8";
LC_NUMERIC = "en_US.UTF-8";
LC_PAPER = "en_US.UTF-8";
LC_TELEPHONE = "en_US.UTF-8";
LC_TIME = "en_US.UTF-8";
};
services.libinput.enable = true; # Enable touchpad support (enabled default in most desktopManager).
services.fprintd.enable = true; # Enables fingerprint sensor
home-manager.users.root = {
imports = with inputs.self.modules.homeManager; [
rebuild
];
home.stateVersion = "25.11";
};
home-manager.users.john.imports = with inputs.self.modules.homeManager; [
gnome
desktop
mysops
rebuild
{
my-vscode.enable = true;
mysops.hostSecretFile = "${flakeDir}/modules/hosts/john-p14s/secrets.yaml";
homeManagerFlakeDir = "${flakeDir}";
ssh.certificates.enable = true;
ssh.matchSets = {
appdaemon = true;
homelab = true;
dev = true;
certs = true;
};
}
];
sops.defaultSopsFile = ./secrets.yaml;
sops.age.sshKeyPaths = [ "${homeDirectory}/.ssh/id_ed25519" ];
mtls = {
# Enable sound with pipewire.
services.pulseaudio.enable = false;
security.rtkit.enable = true; # PulseAudio server uses this to acquire realtime priority.
services.pipewire = {
enable = true;
subject = hostname;
alsa.enable = true;
alsa.support32Bit = true;
pulse.enable = true;
# If you want to use JACK applications, uncomment this
#jack.enable = true;
# use the example session manager (no others are packaged yet so this is enabled by default,
# no need to redefine it in your config for now)
# media-session.enable = true;
};
};
}
+1 -6
View File
@@ -12,14 +12,9 @@
"${inputs.nixos-hardware}/lenovo/thinkpad/p14s/amd/gen4"
] ++ (with self.modules.nixos; [
p14sConfiguration
janus-ca
rebuild
sudo
john
gnome
steam
wireguard
mtls
john
# greetd
# niri
]);
-26
View File
@@ -1,26 +0,0 @@
restic_password: ENC[AES256_GCM,data:8KeL7kOMTHkK+ZpY3aq0/S4UYbkZh72FxJAolcVes79NPfdHiNi77fmAZuXb8Gxasu16vk4y6ksWFmrZquvcbg+cLt8Yze+qvuCiUW62IOppzRIBLk6w2f8uPhU41gsVuj6u4Jz/Su7miNBvJYmF8vulIwDKS7qg43dzIKXY4q1HaRHCD12PkU9EfGpIgivdNCOO0lCE6m/O3IB8Ed4R51scBnVrIj89K5OuTepvIrGElmXiWqBdsTzCIaJCZJ8fDnzJPorLhsTAzKCnQhNoIrW/aYArzj9+WmglmGuqeaoemBoNTrYC9bssUwuWAOqsf1HC7gWST/dP9FmKl7UHlgkslblRcRedFPAQjDhy/RgZCj8hs/XrVYOzwW8WJ0XeB9UPK03QPJc8EZdz9r2Q0+U3LQxMV6Jne5JZGsMpII4b+7b/WbRZTooX3G09MES/gzwD+hyBr++6EpP9QTYs+4XsNLNN+S6y5+YS/7XmRvR7xb93eZ4EfUUKiQYejVKQBl9b8QTJa3q26RPa/82O5zQlzd1w/EjA4G6Vv8H6wvK0fHNwpU3hl9V8EL7Ca7JKfJr11Vw0fNHx2nXjOLURt27wbDeWUaSrzI+z2h/0TSiKWas/Eu5+4L9n7E3H5u5Hg2uaCQkDpy6RJkW9UL6lCC1YEJw5yHTugrPNoMcRlAKRoSIeSZSPfGl0CKYAC39s422xX2f46GpJu0bO8GG/XN9Qk+SxqPHGuzx7nWghFQctZb1ssGKuvHKqaNYZvWeWqqkxyPWb6xjaBaSGxcp5MsET+RAu2F1XgX9Is11mIJETlHhRE3OpMSHh7toIiuNs8YzsgkedVFWhx+Xu27DWvOFvg/01Rh6s5RGJKMgL7WQMLTtY2KI/ko648WXVoCtVymG8d30qWnSt8319jkILX/wdB8Vqp5ceRqHwDE89jhEh0GOg07eLcREnh+gjXij1/bS66/+hHk4jhkzGtbdfEji9sro4Nm+kVRA+6fgJjCvt1sAvjuj10Scx4uH3AdDTW+y0KaniK8EHTZzaGDuaV+tk3hZEfpwpdan5mquoQvFdZWsWlDefX7gRq38szmi9LtULEai0WKOtqnnDIOnWk/3NYeNdzJ28YzrUDbkxtghkRgr7fNGEtyCGRuWRRMc5mVp2FPzAwRKk29ShILpWbvfUzwAWUOEpyAS4bstzlMybf9L8sp20+Ier8gOMGJ81eXXXmhtgMn6aWZ6EDGe1Shl0gDM0JUEAkA2+kf3oIZAvcWiFxj7vyd4Aacw3RetUSJcy8JwbV3I/isxKyy/xs8zE2PUY0z3Cwh9MpVP9G8N6Mef6v2DNDNg+2iJ/eslQdbZRQzdJwnBMhpOopzRwuUaC7jJKyGBkcNbbmNjceJWEue0XVjIcZHJ7BFe3jKluezNIJIf268V35+HIpXJA7BUJn8NPCIQXze5Frm46U2aRTQ53LU4HBGLXYPzx8QLsn0jLVy518w/TztawdT3/SmSWNwYTuZG18TmXs9v3oB6PrUr4M7kJbNRLpRb0ZelbMDwpR074FPwwud6fkjR2lLQBbufmPUBdpVJZ2xFjijle8h1h29zoFsydFNq5wSXUO/nm/S09KdKi5F2S1uKP+NL80OVEeC+QXcBdTry07meVIhsBOh+9ajy1ExaMaPs2xjB5QvTgJA9kfJOAoUygjMC33FwdRw10eGP//IsiRm9EukF1o++MfZnAtcjhoOoy79TjlO8p8YVNwEhZ14Ik/kIizyQtr0gR+pVEIc6+1W29UOWNRXb+ZavXRDq0wvxigMagfcL+zIr4gctufTfubiOmVa85qVJB,iv:VogP+OQvlsYCSqlmffO+o99C0hJm63ZqLXVd2B0oom0=,tag:+se1QHW6xLMj/dZrbY0MKQ==,type:str]
wireguard_private_key: ENC[AES256_GCM,data:sCskwDhemU1y4M4A4R9KxwiL8q+FtxnUqg1omU7yS81H1bbSM804hNzmq+A=,iv:7wNMAG+7wYYXYgKEohIAYisMN5lbz+M5RhCEaHL4yWE=,tag:FENdJTEEGD9cEJVwIIUQDA==,type:str]
sops:
age:
- recipient: age1f6drjusg866yscj8029tk4yfpgecklrvezldm02ankm6h8nnwu5s2u6ahy
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHS0F1R0NnV0gyNFlkUEhZ
UkxvN1ZLQTFsV0tnR0pLNFJycmduU1VXUlFVCjBkRGxQd1B1c3cweTRsZm9OUCt0
anU3RTFUUkxoaXlhdlR0RkxPclVUdVkKLS0tIG9kTEVNK2piRWI5ZWFSejFFUGtD
emtTUGk0cVZWR3F5R05WTTFJUFUwNTAKrRYQAJen6QVSgaOyqPxSIniHiMLUfXuv
/O1Ebz5xLWn99EhloqW7rHhUxXlhxP2CmwfYXizyKFa6nAu6R+BCgg==
-----END AGE ENCRYPTED FILE-----
- recipient: age1ykcs39e62pz3xu6cedg8ea685kv5d5qsrhgkndygzm8rx30xd5ys5t3qxt
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBXNWRXZTFWREIzRVhRaWRB
anNDb3hsZzFOMFMrclhmeldaOWY4b3BJekFBCkJhbjRvRUwva3lFbzNTKzJqK2p2
WVlMaXpvczlhdGduZHIrb2xPaEg3OUUKLS0tIHFtM09mN0FEUWdjWEVEL3VXL00w
WHpOY280S2hpVU1mNnozODRoMnB4bGMKK5RrDK2kAZlWf2igqyzWgshxLPj+f74A
mCmMLDHo5drNieFYp+guqHaHnZkf9IzpAglj7x6wCITjk+l6go5KvA==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2026-03-27T03:59:09Z"
mac: ENC[AES256_GCM,data:MsPAxssWUSvsJQP0Ogrl3r/GoVqeL1L95YTJQbAJZ4FVxhRXP7KfbUnKSclzU6G8CP5WxV18TXZfB4JITKG3Lz5rtVpD/WFMdhDmve0f6BPMAimle2ajWUaWYNePvEynClX3nydLk3h31DjHGa8YvJZqW2ieDb/JDMdBXiLTrWc=,iv:82ul5CV3XXllFCDJfF6beIcAIFj71ycJJF4iEQvovMA=,tag:108L7VkAoPUTLtV74qgrrA==,type:str]
unencrypted_suffix: _unencrypted
version: 3.12.2
+48 -58
View File
@@ -1,72 +1,67 @@
{ withSystem, self, inputs, ... }:
{ inputs, ... }:
let
username = "john";
hostname = "john-pc-ubuntu";
# testTarget = "fded:fb16:653e:25da:be24:11ff:fe89:1cc3"; # soteria
# testTarget = "fded:fb16:653e:25da:be24:11ff:fea0:753f"; # test-nix
testHost = "soteria"; # which host to test build
testTarget = "test-nix";
testTarget = "fded:fb16:653e:25da:be24:11ff:fe89:1cc3"; # soteria
in
{
flake.modules.homeManager."${hostname}" = { config, pkgs, lib, ... }:
flake.modules.homeManager."${hostname}" = { pkgs, config, ... }:
let
selfPkgs = inputs.self.packages.${pkgs.stdenv.hostPlatform.system};
resticPasswordFile = "${config.xdg.configHome}/restic/password.txt";
flakeDir = "${config.xdg.configHome}/home-manager/jsl-dendritic";
certDir = "${config.mtls.certDir}";
mtlsBundle = "${certDir}/${config.mtls.bundleFilename}";
resticPasswordFile = "${config.xdg.configHome}/restic/password.txt";
in
{
imports = with inputs.self.modules.homeManager; [
rebuild
john
mysops
janus-ca
step-ssh-user
mtls
restic
docker
desktop
step-client
mysops
# zed-editor
# myPackage
# myStepClient
];
targets.genericLinux.enable = true;
shell.program = "zsh";
home.username = "${username}";
home.homeDirectory = "/home/${username}";
home.packages = with pkgs; [
nixos-rebuild
(writeShellScriptBin "test-push" ''
mkdir -p /var/tmp/nix-build
chmod 1777 /var/tmp/nix-build
nixos-rebuild switch \
--flake ${flakeDir}#john-pc-ubuntu \
--target-host root@${testTarget}
'')
];
# TODO: make this more restrictive, rather than allowing all unfree packages
nixpkgs.config.allowUnfree = true;
nixpkgs.config.permittedInsecurePackages = [ "openssl-1.1.1w" ];
targets.genericLinux.enable = true;
home.username = "${username}";
home.homeDirectory = "/home/${username}";
home.packages = with pkgs; [
nil # Nix language server
selfPkgs.jsl-zsh
# selfPkgs.my-neovim
selfPkgs.ssh-certs
# selfPkgs.step-bootstrap
# selfPkgs.wg-platform
# self'.packages.myWrappedPackage
(inputs.self.wrappers.test-push.apply {
inherit pkgs flakeDir;
host = testHost;
target = testTarget;
}).wrapper
];
homeManagerFlakeDir = flakeDir;
docker.enable = true;
step-ssh-user = {
enable = true;
principals = ["root" "${username}" "appdaemon"];
provisioner = "admin";
};
ssh = {
certificates.enable = true;
knownHosts = [
"@cert-authority fded:fb16:653e:25da:be24:11ff:fea0:753f ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBNug18oLH0vZxnibXJzMJvTWFPZTnSlhCDDVi+rHhgnIum6ZXQ4SF+VHOOAM5BbzZmMKitNJ5lcrGP15Eur7DzQ="
];
matchSets = {
certs = true;
appdaemon = true;
homelab = true;
dev = true;
tailscale = true;
};
};
@@ -93,28 +88,23 @@ in
"/home/john/john-nas"
];
};
# mtls = {
# enable = true;
# subject = hostname;
# san = [
# "${hostname}"
# "192.168.1.85"
# "spiffe://john-stream.com/ubuntu"
# ];
# lifetime = "1h";
# renew.onCalendar = "*:1/10";
# };
mtls = {
enable = true;
subject = hostname;
san = [
"${hostname}"
"192.168.1.85"
"spiffe://john-stream.com/ubuntu"
];
lifetime = "1h";
renew.onCalendar = "*:1/10";
};
};
flake.homeConfigurations."john@john-pc-ubuntu" = inputs.home-manager.lib.homeManagerConfiguration {
pkgs = import inputs.nixpkgs.outPath {
localSystem.system = "x86_64-linux";
config = {
allowUnfree = true;
permittedInsecurePackages = [ "openssl-1.1.1w" ];
problems.handlers.sublimetext4.broken = "ignore";
};
};
modules = [ inputs.self.modules.homeManager."${hostname}" ];
flake.homeConfigurations."${hostname}" = inputs.home-manager.lib.homeManagerConfiguration {
pkgs = import inputs.nixpkgs { system = "x86_64-linux"; };
modules = with inputs.self.modules; [
homeManager."${hostname}"
];
};
}
-33
View File
@@ -1,33 +0,0 @@
{ self, inputs, ... }: {
flake.wrappers.test-push = inputs.wrappers.lib.wrapModule ({config, lib, wlib, ... }: {
options = {
flakeDir = lib.mkOption {
type = lib.types.str;
};
host = lib.mkOption {
type = lib.types.str;
};
target = lib.mkOption {
type = lib.types.str;
};
sshUser = lib.mkOption {
type = lib.types.str;
default = "root";
};
elevationStrategy = lib.mkOption {
type = lib.types.str;
default = "none";
};
};
config = {
binName = "test-push";
package = config.pkgs.nh;
args = [
"os" "switch" "${config.flakeDir}#${config.host}"
"--target-host" "${config.sshUser}@${config.target}"
"--elevation-strategy" "${config.elevationStrategy}"
];
};
});
}
-123
View File
@@ -1,123 +0,0 @@
{ self, inputs, ... }: {
flake.modules.nixos.omen-nixos = { pkgs, lib, ... }: {
# import any other modules from here
imports = [
self.modules.nixos.omen-nixos-hardware
self.modules.nixos.base
self.modules.nixos.greetd
self.modules.nixos.niri
self.modules.nixos.onepassword
self.modules.nixos.steam
];
# Use the systemd-boot EFI boot loader.
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
# Use latest kernel.
boot.kernelPackages = pkgs.linuxPackages_latest;
networking.hostName = "omen-nixos"; # Define your hostname.
# Configure network proxy if necessary
# networking.proxy.default = "http://user:password@proxy:port/";
# networking.proxy.noProxy = "127.0.0.1,localhost,internal.domain";
# Select internationalisation properties.
# i18n.defaultLocale = "en_US.UTF-8";
# console = {
# font = "Lat2-Terminus16";
# keyMap = "us";
# useXkbConfig = true; # use xkb.options in tty.
# };
# Enable CUPS to print documents.
# services.printing.enable = true;
# Enable sound.
# services.pulseaudio.enable = true;
# OR
# services.pipewire = {
# enable = true;
# pulse.enable = true;
# };
# Enable touchpad support (enabled default in most desktopManager).
# services.libinput.enable = true;
# Define a user account. Don't forget to set a password with passwd.
# users.users.alice = {
# isNormalUser = true;
# extraGroups = [ "wheel" ]; # Enable sudo for the user.
# packages = with pkgs; [
# tree
# ];
# };
# programs.firefox.enable = true;
# List packages installed in system profile.
# You can use https://search.nixos.org/ to find more packages (and options).
environment.systemPackages = with pkgs; [
# vim # Do not forget to add an editor to edit configuration.nix! The Nano editor is also installed by default.
wget
git
micro
nh
];
# Some programs need SUID wrappers, can be configured further or are
# started in user sessions.
# programs.mtr.enable = true;
# programs.gnupg.agent = {
# enable = true;
# enableSSHSupport = true;
# };
# List services that you want to enable:
# Enable the OpenSSH daemon.
services.openssh.enable = true;
# Open ports in the firewall.
# networking.firewall.allowedTCPPorts = [ ... ];
# networking.firewall.allowedUDPPorts = [ ... ];
# Or disable the firewall altogether.
# networking.firewall.enable = false;
# Copy the NixOS configuration file and link it from the resulting system
# (/run/current-system/configuration.nix). This is useful in case you
# accidentally delete configuration.nix.
# system.copySystemConfiguration = true;
# This option defines the first version of NixOS you have installed on this particular machine,
# and is used to maintain compatibility with application data (e.g. databases) created on older NixOS versions.
#
# Most users should NEVER change this value after the initial install, for any reason,
# even if you've upgraded your system to a new NixOS release.
#
# This value does NOT affect the Nixpkgs version your packages and OS are pulled from,
# so changing it will NOT upgrade your system - see https://nixos.org/manual/nixos/stable/#sec-upgrading for how
# to actually do that.
#
# This value being lower than the current NixOS release does NOT mean your system is
# out of date, out of support, or vulnerable.
#
# Do NOT change this value unless you have manually inspected all the changes it would make to your configuration,
# and migrated your data accordingly.
#
# For more information, see `man configuration.nix` or https://nixos.org/manual/nixos/stable/options#opt-system.stateVersion .
system.stateVersion = "26.05"; # Did you read the comment?
# ...
home-manager.users.john.imports = with inputs.self.modules.homeManager; [
desktop
# rebuild
{
my-vscode.enable = true;
}
];
};
}
-8
View File
@@ -1,8 +0,0 @@
{ self, inputs, ... }: {
flake.nixosConfigurations.omen-nixos = inputs.nixpkgs.lib.nixosSystem {
modules = [
self.modules.nixos.omen-nixos
self.modules.nixos.john
];
};
}
-28
View File
@@ -1,28 +0,0 @@
{ self, inputs, ... }: {
flake.modules.nixos.omen-nixos-hardware = { config, lib, pkgs, modulesPath, ... }: {
imports = [
(modulesPath + "/installer/scan/not-detected.nix")
];
boot.initrd.availableKernelModules = [ "nvme" "xhci_pci" "ahci" "usbhid" "usb_storage" "sd_mod" ];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ "kvm-amd" ];
boot.extraModulePackages = [ ];
fileSystems = {
"/" = {
device = "/dev/disk/by-uuid/35f77d1a-346c-4c52-83b2-7d25e2ac9fe1";
fsType = "ext4";
};
"/mnt/shared" = {
device = "/dev/disk/by-uuid/216e8dca-170d-4377-bf1a-69b574e1778c";
fsType = "ext4";
};
};
swapDevices = [ ];
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
};
}
-119
View File
@@ -1,119 +0,0 @@
{ 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";
in
{
flake.nixosConfigurations."${hostname}" = inputs.nixpkgs.lib.nixosSystem {
modules = with inputs.self.modules; [
nixos.lxc
nixos."${username}"
nixos.mysops
nixos.step-ssh-host
nixos.login-text
nixos.docker
nixos.mtls
nixos.janus-ca
nixos.forgejo
nixos.restic-server
# nixos.restic-envoy
({ config, pkgs, ... }: {
networking.hostName = hostname;
time.timeZone = "America/Chicago";
# Removes password for sudo
security.sudo-rs.extraRules = lib.mkAfter [
{
users = [ username ];
commands = [
{
command = "ALL";
options = [ "NOPASSWD" ];
}
];
}
];
users.users."${username}".extraGroups = [ "mtls" ];
mtls = {
enable = true;
certDir = config.janus-ca.certDir;
subject = hostname;
san = [
"${hostname}.john-stream.com"
# "192.168.1.142"
"forgejo.john-stream.com"
"192.168.1.244"
];
lifetime = "12h";
renew.onCalendar = "*:3/15";
renew.reloadUnits = [ "forgejo.service" "restic-rest-server.service" ];
certReaders = [ config.services.forgejo.user "restic" ];
};
forgejo = {
enable = true;
root_url = "https://forgejo.john-stream.com";
https = true;
port = 443;
};
resticServer = {
enable = true;
dataDir = "/mnt/restic";
privateRepos = true;
listenAddress = "0.0.0.0:8000";
tls = {
certFile = config.mtls.certFile;
keyFile = config.mtls.keyFile;
};
};
loginText.extraServiceStatus = {
Docker = "docker";
"mTLS Renewal" = "mtls-renew.timer";
Forgejo = "forgejo.service";
"Forgejo Backup" = "forgejo-dump.timer";
"Restic REST Server" = "restic-rest-server.service";
};
step-ssh-host.hostname = hostname;
# This provides the secrets at install time
sops.defaultSopsFile = ./secrets.yaml;
# programs.zsh.enable = true;
home-manager.users."${username}".imports = [ inputs.self.modules.homeManager.soteria ];
environment.systemPackages = [
inputs.self.packages.${pkgs.stdenv.hostPlatform.system}.my-neovim
inputs.self.packages.${pkgs.stdenv.hostPlatform.system}.jsl-zsh
];
})
];
};
flake.modules.homeManager.soteria = { config, pkgs, lib, ... }: {
imports = [
inputs.self.modules.homeManager.rebuild
inputs.self.modules.homeManager.mysops
({ config, pkgs, lib, ... }: {
homeManagerFlakeDir = "${config.xdg.configHome}/home-manager";
docker.enable = true;
# This will provide the edit-secrets script targeting this file
mysops.hostSecretFile = "${config.homeManagerFlakeDir}/modules/hosts/soteria/secrets.yaml";
})
];
};
flake.homeConfigurations.soteria = withSystem "x86_64-linux" (ctx@{ config, inputs', ...}:
inputs.home-manager.lib.homeManagerConfiguration {
pkgs = inputs'.nixpkgs.legacyPackages;
modules = [
inputs.self.modules.homeManager."${username}"
inputs.self.modules.homeManager.soteria
];
});
}
+14 -32
View File
@@ -1,44 +1,26 @@
janus:
admin_jwk: ENC[AES256_GCM,data:2XcN5X77wTQ+OUXa4C9xAErGvrwJKseHjCcgoj6jt+c=,iv:9x+M1wM0dYND1JYkJjM4N8pSOok2OF+P9vmm9NCumTI=,tag:dTCfh+KB1iRJBVoNsGqvuw==,type:str]
forgejo:
#ENC[AES256_GCM,data:/wtm0uXbiWFoGNWtlTzVuNxBR7CPm2FMB98t3AxSj5V5rltLvzF9BjgWoJCiX3ltzmU=,iv:xaZXbUIGJHxPrLRQzEQI7hgRgc0y061jIhoE3zlcMaA=,tag:fQGheCIdBKm6wE+vtj7g6A==,type:comment]
secret_key: ENC[AES256_GCM,data:/jcyeDcsryLqu9Q3VnNaENb71/Tl5JUr0zDzxt8L5UCtnJ/YHA6MKItrQ9ZHFv1XROtnSfZl9D5kKomeM8EVqA==,iv:HxMKAMVQ08gkq6SWENj0/d8i9PhcgPCp5eqbztj9bSg=,tag:nO2dFWT/vfgGc/9JIDZrGw==,type:str]
#ENC[AES256_GCM,data:TADqVsZwXpH/zfrXNclavH8Sv++jX081UjK/Mkys5x52gU5fU0ikk478EmwzeergsHpmxxon,iv:v9HsIsud+eiko7F0u622MBrTVDsIxlpqru0Viifvqow=,tag:FLgNTW0EcGhHT6iI9I6Bsg==,type:comment]
internal_token: ENC[AES256_GCM,data:7eZM+misgHZz1p6HH9VzCAPIIFjp2vT0kyc9B1KllE+x2URyrgs5IR9SZ+T5vNdMdnlNzF25pUhKzjbak8tVN554rYWp06SczzqhLyaoHKs8iGBohMqfJkoZxxIVSB7g7GC+/SAsb719,iv:XbBXqXh/PqjEcAj0qsajR7Ij56KGT/9Spdp5T549p9I=,tag:o4ONucOkIE2VJ6VjOlPb6w==,type:str]
#ENC[AES256_GCM,data:ZqwgnKjaolJtjcy287fnDOkb/oSLnBpfWfsTeVPwbIE8YLRSoPP4gbCnHJBLq+TJNNI=,iv:zTvw4ZS6C1ifUwOijNLuTfUQ3JM+5gj1X2f/s8MwWXc=,tag:Y1yKlL+jIRHVBulGlSErog==,type:comment]
jwt_secret: ENC[AES256_GCM,data:e59MlATOorsTIQjtTUKfX5Yo3CVsbbfuKczp1gh1m2D1kkZK3ORFztYpjg==,iv:JH3PVUmXToiThEKDkDJ8MGVMAPlIEgPSWhru+9WgNjk=,tag:FfDpaCPejpw6kGDkxJwDWw==,type:str]
lfs_jwt_secret: ENC[AES256_GCM,data:xi9PEKFUGRyc3YOg3JM3KrrENi9xsbeBjiz4R16SK5WDafoGFLazN6KRJQ==,iv:1IhPyQDwA8tZ22pfZJiU8TRTCLCHC/HAnKdmSGDfvcM=,tag:rLdREVSKBm67rt8ayN16Vw==,type:str]
restic_password: ENC[AES256_GCM,data:u7QOZXJkxVG4J75K5nphb2uJGdz6jbWuVSsKKu+41fshp7cVoRijtr/Cs02LjVse,iv:bt1W2FeBTG6ypBFYzMPXPIkYTSn0uHURY2ui6MRgYY8=,tag:DObAMws/zQcM+UKUe9EECA==,type:str]
admin_password: ENC[AES256_GCM,data:4pnSq0f1iTNFWn/Qcw+J7LWIXXd/j5v3WwFSzXfqgKA=,iv:/usSHYST8zv7AMvDNuW/fFLL+40IrderjL6bUWzBNd4=,tag:V/q1+SjIcYsZ0+PC/Q7c1A==,type:str]
sops:
age:
- recipient: age1ykcs39e62pz3xu6cedg8ea685kv5d5qsrhgkndygzm8rx30xd5ys5t3qxt
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA5QThHOFdVQ0F6ZDE3QXdL
QVF0WW5yRkNtNXIyRm1LeEhwY2dRRWo0WENZCmZoWmlXZDh4ZGlUR2JYOUxuaVAx
WjhXVHBMNWdrdkgvTENJR2RpWldkNTAKLS0tIEF1bS9JSnFTbytBa1U5RjVzWkd3
S0pKVVI2RFN5a1BMYXBEY3VOUUM5QTAKNarYZm9DKRQhosSJBn9yryFxDkmFTV/o
i8b/tZ1ZybjEXX+X1EsgylM5u8iKkbEyUzqKO0E0gpg4qXSsJaMMYQ==
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB0V1JsVDhnMHpaNHRQRCtw
V3JMQm1SREZSYUpaYmZNQ1FEVmN0VFZRSlQ0Ck9wQlpqZFBrRUJENWVBbTd5cVVo
ZlhYZnhGamk1ZlQ0N001ZWcrQ1Evb3cKLS0tIHpuTWhRTU1QeWFRUytiU21CMW94
eFc0ankvcWhqK3Q4MjRCVC9nTlJteU0K1tJvYM2M1XmlsCTpadHyf6EGE2Lg+XBL
TGTjMPSWqClWYB9HFZ4nCurEK/JidBanmGkc0Y9eFz9XYKl7rtyXUw==
-----END AGE ENCRYPTED FILE-----
- recipient: age1h0prahyukq4l564yqwgcpg3g6gdrjflk0suklussjjrjstxd9uesws8633
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBhdEN2M1lkMFoyQzg3bCsz
cVB0NUlkV1JPRjQ1TW5RSVFHUjdNMERoRXlzCk9tc25iMG00TkZjWjk2S2cyVE1J
cUFITDh2SkZGN0lpeWJVcWM3V3JrTTQKLS0tIENOWVBzTFYrdHRsMERwV0RKS01j
QVI5TVl4L0dwOWpoQ1I2THZ6cWovWGMKIo1x1ZbdTyr/dNlPhvuomfk2MoPLsHyU
N3CP3Scu3aZ7vqVua7uwtv4xQqyQI/yOnFjwxrVYPJ+N5Y/b4wsC6A==
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBNRzFlWTNxSGpaNU85QTFj
SnlRZkpZQTZQVjNqWFhLMHhKVm5ZVXB4Rms0CmZQclFzYkpWLzlqT2xNUTJMd2dJ
Wm5PQXNabjRwV1hVRXFGVmxteGk2emMKLS0tIG5kcm5KamxNZmpKVGt5bUo1dVg5
dFN2RGdqM29mM3hadXpBakY4QThxZnMKI9RbfXJHIHvYHy/2corfwDq+OHRPrmkA
tWLH/KWqwGt0hvc5j8bUfRECgjdXmbC9kpAgDs8PhJF+X1ijVFrIYg==
-----END AGE ENCRYPTED FILE-----
- recipient: age1gvplss0ddmyf6vpjy363wu3n057vhm0j6n7tc94cxd8kadapypws5mtaj0
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBGSGQvL2ZYM2dtV1JWaDR1
RjJnN0YwWFM4d1IwTElqUnZQQkVDTmdydUJjCjdCUWxCVDNXUHVLd2ZHcDA5L0Mw
cEVxVE84L2xmQnRoYmsxVEdMSEZjdGcKLS0tIHViRVJyTWt4Q3JrMWtYaS9QdHlS
Yjd0MUcxcExvWVpCOUR3MkdZdGQyWUkKnru0Y2A98+0Mps7EtVK7ct3vPqIGveUt
E5fzpcKvdefzObrx7BPTwJ19t2fZg/dSi7HKwx3vmKZSzyQaqJOzsg==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2026-04-04T23:18:43Z"
mac: ENC[AES256_GCM,data:qBgeli5lHb4pyA8nAADBuRBAaq8VbAIsFI37OZtgnbnoHW2crxo3YC+EknaIYnZpZ48kwVhQS5lGRjI6JsWWhTH3+LVAhTmS2Qj/pZTD/JDLK6XJGXS4U9nB7m9aGYyW8gFCy9/DfoJWGsS//+ZmUikKPfd5kMZgh1zGoYCIGug=,iv:h+2fA+bO2SMCNrEslP36x3BPRaIy25cU/DNX8CYSC6A=,tag:RvVyUZ4ONRaKaqGiT31eUQ==,type:str]
lastmodified: "2026-03-23T22:27:45Z"
mac: ENC[AES256_GCM,data:tQ8EMXWqw7wd/QZqUPn/sAczk0G4jSUR96AF83cmJGYuoZkMkOzsMFt448IkWxNWOJHPmIc0vs+c3ngQxHyx6Uf9jVWzMkvfdzMYj82QuLsmXQ9e4/IAE9h+52uagbwgoOJwPCF+AQetSHez/jWPPIQPN5DLfz0edg4xYReX7DA=,iv:GsvotFkehIz5L4I9j0oQ89oM5XjZQAr6vrVd79tqFes=,tag:wLi4sXz8JqvaoUaYjEj/vA==,type:str]
unencrypted_suffix: _unencrypted
version: 3.12.2
version: 3.12.1
+78
View File
@@ -0,0 +1,78 @@
{ inputs, ... }:
let
username = "john";
hostname = "soteria";
in
{
flake.nixosConfigurations."${hostname}" = inputs.nixpkgs.lib.nixosSystem {
modules = with inputs.self.modules; [
nixos.lxc
nixos.mysops
nixos.step-ssh-host
inputs.home-manager.nixosModules.home-manager
nixos."${username}"
nixos.zsh
nixos.login-text
# nixos.mtls
# nixos.restic-server
# nixos.restic-envoy
{
networking.hostName = hostname;
nix.settings.build-dir = "/var/tmp/nix-build";
systemd.tmpfiles.rules = [
"d /var/tmp/nix-build 1777 root root -"
];
step-ssh-host = {
hostname = hostname;
};
home-manager.users."${username}" = {
imports = with inputs.self.modules; [
homeManager"${hostname}"
];
};
}
];
};
flake.modules.homeManager."${hostname}" = { config, lib, pkgs, ... }: {
imports = with inputs.self.modules; [
homeManager.rebuild
homeManager.mysops
homeManager.janus-ca
homeManager.mtls
homeManager.docker
];
homeManagerFlakeDir = "${config.xdg.configHome}/home-manager";
home.username = "${username}";
home.homeDirectory = "/home/${username}";
shell.program = "zsh";
docker.enable = true;
# This will provide the edit-secrets script targeting this file
mysops.hostSecretFile = "${config.xdg.configHome}/home-manager/modules/hosts/soteria/secrets.yaml";
mtls = {
enable = true;
subject = hostname;
san = [
"${hostname}.john-stream.com"
"192.168.1.142"
];
lifetime = "1h";
renew.onCalendar = "*:3/15";
renew.postCommands = [
"${lib.getExe pkgs.docker} restart envoy"
];
};
};
flake.homeConfigurations."${hostname}" = inputs.home-manager.lib.homeManagerConfiguration {
pkgs = import inputs.nixpkgs { system = "x86_64-linux"; };
modules = with inputs.self.modules; [
homeManager."${username}"
homeManager."${hostname}"
];
};
}
+3 -3
View File
@@ -11,14 +11,14 @@ in
nixos.step-ssh-host
inputs.home-manager.nixosModules.home-manager
nixos."${username}"
nixos.zsh
nixos.docker
{
step-ssh-host.hostname = hostname;
sops.defaultSopsFile = ../../keys/secrets.yaml;
home-manager.users."${username}" = {
imports = with inputs.self.modules.homeManager; [
mysops
sops
];
shell.program = "zsh";
docker.enable = true;
ssh.matchSets = {
certs = true;
+25 -128
View File
@@ -1,148 +1,45 @@
{ self, inputs, ... }:
{ inputs, ... }:
{
flake.modules.nixos.rebuild =
{ config, pkgs, lib, ... }:
let
flakeDir = config.rebuild.flakeDir;
echoCmd = lib.getExe' pkgs.coreutils "echo";
hostnameCmd = "$(${lib.getExe pkgs.hostname} -s)";
nfs = with pkgs; writeShellApplication {
name = "nfs";
runtimeInputs = [ coreutils hostname nh ];
text = ''
HOSTNAME=$(hostname -s)
echo "Switching to the $HOSTNAME nixos profile"
nh os switch "$@" "${flakeDir}#$HOSTNAME"
'';
};
nfsu = with pkgs; writeShellApplication {
name = "nfsu";
runtimeInputs = [ nfs pkgs.git ];
text = ''nfs --refresh "$@"'';
};
in
{
options.rebuild = {
flakeDir = lib.mkOption {
description = "Path to the flake directory.";
type = lib.types.str;
default = "/etc/nixos";
};
};
config = {
environment.systemPackages = with pkgs; [ nfs nfsu ];
};
};
flake.modules.homeManager.rebuild =
{ config, pkgs, lib, ... }:
let
flakeDir = config.homeManagerFlakeDir;
flake-parts-check = with pkgs; writeShellApplication {
name = "flake-parts-check";
runtimeInputs = [ nix ];
text = ''
cd ${flakeDir}
nix run "${flakeDir}#write-flake"
nix flake check
'';
};
test-build = with pkgs; writeShellApplication {
name = "test-build";
runtimeInputs = [ coreutils nix hostname ];
text = ''
if [ -z "$1" ]; then
HOSTNAME=$(hostname -s)
else
HOSTNAME="$1"
fi
echo "Testing the evaulation of the nixos config for $HOSTNAME"
nix eval "${flakeDir}#nixosConfigurations.$HOSTNAME.config.system.build.toplevel.drvPath"
'';
};
in
{ pkgs, lib, config, ... }:
{
options = {
homeManagerFlakeDir = lib.mkOption {
description = "Path to the home-manager flake directory.";
type = lib.types.str;
default = "${config.xdg.configHome}/home-manager";
description = "Path to the home-manager flake directory.";
};
};
config = {
config = let
nixBin = lib.getExe pkgs.nix;
flakeDir = config.homeManagerFlakeDir;
in
{
home.activation.printFlakeDir = lib.hm.dag.entryAfter ["writeBoundary"] ''
run echo "Home Manager flake directory: ${flakeDir}"
'';
home.packages = with pkgs; [
home-manager
(symlinkJoin {
name = "build-tools";
paths = [
flake-parts-check
test-build
(inputs.self.wrappers.home-switch.apply {
inherit pkgs flakeDir;
}).wrapper
(inputs.self.wrappers.home-switch.apply {
binName = lib.mkForce "nhmu";
inherit pkgs flakeDir;
extraOptions = [ "--update" ];
}).wrapper
(inputs.wrappers.lib.wrapPackage {
binName = "cleanup";
inherit pkgs;
package = nh;
args = [ "clean" "user" "--keep-since" "3days" ];
})
];
})
(writeShellScriptBin "flake-parts-test" ''
echo "Test ${flakeDir}"
'')
(writeShellScriptBin "flake-parts-check" ''
cd ${flakeDir}
${nixBin} run ".#write-flake"
${nixBin} flake check
'')
(writeShellScriptBin "nhms" ''
HOSTNAME=$(hostname -s)
echo "Switching to the $HOSTNAME profile"
${lib.getExe home-manager} switch --impure --flake ${flakeDir}#$HOSTNAME
'')
(writeShellScriptBin "nhmu" ''
${nixBin} flake update --flake ${flakeDir}
nhms
'')
];
};
};
flake.wrappers.home-switch = inputs.wrappers.lib.wrapModule ({config, lib, wlib, ... }: {
options = {
flakeDir = lib.mkOption {
type = lib.types.str;
};
user = lib.mkOption {
type = lib.types.str;
default = "$(whoami)";
};
hostname = lib.mkOption {
type = lib.types.str;
default = "$(hostname -s)";
};
configuration = lib.mkOption {
type = lib.types.str;
default = "${config.user}@${config.hostname}";
};
extraOptions = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
};
};
config = {
binName = "nhms";
extraPackages = with config.pkgs; [ coreutils hostname nh ];
preHook = ''
CONFIG=${config.configuration}
echo "Switching to $CONFIG from ${config.flakeDir}"
'';
package = config.pkgs.nh;
args = [
"home" "switch"
"--configuration" "${config.configuration}"
"$@"
"${config.flakeDir}"
] ++ config.extraOptions;
};
});
}
+23 -34
View File
@@ -1,50 +1,39 @@
# Lifted from:
# https://github.com/Doc-Steve/dendritic-design-with-flake-parts/blob/69edacdb5a4a6ca71d649bb8eb62cf8c630c8627/modules/users/bob%20%5BNDn%5D/bob.nix#L8
{ self, inputs, ... }:
{ self, ... }:
{
config.flake.factory.user = {
username,
isAdmin ? false,
noPassword ? false,
# homeImports ? [ ],
# homePackages ? [ ],
}: {
nixos."${username}" = { config, lib, pkgs, ... }: {
imports = [
inputs.home-manager.nixosModules.home-manager
];
environment.shells = [
"${lib.getExe pkgs.zsh}"
"${lib.getExe inputs.self.packages.${pkgs.stdenv.hostPlatform.system}.jsl-zsh}"
];
users.groups."${username}" = {};
config.flake.factory.user = username: isAdmin: {
nixos."${username}" = { lib, pkgs, ... }: {
users.users."${username}" = {
isNormalUser = true;
group = username;
home = "/home/${username}";
shell = pkgs.zsh;
extraGroups = [ "input" "networkmanager" "video" "render" ]
++ lib.optional isAdmin "wheel"
++ lib.optional config.virtualisation.docker.enable "docker"
++ lib.optional (isAdmin && config.services.forgejo.enable) config.services.forgejo.group
++ lib.optional (isAdmin && config.services.postgresql.enable) "postgres";
extraGroups = [
"input"
"networkmanager"
] ++ lib.optionals isAdmin [
"docker"
"wheel"
];
};
programs.zsh.enable = true;
# Removes password for sudo
security.sudo-rs = lib.mkIf isAdmin {
enable = true;
extraRules = [{
users = [ "${username}" ];
commands = [{
command = "ALL";
options = [ "NOPASSWD" ];
}];
}];
};
security.sudo-rs.enable = lib.mkIf isAdmin true;
home-manager.useGlobalPkgs = true;
# https://github.com/Doc-Steve/dendritic-design-with-flake-parts/wiki/Dendritic_Aspects#multi-context-aspect
home-manager.users."${username}" = {
imports = [ self.modules.homeManager."${username}" ];
home.username = "${username}";
home.homeDirectory = "/home/${username}";
home.packages = with pkgs; [
# fzf zoxide starship
imports = [
self.modules.homeManager."${username}"
];
};
};
-21
View File
@@ -1,21 +0,0 @@
{ self, inputs, ... }: {
config.flake-file.inputs = {
wrapper-modules = {
url = "github:BirdeeHub/nix-wrapper-modules";
inputs.nixpkgs.follows = "nixpkgs";
};
wrappers = {
url = "github:lassulus/wrappers";
inputs.nixpkgs.follows = "nixpkgs";
};
};
options = {
# This is what allows wrappers to be defined in flake.wrappers.<wrapper-name> throughout different flake-parts modules
flake = inputs.flake-parts.lib.mkSubmoduleOptions {
wrappers = inputs.nixpkgs.lib.mkOption {
default = {};
};
};
};
}
+35 -47
View File
@@ -1,56 +1,44 @@
{ inputs, ... }: {
flake.modules.nixos.login-text = { config, lib, ... }:
let
defaultServiceStatus = {
SSH = "sshd.socket";
"SSH Cert Renewal" = "step-ssh-host-renew.timer";
};
in {
options.loginText.extraServiceStatus = lib.mkOption {
type = lib.types.attrsOf lib.types.str;
default = { };
description = "Additional rust-motd service status entries keyed by display name.";
};
flake.modules.nixos.login-text = { config, ... }: {
programs.rust-motd = {
enable = true;
refreshInterval = "*:0/5";
order = [
"global"
"last_login"
"service_status"
# "uptime"
"memory"
"filesystems"
];
settings = {
global = {
time_format = "%Y-%m-%d %H:%M:%S %Z";
};
config = {
programs.rust-motd = {
enable = true;
refreshInterval = "*:0/5";
order = [
"global"
"last_login"
"service_status"
# "uptime"
"memory"
"filesystems"
];
settings = {
global = {
time_format = "%Y-%m-%d %H:%M:%S %Z";
};
last_login = {
john = 3;
root = 3;
};
last_login = {
john = 3;
root = 3;
};
service_status = {
Docker = "docker";
SSH = "sshd.socket";
"SSH Cert Renewal" = "step-ssh-host-renew.timer";
"mTLS Renewal" = "mtls-renew.timer";
};
service_status = lib.mkMerge [
defaultServiceStatus
config.loginText.extraServiceStatus
];
# This calculation is wrong for LXCs
# uptime = {
# prefix = "Uptime";
# };
# This calculation is wrong for LXCs
# uptime = {
# prefix = "Uptime";
# };
memory = {
swap_pos = "beside";
};
memory = {
swap_pos = "beside";
};
filesystems = {
root = "/";
};
filesystems = {
root = "/";
};
};
};
+2 -1
View File
@@ -1,9 +1,10 @@
{
flake.modules.homeManager.bash = { pkgs, lib, config, ... }:
{
programs.bash = {
programs.bash = lib.mkIf (config.shell.program == "bash") {
enable = true;
enableCompletion = true;
package = pkgs.bash;
};
};
}
+5 -23
View File
@@ -1,32 +1,14 @@
{ self, inputs, ... }:
{ inputs, pkgs, lib, ... }:
{
flake.modules.homeManager.eza = { config, pkgs, lib, ... }: {
flake.modules.homeManager.eza = { pkgs, lib, ... }: {
programs.eza = {
enable = true;
package = pkgs.eza;
enableBashIntegration = true;
enableZshIntegration = true;
package = inputs.self.packages.${pkgs.stdenv.hostPlatform.system}.my-eza;
};
};
perSystem = { system, pkgs, ... }: {
packages.my-eza = inputs.wrappers.lib.wrapPackage {
inherit pkgs;
package = pkgs.eza;
args = [
"--all"
"--long"
"--group-directories-first"
"--icons=auto"
"--color=auto"
"--sort=type"
"--dereference"
"--octal-permissions"
"--smart-group"
"--no-time"
"--git"
"$@"
];
home.shellAliases = {
ls = "${lib.getExe pkgs.eza} -lgos type --no-time --follow-symlinks";
};
};
}
+10 -13
View File
@@ -1,18 +1,15 @@
{
flake.modules.homeManager.files = { pkgs, lib, ... }:
flake.modules.homeManager.files = { pkgs, ... }:
{
programs.lf = {
enable = true;
cmdKeybindings = {
"D" = "delete";
};
settings = {
preview = true;
hidden = true;
drawbox = true;
icons = true;
ignorecase = true;
};
programs.lf.enable = true;
programs.lf.cmdKeybindings = {
"D" = "delete";
};
home.packages = with pkgs; [
gdu
lf
# TODO: find a CLI file editor that's not insane
];
};
}
+4 -36
View File
@@ -8,26 +8,11 @@
};
flake.modules.homeManager.ghostty = { config, pkgs, lib, ... }:
let
ghosttyX11 = pkgs.symlinkJoin {
name = "ghostty-x11";
paths = [ (config.lib.nixGL.wrap pkgs.ghostty) ];
nativeBuildInputs = [ pkgs.makeWrapper ];
meta.mainProgram = "ghostty";
postBuild = ''
wrapProgram $out/bin/ghostty --set GDK_BACKEND x11
'';
};
in
{
home.sessionVariables = {
TERMINAL = "ghostty";
};
home.packages = with pkgs; [
inputs.self.packages.${system}.ghosttyGPUCheck
];
targets.genericLinux.nixGL = {
packages = inputs.nixgl.packages.${pkgs.stdenv.hostPlatform.system};
defaultWrapper = "mesa";
@@ -37,7 +22,7 @@
programs.ghostty = {
enable = true;
enableZshIntegration = true;
package = ghosttyX11;
package = config.lib.nixGL.wrap pkgs.ghostty;
settings = {
command = "TERM=xterm-256color ${lib.getExe pkgs.zsh}";
font-size = 12;
@@ -47,7 +32,6 @@
shell-integration = "zsh";
shell-integration-features = [ "no-title" "sudo" ];
gtk-single-instance = true;
mouse-scroll-multiplier = 0.375;
window-position-x = 25;
window-position-y = 25;
@@ -74,15 +58,11 @@
};
# https://github.com/ghostty-org/ghostty/discussions/3763#discussioncomment-11699970
xdg.desktopEntries."com.mitchellh.ghostty" =
let
ghosttyCmd = lib.getExe ghosttyX11;
in
{
xdg.desktopEntries."com.mitchellh.ghostty" = {
name = "Ghostty";
type = "Application";
comment = "A terminal emulator";
exec = ghosttyCmd;
exec = "nixGLMesa ghostty";
icon = "com.mitchellh.ghostty";
terminal = false;
startupNotify = true;
@@ -99,21 +79,9 @@
actions = {
new-window = {
name = "New Window";
exec = ghosttyCmd;
exec = "nixGLMesa ghostty";
};
};
};
};
perSystem = { system, pkgs, lib, ... }: {
packages.ghosttyGPUCheck = pkgs.writeShellApplication {
name = "ghostty-gpu-check";
runtimeInputs = with pkgs; [
bash
gnugrep
procps # for pgrep, pidof, etc.
];
text = builtins.readFile ../../scripts/ghostty-gpu-check.sh;
};
};
}
+3 -8
View File
@@ -1,8 +1,6 @@
{ self, inputs, ... }: {
flake.modules.homeManager.git = { config, pkgs, lib, ... }: {
home.packages = with pkgs; [
git-credential-oauth
];
{
flake.modules.homeManager.git = { config, lib, ... }:
{
programs.git = {
enable = true;
settings = {
@@ -16,8 +14,5 @@
enableBashIntegration = true;
enableZshIntegration = true;
};
home.shellAliases = {
"lzg" = "lazygit";
};
};
}
-183
View File
@@ -1,183 +0,0 @@
{ self, inputs, ... }: {
flake-file.inputs = {
nvf = {
url = "github:notashelf/nvf";
inputs.nixpkgs.follows = "nixpkgs";
};
};
perSystem = { system, pkgs, config, ... }:
let
commonNeovimModule = {
config.vim = {
options = {
number = true;
relativenumber = true;
expandtab = true;
shiftwidth = 4;
tabstop = 4;
softtabstop = 4;
wrap = true;
linebreak = true;
};
syntaxHighlighting = true;
theme.enable = true;
theme.name = "catppuccin";
theme.style = "mocha";
git.enable = true;
# git.neogit.enable = true;
# https://github.com/akinsho/toggleterm.nvim
terminal.toggleterm = {
enable = true;
lazygit = {
enable = true;
package = pkgs.lazygit;
direction = "float";
mappings.open = "<C-g>";
};
};
languages = {
enableTreesitter = true;
enableFormat = true;
nix.enable = true;
yaml.enable = true;
toml.enable = true;
};
};
};
telescopeModule = {
config.vim = {
telescope = {
enable = true;
extensions = [
{
name = "fzf";
packages = [ pkgs.vimPlugins.telescope-fzf-native-nvim ];
setup = {
fzf.fuzzy = true;
};
}
];
};
keymaps = [
{
desc = "Key Maps [Telescope]";
key = "<leader>fkm";
mode = "n";
silent = false;
action = "<cmd>:Telescope keymaps<CR>";
}
{
desc = "Toggle Filesystem Tree [NeoTree]";
key = "<C-b>";
mode = [ "n" "v" "t" ];
silent = false;
action = "<cmd>:Neotree toggle filesystem left action=show<CR>";
}
];
};
};
keymapsModule = {
config.vim = {
keymaps = [
{
desc = "Edit key mappings";
key = "<leader>ekm";
mode = [ "n" ];
silent = false;
action = "<cmd>:edit +/keymaps /home/john/.config/home-manager/jsl-dendritic/modules/programs/neovim.nix<CR>";
}
{
desc = "Home Manager Switch";
key = "<leader>nhms";
mode = [ "n" ];
silent = false;
action = "<cmd>:TermExec cmd='clear && nhms && exit' name='Nix Home Manager Switch' direction=float<CR>";
}
{
key = "<C-`>";
mode = [ "n" "v" "t" ];
silent = false;
action = "<cmd>:ToggleTerm<CR>";
}
];
};
};
in {
packages.my-neovim = ((inputs.nvf.lib.neovimConfiguration {
inherit pkgs;
modules = [
commonNeovimModule
telescopeModule
keymapsModule
{
# https://nvf.notashelf.dev/search.html
config.vim = {
utility.nix-develop.enable = true;
utility.oil-nvim.enable = true;
utility.oil-nvim.gitStatus.enable = true;
filetree.neo-tree = {
enable = true;
};
# Enable Treesitter
treesitter = {
enable = true;
grammars = with pkgs.vimPlugins.nvim-treesitter-parsers; [ python ];
};
lsp.enable = true;
languages = {
markdown = {
enable = true;
extensions = {
# render-markdown-nvim.enable = true;
markview-nvim.enable = true;
};
};
bash.enable = true;
css.enable = true;
yaml.enable = true;
toml.enable = true;
python = {
enable = true;
dap.enable = true;
format.type = [ "ruff" ];
};
};
};
}
];
}).neovim).overrideAttrs (old: {
pname = "my-neovim";
version = "1.0.0";
});
packages.neovim-min = ((inputs.nvf.lib.neovimConfiguration {
inherit pkgs;
modules = [
commonNeovimModule
telescopeModule
keymapsModule
];
}).neovim).overrideAttrs (old: {
pname = "neovim-min";
version = "1.0.0";
});
};
}
+33
View File
@@ -0,0 +1,33 @@
{ self, inputs, ... }: {
flake-file.inputs = {
wrapper-modules.url = "github:BirdeeHub/nix-wrapper-modules";
wrapper-modules.follows = "nixpkgs";
};
flake.modules.nixos.niri = { pkgs, lib, ... }: {
programs.niri = {
enable = true;
package = self.packages.${pkgs.stdenv.hostPlatform.system}.myNiri;
};
};
perSystem = { pkgs, lib, self', ... }: {
packages.myNiri = inputs.wrapper-modules.wrappers.niri.wrap {
inherit pkgs;
env.RUST_BACKTRACE = "full";
settings = {
spawn-at-startup = [
"${lib.getExe self'.packages.myNoctalia}"
];
xwayland-satellite.path = lib.getExe pkgs.xwayland-satellite;
input.keyboard.xkb.layout = "us,ua";
layout.gaps = 5;
binds = {
"Mod+Return".spawn-sh = lib.getExe pkgs.ghostty;
"Mod+Q".close-window = null;
"Mod+S".spawn-sh = "${lib.getExe self'.packages.myNoctalia} ipc call launcher toggle";
};
};
};
};
}
+8
View File
@@ -0,0 +1,8 @@
{ self, inputs, ... }: {
perSystem = { pkgs, ... }: {
packages.myNoctalia = inputs.wrapper-modules.wrappers.noctalia-shell.wrap {
inherit pkgs;
# settings = (builtins.fromJSON (builtins.readFile ./noctalia.json)).settings;
};
};
}
+16 -11
View File
@@ -1,20 +1,25 @@
{ self, inputs, ... }: {
flake.modules.nixos.onepassword = { config, ... }: {
programs._1password.enable = true;
programs._1password-gui = {
{
flake.modules.homeManager.onepassword = { config, pkgs, lib, ... }: {
programs._1password-shell-plugins = {
# enable 1Password shell plugins for bash, zsh, and fish shell
enable = true;
# Certain features, including CLI integration and system authentication support,
# require enabling PolKit integration on some desktop environments (e.g. Plasma).
polkitPolicyOwners = [ "john" ];
# TODO this should not be a hardcoded username
# the specified packages as well as 1Password CLI will be
# automatically installed and configured to use shell plugins
# https://developer.1password.com/docs/cli/shell-plugins
plugins = with pkgs; [ gh ];
};
};
flake.modules.homeManager.onepassword = { config, ... }: {
home.file.".config/1Password/ssh/agent.toml".text = ''
# https://developer.1password.com/docs/ssh/agent/config
[[ssh-keys]]
vault = "Private"
'';
programs.ssh = {
enable = true;
extraConfig = ''
Host *
IdentityAgent ${config.home.homeDirectory}/.1password/agent.sock
'';
};
};
}
}
+37
View File
@@ -0,0 +1,37 @@
# This module provides all the shell options
{ inputs, lib, ... }:
{
flake.modules.homeManager.shell-tools = { config, pkgs, ... }: {
options.shell.program = lib.mkOption {
type = lib.types.enum [ "bash" "zsh" ];
default = "zsh";
description = "Which interactive shell configuration to enable.";
};
imports = with inputs.self.modules.homeManager; [
bash
zsh
# Tools
eza
files
];
config = {
home.shell.enableShellIntegration = true;
programs.zsh.enable = lib.mkForce (config.shell.program == "zsh");
home.packages = with pkgs; [
wget
curl
busybox
gnugrep
dig
btop
uv
xclip
jq
ripgrep
];
};
};
}
+64 -73
View File
@@ -1,4 +1,4 @@
{ self, inputs, ... }:
{ inputs, ... }:
let
inputs' = inputs; # save a reference before it's shadowed
in
@@ -14,84 +14,75 @@ in
imports = [ inputs.sops-nix.nixosModules.sops ];
};
flake.modules.homeManager.mysops =
{ config, pkgs, lib, ... }:
let
cfg = config.mysops;
in
{
imports = [
# This import makes the sops config attribute available below
inputs'.sops-nix.homeManagerModules.sops
];
# Define the homeModules that are used by flake-parts
# https://flake.parts/options/home-manager.html#opt-flake.modules.homeManager
flake.modules.homeManager.mysops = { inputs, config, pkgs, lib, ... }:
let
cfg = config.mysops;
sopsBin = lib.getExe pkgs.sops;
sopsConfigPath = ../../.sops.yaml;
sopsSecretsPath = ../../keys/secrets.yaml;
options.mysops = {
hostSecretFile = lib.mkOption {
description = "Path to the secrets file for this host. Used to create the edit-secrets script";
type = lib.types.nullOr lib.types.str;
default = null;
};
};
editScript = lib.optional (cfg.hostSecretFile != null) (pkgs.writeShellScriptBin "edit-secrets" ''
${sopsBin} --config ${sopsConfigPath} ${cfg.hostSecretFile}
'');
in
{
imports = [
# This import makes the sops config attribute available below
inputs'.sops-nix.homeManagerModules.sops
];
config =
let
my-sops = (inputs.self.wrappers.mySops.apply {
inherit pkgs;
sshKey = config.ssh.identityFile;
}).wrapper;
in
{
# Option definitions for the sops home-manager module:
# https://github.com/Mic92/sops-nix/blob/master/modules/home-manager/sops.nix
sops = {
defaultSopsFile = ../../keys/secrets.yaml;
defaultSopsFormat = "yaml";
age.sshKeyPaths = [ "${config.ssh.identityFile}" ];
};
home.packages = with pkgs; [
my-sops
(inputs.wrappers.lib.wrapPackage {
binName = "ls-secrets";
inherit pkgs;
package = inputs.self.packages.${pkgs.stdenv.hostPlatform.system}.my-eza;
args = [
"-T" "--follow-symlinks"
"${config.xdg.configHome}/sops-nix/secrets"
];
})
]
++ lib.optional (cfg.hostSecretFile != null) (inputs.wrappers.lib.wrapPackage {
binName = "edit-secrets";
inherit pkgs;
package = my-sops;
args = [ cfg.hostSecretFile ];
});
};
};
flake.wrappers.mySops = inputs.wrappers.lib.wrapModule ({config, lib, wlib, ... }: {
options = {
sshKey = lib.mkOption {
options.mysops = {
ageKeyFile = lib.mkOption {
description = "Default location for the age key";
type = lib.types.str;
description = "String path to the SSH key to use for creating an AGE key at runtime";
default = "${config.xdg.configHome}/sops/age/keys.txt";
};
hostSecretFile = lib.mkOption {
description = "Path to the secrets file for this host";
type = lib.types.nullOr lib.types.str;
default = null;
};
};
config = {
# binName = "my-sops";
package = config.pkgs.sops;
extraPackages = with config.pkgs; [ coreutils ssh-to-age ];
preHook = ''
AGE_KEY=$(umask 077; mktemp)
ssh-to-age -private-key -i ${config.sshKey} > "$AGE_KEY"
'';
flags."--config" = "${../../.sops.yaml}";
postHook = ''
rm "$AGE_KEY"
echo "Removed $AGE_KEY"
'';
home.packages = with pkgs; [
eza
age
sops # This is necessary to make the sops binary available
ssh-to-age
(writeShellScriptBin "gen-age-key" ''
set -eu
if [ ! -f "${config.ssh.IdentityFile}" ]; then
echo "SSH identity file not found: ${config.ssh.IdentityFile}" >&2
exit 1
fi
if [ -e "${cfg.ageKeyFile}" ]; then
echo "Refusing to overwrite existing age key file: ${cfg.ageKeyFile}" >&2
exit 1
fi
mkdir -p "$(dirname "${cfg.ageKeyFile}")"
${lib.getExe pkgs.ssh-to-age} -i ${config.ssh.IdentityFile} -private-key > ${cfg.ageKeyFile}
echo -n "Created ${cfg.ageKeyFile}: "
echo $(show-age-key)
'')
(writeShellScriptBin "show-age-key" "${lib.getExe' pkgs.age "age-keygen"} -y ${cfg.ageKeyFile}")
(writeShellScriptBin "ls-secrets" "${lib.getExe pkgs.eza} -alT --follow-symlinks ~/.config/sops-nix/secrets")
] ++ editScript;
home.shellAliases.sops = "${sopsBin} --config ${sopsConfigPath}";
# Option definitions for the sops home-manager module:
# https://github.com/Mic92/sops-nix/blob/master/modules/home-manager/sops.nix
sops = {
defaultSopsFile = sopsSecretsPath;
defaultSopsFormat = "yaml";
age.sshKeyPaths = [ "${config.ssh.IdentityFile}" ];
};
};
});
};
}
-54
View File
@@ -1,54 +0,0 @@
{ self, inputs, ... }: {
perSystem = { system, pkgs, lib, ... }: {
packages.starship = (inputs.wrappers.wrapperModules.starship.apply {
inherit pkgs;
settings = lib.recursiveUpdate (lib.importTOML (pkgs.fetchurl {
url = https://starship.rs/presets/toml/catppuccin-powerline.toml;
sha256 = "1jzbbzm3nwdlldq43aj3csp0ps23fsa6x2225z85l0s9qbj4cdy2";
})) {
palette = "catppuccin_mocha";
add_newline = true;
line_break.disabled = false;
git_status.diverged = "\${ahead_count}\${behind_count}";
cmd_duration.format = "󰔛 $duration";
cmd_duration.show_notifications = false;
hostname = {
disabled = false;
ssh_symbol = "🌐";
format = "[$ssh_symbol$hostname]($style)";
style = "bg:red fg:crust";
};
os.symbols.NixOS = " ";
format = lib.replaceStrings ["\n"] [""] ''
[](red)
$os
$username
$hostname
[](bg:peach fg:red)
$directory
[](bg:yellow fg:peach)
$git_branch
$git_status
[](fg:yellow bg:green)
$c
$rust
$golang
$nodejs
$php
$java
$kotlin
$haskell
$python
[](fg:green bg:sapphire)
$conda
[](fg:sapphire bg:lavender)
$time
[ ](fg:lavender)
$cmd_duration
$line_break
$character
'';
};
}).wrapper;
};
}
-14
View File
@@ -1,14 +0,0 @@
{ self, inputs, ... }: {
flake.modules.nixos.steam = {
programs.steam = {
enable = true;
gamescopeSession.enable = true;
# Open ports in the firewall for Steam Remote Play
remotePlay.openFirewall = true;
# Open ports in the firewall for Source Dedicated Server
dedicatedServer.openFirewall = true;
# Open ports in the firewall for Steam Local Network Game Transfers
localNetworkGameTransfers.openFirewall = true;
};
};
}
+37
View File
@@ -0,0 +1,37 @@
{ self, inputs, ... }: {
#
# Home Manager Module
#
flake.modules.homeManager.step-ssh-user = { config, pkgs, lib, ... }:
let
cfg = config.step-ssh-user;
firstPrincipal = lib.head cfg.principals;
principalArgs = lib.concatMapStringsSep " "
(principal: "--principal \"${principal}\"") cfg.principals;
in
{
options.step-ssh-user = {
enable = lib.mkEnableOption "opionated step client config for SSH certs";
provisioner = lib.mkOption {
type = lib.types.str;
default = "admin";
};
principals = lib.mkOption {
type = lib.types.listOf lib.types.str;
};
};
config = lib.mkIf cfg.enable {
sops.secrets."janus/admin_jwk".mode = "0400";
home.packages = with pkgs; [
(writeShellScriptBin "sign-ssh-cert" ''
${lib.getExe pkgs.step-cli} ssh certificate \
--sign \
${principalArgs} \
--provisioner "${cfg.provisioner}" \
--provisioner-password-file "${config.sops.secrets."janus/admin_jwk".path}" \
"${firstPrincipal}" "${config.ssh.IdentityFile}.pub"
'')
];
};
};
}
+18 -23
View File
@@ -1,26 +1,21 @@
{ self, inputs, ... }: {
flake.modules.homeManager.vscode = { config, pkgs, lib, ... }: {
options.my-vscode = {
enable = lib.mkEnableOption "Enable nix-managed VSCode";
};
config = lib.mkIf config.my-vscode.enable {
programs.vscode = {
enable = true;
package = pkgs.vscode;
profiles.default.extensions = with pkgs.vscode-extensions; [
mhutchie.git-graph
ms-vscode-remote.vscode-remote-extensionpack
ms-python.python
ms-python.vscode-pylance
ms-toolsai.jupyter
charliermarsh.ruff
github.vscode-pull-request-github
github.vscode-github-actions
github.copilot
catppuccin.catppuccin-vsc
];
};
{
flake.modules.homeManager.vscode = { pkgs, ... }:
{
programs.vscode = {
enable = true;
package = pkgs.vscode;
profiles.default.extensions = with pkgs.vscode-extensions; [
mhutchie.git-graph
ms-vscode-remote.vscode-remote-extensionpack
ms-python.python
ms-python.vscode-pylance
ms-toolsai.jupyter
charliermarsh.ruff
github.vscode-pull-request-github
github.vscode-github-actions
github.copilot
catppuccin.catppuccin-vsc
];
};
};
}
-68
View File
@@ -1,68 +0,0 @@
{ self, inputs, ... }: {
flake.modules.nixos.wireguard = { config, pkgs, lib, ... }:
let
wgInterface = "platform";
in
{
imports = [ inputs.sops-nix.nixosModules.sops ];
environment.systemPackages = with pkgs; [
wireguard-tools # https://github.com/WireGuard/wireguard-tools
# wg-netmanager # https://github.com/gin66/wg_netmanager
];
sops.secrets.wireguard_private_key = { };
networking.wg-quick.interfaces = {
${wgInterface} = {
autostart = false; # Managed by dispatcher
postUp = "echo 'Post up command'";
address = [ "192.168.3.5/32" ];
dns = [ "192.168.1.150" ];
privateKeyFile = config.sops.secrets.wireguard_private_key.path;
peers = [
{
publicKey = "BD1/q18OfpoMCDusNZk9cqB1vvR8bgodZ1L7198jVic=";
allowedIPs = [ "192.168.0.0/16" ];
endpoint = "wg.john-stream.com:51830";
persistentKeepalive = 25;
}
];
};
};
};
perSystem = { system, pkgs, lib, ... }:
let
connect = pkgs.writeShellApplication {
name = "wg-platform-connect";
text = ''
sudo systemctl start wg-quick-platform.service
START_TIME=$(sudo systemctl show -p ActiveEnterTimestamp wg-quick-platform | cut -d= -f2)
journalctl -u wg-quick-platform --since "$START_TIME" --no-pager
'';
};
disconnect = pkgs.writeShellApplication {
name = "wg-platform-disconnect";
text = ''
STOP_TIME=$(date '+%Y-%m-%d %H:%M:%S')
systemctl stop wg-quick-platform.service
journalctl -u wg-quick-platform.service --since "$STOP_TIME" --no-pager
'';
};
in
{
packages.wg-platform = inputs.wrappers.lib.wrapPackage {
inherit pkgs;
runtimeInputs = with pkgs; [ coreutils systemd wireguard-tools ];
package = pkgs.symlinkJoin {
name = "wg-platform";
meta.mainProgram = "wg-platform-connect";
paths = [
connect
disconnect
];
};
};
};
}
-11
View File
@@ -1,11 +0,0 @@
{ self, inputs, ... }: {
# https://github.com/Lassulus/wrappers/blob/main/modules/yazi/module.nix
perSystem = { system, pkgs, lib, ... }: {
packages.yazi = (inputs.wrappers.wrapperModules.yazi.apply {
inherit pkgs;
settings = {
mgr.ratio = [ 1 4 3 ];
};
}).wrapper;
};
}
-39
View File
@@ -1,39 +0,0 @@
{ self, ... }:
let
packageName = "zed-editor";
vulkanIcd = "/usr/share/vulkan/icd.d/nvidia_icd.json";
eglVendor = "/usr/share/glvnd/egl_vendor.d/10_nvidia.json";
in
{
perSystem = { pkgs, ... }: {
packages."${packageName}" = pkgs.symlinkJoin {
name = "zed-editor-host-gpu";
paths = [ pkgs.zed-editor ];
nativeBuildInputs = [ pkgs.makeWrapper ];
meta = pkgs.zed-editor.meta // {
mainProgram = "zeditor";
};
postBuild = ''
for exe in $out/bin/*; do
wrapProgram "$exe" \
--unset WAYLAND_DISPLAY \
--unset GDK_BACKEND \
--set VK_DRIVER_FILES ${vulkanIcd} \
--set VK_ICD_FILENAMES ${vulkanIcd} \
--set __EGL_VENDOR_LIBRARY_FILENAMES ${eglVendor} \
--set __GLX_VENDOR_LIBRARY_NAME nvidia
done
'';
};
};
flake.modules.homeManager.zed-editor = { pkgs, ... }: {
home.packages = [ pkgs.vulkan-tools ];
programs.zed-editor = {
enable = true;
package = self.packages.${pkgs.stdenv.hostPlatform.system}."${packageName}";
installRemoteServer = true;
};
};
}
+7 -102
View File
@@ -1,25 +1,6 @@
{ self, inputs, ... }:
{ inputs, ... }:
let
username = "john";
historySize = 10000;
homeEndKeyBindings = ''
# Normalize common Home/End escape sequences across terminal emulators.
bindkey "^[[H" beginning-of-line
bindkey "^[[F" end-of-line
bindkey "^[[1~" beginning-of-line
bindkey "^[[4~" end-of-line
bindkey "^[[7~" beginning-of-line
bindkey "^[[8~" end-of-line
bindkey "^[OH" beginning-of-line
bindkey "^[OF" end-of-line
bindkey "^[[3~" delete-char
# Normalize common Ctrl+Arrow sequences for word-wise movement.
bindkey "^[[1;5D" backward-word
bindkey "^[[1;5C" forward-word
bindkey "^[[5D" backward-word
bindkey "^[[5C" forward-word
'';
in
{
flake.modules = {
@@ -35,14 +16,11 @@ in
homeManager.zsh = { pkgs, config, ... }: {
programs.zsh = {
enable = true;
package = inputs.self.packages.${pkgs.stdenv.hostPlatform.system}.jsl-zsh;
package = pkgs.zsh;
enableCompletion = true;
autosuggestion.enable = true;
# syntaxHighlighting.enable = true;
initContent = ''
HOST=$(hostname -s)
${homeEndKeyBindings}
'';
initContent = "HOST=$(hostname -s)";
dotDir = "${config.xdg.configHome}/zsh";
history = {
append = true;
@@ -53,8 +31,8 @@ in
"eza"
"clear"
];
save = historySize;
size = historySize;
save = 1000;
size = 1000;
share = true;
};
oh-my-zsh = {
@@ -72,78 +50,5 @@ in
};
};
};
perSystem = { config, self', pkgs, lib, ... }: {
packages.jsl-zsh =
let
ignorePatterns = [
"ls" "eza" "history" "clear"
];
aliasStr = lib.concatStringsSep "\n" (
lib.mapAttrsToList (k: v: "alias -- ${lib.escapeShellArg k}=${lib.escapeShellArg v}") {
ls = "eza";
ll = "eza -l";
la = "eza -a";
lt = "eza --tree";
lla = "eza -la";
ds = "gdu -i /snap /";
ld = "lazydocker";
});
in
(inputs.wrappers.wrapperModules.zsh.apply {
inherit pkgs;
binName = "jsl-zsh";
env = {
LANG = "en_US.UTF-8";
COLORTERM = "truecolor";
DEVENV_SHELL_TYPE = "zsh";
};
settings = {
completion = {
enable = true;
extraCompletions = true;
caseInsensitive = true;
fuzzySearch = true;
};
autoSuggestions = {
enable = true;
strategy = [ "history" "completion" ];
};
history = {
append = true;
expanded = true;
share = true;
ignoreAllDups = true;
ignoreSpace = true;
};
integrations = {
fzf.enable = true;
starship = {
enable = true;
package = self'.packages.starship;
};
zoxide.enable = true;
};
};
extraRC = ''
${homeEndKeyBindings}
HISTFILE=$HOME/.config/zsh/.zsh_history
SAVEHIST=${toString historySize}
HISTORY_IGNORE=${lib.escapeShellArg "(${lib.concatStringsSep "|" ignorePatterns})"}
HOSTNAME=$(hostname -s)
${aliasStr}
eval "$(devenv hook zsh)"
'';
extraPackages = with pkgs; [
lazygit
lazydocker
devenv
self'.packages.shell-tools
self'.packages.neovim-min
];
}).wrapper;
};
}
}
+11 -4
View File
@@ -1,13 +1,20 @@
{ self, inputs, ... }:
{ inputs, ... }:
{
flake.modules.nixos.docker = {
virtualisation.docker.enable = true;
home-manager.sharedModules = [ inputs.self.modules.homeManager.docker ];
virtualisation.docker = {
enable = true;
};
home-manager.sharedModules = with inputs.self.modules.homeManager; [
docker
];
};
flake.modules.homeManager.docker = { config, lib, pkgs, ... }:
{
options.docker.enable = lib.mkEnableOption "Docker tools and utilities";
options.docker = {
enable = lib.mkEnableOption "Docker tools and utilities";
};
config = lib.mkIf config.docker.enable {
programs.lazydocker.enable = true;
programs.docker-cli.enable = true;
@@ -1,67 +1,12 @@
{ self, inputs, ... }: {
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}"
];
};
};
{ 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.homeManager.restic = { config, pkgs, lib, ... }:
let
@@ -74,11 +19,6 @@
type = lib.types.str;
default = "john-ubuntu";
};
repoUrl = lib.mkOption {
description = "URL to the REST endpoint";
type = lib.types.str;
default = "rest:https://soteria.john-stream.com/${cfg.repoName}";
};
passwordFile = lib.mkOption {
description = "String path to the restic password file";
type = lib.types.str;
@@ -104,24 +44,29 @@
};
};
config = {
config = let
resticRepository = "rest:https://soteria.john-stream.com/${cfg.repoName}";
caCert = "${config.mtls.certDir}/root_ca.crt";
mtlsClientCert = "${config.mtls.certDir}/${config.mtls.bundleFilename}";
in
{
home.sessionVariables = {
RESTIC_REPOSITORY = cfg.repoUrl;
RESTIC_REPOSITORY = resticRepository;
RESTIC_PASSWORD_FILE = cfg.passwordFile;
RESTIC_CACERT = config.mtls.caFile;
RESTIC_TLS_CLIENT_CERT = config.mtls.bundleFile;
RESTIC_CACERT = caCert;
RESTIC_TLS_CLIENT_CERT = mtlsClientCert;
};
# This is necessary because the restic service in home manager doesn't otherwise expose these options.
systemd.user.services."restic-backups-${cfg.repoName}".Service.Environment = [
"RESTIC_CACERT=${config.mtls.caFile}"
"RESTIC_TLS_CLIENT_CERT=${config.mtls.bundleFile}"
"RESTIC_CACERT=${caCert}"
"RESTIC_TLS_CLIENT_CERT=${mtlsClientCert}"
];
services.restic = {
enable = true;
backups.${cfg.repoName} = {
repository = cfg.repoUrl;
repository = resticRepository;
passwordFile = cfg.passwordFile;
paths = cfg.paths;
timerConfig = {
+70 -106
View File
@@ -1,32 +1,22 @@
{ inputs, ... }:
{inputs, ... }:
let
userName = "john";
sshHostCAPubKey = "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBNug18oLH0vZxnibXJzMJvTWFPZTnSlhCDDVi+rHhgnIum6ZXQ4SF+VHOOAM5BbzZmMKitNJ5lcrGP15Eur7DzQ=";
in
{
flake.modules.nixos.ssh = { config, pkgs, lib, ... }:
flake.modules.nixos.ssh = { pkgs, config, lib, ... }:
let
cfg = config.ssh;
configDir = "/etc/ssh";
userCAPath = "ssh/ssh_user_ca.pub";
hostKeyFile = "ssh/ssh_host_ed25519_key";
in
{
options.ssh = {
hostKey = lib.mkOption {
description = "String path to the host private key file";
type = lib.types.str;
default = "ssh_host_ed25519_key";
};
certificates = {
enable = lib.mkEnableOption "Enable SSH host certificates";
userCA = lib.mkOption {
description = "Content for the SSH user CA file (public key)";
type = lib.types.path;
default = ../hosts/janus/ssh_user_ca.pub;
};
userCAFile = lib.mkOption {
description = "String path to the SSh user CA";
type = lib.types.str;
default = "ssh_user_ca.pub";
default = ../../keys/ssh_user_ca.pub;
};
};
};
@@ -39,21 +29,21 @@ in
{
PasswordAuthentication = false;
KbdInteractiveAuthentication = false;
HostKey = "${configDir}/${cfg.hostKey}";
HostKey = "/etc/${hostKeyFile}";
}
(lib.mkIf cfg.certificates.enable {
TrustedUserCAKeys = "${configDir}/${cfg.certificates.userCAFile}";
HostCertificate = "${configDir}/${cfg.hostKey}-cert.pub";
TrustedUserCAKeys = "/etc/${userCAPath}";
HostCertificate = "/etc/${hostKeyFile}-cert.pub";
})
];
};
environment.etc."ssh/${cfg.certificates.userCAFile}" = lib.mkIf cfg.certificates.enable {
environment.etc."${userCAPath}" = lib.mkIf cfg.certificates.enable {
source = cfg.certificates.userCA;
};
programs.ssh.knownHosts = lib.mkIf cfg.certificates.enable {
"192.168.1.*" = {
"192.168.1.*" = {
certAuthority = true;
publicKey = sshHostCAPubKey;
};
@@ -65,57 +55,52 @@ in
};
};
flake.modules.homeManager.ssh = { config, pkgs, lib, ... }:
let
cfg = config.ssh;
configDir = "${config.home.homeDirectory}/.ssh";
identityFile = cfg.identityFile;
publicKeyFile = "${identityFile}.pub";
certificateFile = "${identityFile}-cert.pub";
in
flake.modules.homeManager.ssh = { pkgs, config, lib, ... }:
{
options.ssh = with lib; {
identityFile = mkOption {
options.ssh = {
IdentityFile = lib.mkOption {
# Intentionally not using a path type here because that will end up with the private key getting copied into the store
type = types.str;
type = lib.types.str;
default = "${config.home.homeDirectory}/.ssh/id_ed25519";
description = "Path to the SSH identity file.";
};
certificates = {
enable = mkEnableOption "Enable SSH client certificates";
enable = lib.mkEnableOption "Enable SSH user certificates";
# sshCertProvisioner = lib.mkOption {
# type = lib.types.str;
# default = "admin";
# };
};
knownHostsFile = mkOption {
type = types.str;
default = "${configDir}/known_hosts";
};
knownHosts = mkOption {
description = "";
type = types.listOf types.str;
default = [ ];
knownHostsFile = lib.mkOption {
type = lib.types.str;
default = "${config.home.homeDirectory}/.ssh/known_hosts";
};
matchSets = {
appdaemon = mkEnableOption "Enable AppDaemon SSH targets";
certs = mkEnableOption "Enable Janus and Soteria SSH targets";
homelab = mkEnableOption "Enable various Homelab targets";
dev = mkEnableOption "Enable development targets";
tailscale = mkEnableOption "Enable tailscale targets";
appdaemon = lib.mkEnableOption "Enable AppDaemon SSH targets";
certs = lib.mkEnableOption "Enable Janus and Soteria SSH targets";
homelab = lib.mkEnableOption "Enable various Homelab targets";
dev = lib.mkEnableOption "Enable development targets";
};
};
# All this stuff has to be wrapped in a config attribute because of the presence of the options here?
config = let
cfg = config.ssh;
identityFile = cfg.IdentityFile;
publicKeyFile = "${identityFile}.pub";
certificateFile = "${identityFile}-cert.pub";
provisionerPasswordPath = config.sops.secrets."janus/admin_jwk".path;
in {
home.file.".ssh/known_hosts" = {
text = lib.concatStringsSep "\n" (
cfg.knownHosts ++ lib.optionals cfg.certificates.enable [
"@cert-authority 192.168.1.* ${sshHostCAPubKey}"
"@cert-authority *.john-stream.com ${sshHostCAPubKey}"
[
"fded:fb16:653e:25da:be24:11ff:fea0:753f ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJ9ZqiWPrCwHjxFCiu0lT4rlQs7KyMapxKJQQ5PJP1eh"
]
++ (lib.optional cfg.certificates.enable "@cert-authority 192.168.1.* ${sshHostCAPubKey}")
++ (lib.optional cfg.certificates.enable "@cert-authority *.john-stream.com ${sshHostCAPubKey}")
);
};
@@ -124,92 +109,71 @@ in
enableDefaultConfig = false;
extraConfig = ''
SetEnv TERM="xterm-256color"
IdentityAgent ~/.1password/agent.sock
'';
settings = lib.mkMerge [
matchBlocks = lib.mkMerge [
{
"john-pc-ubuntu" = {
HostName = "192.168.1.85";
"*" = {
user = "john";
compression = false;
serverAliveInterval = 0;
serverAliveCountMax = 3;
identitiesOnly = true;
inherit identityFile certificateFile;
hashKnownHosts = false;
userKnownHostsFile = cfg.knownHostsFile;
addKeysToAgent = "yes";
forwardAgent = false;
};
"*" = lib.mkMerge [
{
User = "john";
IdentityAgent = "${config.home.homeDirectory}/.1password/agent.sock";
Compression = false;
ServerAliveInterval = 0;
ServerAliveCountMax = 3;
IdentitiesOnly = true;
IdentityFile = identityFile;
HashKnownHosts = false;
UserKnownHostsFile = cfg.knownHostsFile;
AddKeysToAgent = "yes";
ForwardAgent = false;
}
(lib.mkIf cfg.certificates.enable { CertificateFile = certificateFile; })
];
}
(lib.mkIf cfg.matchSets.appdaemon {
"appdaemon" = {
HostName = "192.168.1.242";
User = "appdaemon";
hostname = "192.168.1.242";
user = "appdaemon";
};
"ad-nix" = {
HostName = "192.168.1.201";
User = "appdaemon";
hostname = "192.168.1.201";
user = "appdaemon";
};
})
(lib.mkIf cfg.matchSets.certs {
"janus" = {
HostName = "janus.john-stream.com";
User = "root";
hostname = "janus.john-stream.com";
user = "root";
};
"soteria" = {
HostName = "soteria.john-stream.com";
User = "john";
hostname = "soteria.john-stream.com";
user = "john";
};
})
(lib.mkIf cfg.matchSets.homelab {
"docs" = {
HostName = "192.168.1.110";
User = "root";
RequestTTY = "force";
RemoteCommand = "~/.nix-profile/bin/jsl-zsh";
hostname = "192.168.1.110";
user = "root";
};
"gitea" = {
HostName = "192.168.1.104";
User = "john";
hostname = "192.168.1.104";
user = "john";
};
"hermes" = {
HostName = "192.168.1.150";
User = "root";
# Enabling this breaks the ability of Zed to install its remote stuff
# RequestTTY = "force";
# RemoteCommand = "/root/.nix-profile/bin/jsl-zsh";
hostname = "192.168.1.150";
user = "root";
};
"panoptes" = {
HostName = "192.168.1.107";
User = "panoptes";
hostname = "192.168.1.107";
user = "panoptes";
};
})
(lib.mkIf cfg.matchSets.dev {
"test-nix" = {
HostName = "fded:fb16:653e:25da:be24:11ff:fea0:753f";
User = "john";
RequestTTY = "auto";
# RemoteCommand = "/run/current-system/sw/bin/jsl-zsh";
};
})
(lib.mkIf cfg.matchSets.tailscale {
"jdl-docker" = {
HostName = "jdl-docker.tailcf205.ts.net";
User = "john";
RequestTTY = "auto";
# RemoteCommand = "~/.nix-profile/bin/jsl-zsh";
hostname = "fded:fb16:653e:25da:be24:11ff:fea0:753f";
user = "john";
identityFile = identityFile;
};
})
];
+303
View File
@@ -0,0 +1,303 @@
{ inputs, lib, ... }:
let
# Options that will be in common between
opts = {
enable = lib.mkEnableOption "Enable mTLS";
subject = lib.mkOption {
description = "The Common Name, DNS Name, or IP address that will be set as the Subject Common Name for the certificate. If no Subject Alternative Names (SANs) are configured (via the --san flag) then the subject will be set as the only SAN.";
type = lib.types.str;
};
keyFilename = lib.mkOption {
description = "String filename for the private key";
type = lib.types.str;
default = "key.pem";
};
certFilename = lib.mkOption {
description = "String filename for the public certificate";
type = lib.types.str;
default = "cert.pem";
};
bundleFilename = lib.mkOption {
description = "String filename for the mTLS key bundle";
type = lib.types.str;
default = "mtls.pem";
};
san = lib.mkOption {
description = "List of SAN to give the mTLS cert";
type = lib.types.listOf lib.types.str;
default = [ ];
};
provisioner = lib.mkOption {
type = lib.types.str;
default = "admin";
};
lifetime = lib.mkOption {
type = lib.types.str;
default = "6h";
};
renew = {
enable = lib.mkOption {
description = "Enable automatic mTLS certificate renewal using a systemd timer.";
type = lib.types.bool;
default = true;
};
onCalendar = lib.mkOption {
description = "systemd OnCalendar schedule for mTLS certificate renewal checks.";
type = lib.types.str;
default = "*:1/15";
};
randomizedDelaySec = lib.mkOption {
description = "Randomized delay added to renewal timer runs to avoid synchronized renewals.";
type = lib.types.str;
default = "5m";
};
user = lib.mkOption {
description = "User account to run the mTLS renewal service as.";
type = lib.types.str;
default = "root";
};
group = lib.mkOption {
description = "Group to run the mTLS renewal service as. Defaults to the configured renewal user when null.";
type = lib.types.nullOr lib.types.str;
default = null;
};
reloadUnits = lib.mkOption {
description = "systemd units to try-reload-or-restart after a successful certificate renewal.";
type = lib.types.listOf lib.types.str;
default = [ ];
};
postCommands = lib.mkOption {
description = "Shell commands to run after a successful certificate renewal.";
type = lib.types.listOf lib.types.lines;
default = [ ];
};
};
};
mkMtlsRenewScript = {
pkgs,
tlsCert,
tlsKey,
mtlsBundle,
reloadUnits ? [ ],
postCommands ? [ ],
systemctlArgs ? [ ],
}:
let
systemctl = lib.getExe' pkgs.systemd "systemctl";
escapedArgs = lib.escapeShellArgs systemctlArgs;
systemctlCommand = "${systemctl} ${escapedArgs}";
renewReloadScript = lib.concatMapStringsSep "\n" (unit: ''
if ${systemctlCommand} --quiet is-active "${unit}"; then
${systemctlCommand} try-reload-or-restart "${unit}"
fi
'') reloadUnits;
renewPostCommands = lib.concatStringsSep "\n" postCommands;
in
pkgs.writeShellScriptBin "mtls-renew" ''
set -euo pipefail
if ${lib.getExe pkgs.step-cli} certificate needs-renewal "${tlsCert}"; then
echo "Renewing mTLS certificate"
else
echo "Skipping renew"
exit "$?"
fi
${lib.getExe pkgs.step-cli} ca renew --force "${tlsCert}" "${tlsKey}"
umask 077
${lib.getExe' pkgs.coreutils "cat"} "${tlsCert}" "${tlsKey}" > "${mtlsBundle}"
echo "Reloading units:"
${renewReloadScript}
echo "Post commands:"
${renewPostCommands}
'';
mkNixosMtlsRenewService = {
pkgs,
tlsCert,
tlsKey,
mtlsBundle,
reloadUnits ? [ ],
postCommands ? [ ],
user ? "root",
group ? null,
}:
let
serviceGroup = if group == null then user else group;
renewScript = mkMtlsRenewScript {
inherit pkgs tlsCert tlsKey mtlsBundle reloadUnits postCommands;
};
in
{
description = "Renew the mTLS certificate when Smallstep marks it ready";
wantedBy = [ ];
after = [ "network-online.target" ];
wants = [ "network-online.target" ];
serviceConfig = {
Type = "oneshot";
User = user;
Group = serviceGroup;
ExecStart = lib.getExe renewScript;
};
};
mkNixosMtlsRenewTimer = {
onCalendar,
randomizedDelaySec,
unit ? "mtls-renew.service",
}: {
description = "Periodic Smallstep renewal for the mTLS certificate";
wantedBy = [ "timers.target" ];
timerConfig = {
Persistent = true;
OnCalendar = onCalendar;
AccuracySec = "1us";
RandomizedDelaySec = randomizedDelaySec;
Unit = unit;
};
};
mkHomeManagerMtlsRenewService = {
pkgs,
tlsCert,
tlsKey,
mtlsBundle,
reloadUnits ? [ ],
postCommands ? [ ],
}:
let
renewScript = mkMtlsRenewScript {
inherit pkgs tlsCert tlsKey mtlsBundle reloadUnits postCommands;
systemctlArgs = [ "--user" ];
};
in
{
Unit = {
Description = "Renew the mTLS certificate when Smallstep marks it ready";
After = [ "network-online.target" ];
Wants = [ "network-online.target" ];
};
Service = {
Type = "oneshot";
ExecStart = lib.getExe renewScript;
};
};
mkHomeManagerMtlsRenewTimer = {
onCalendar,
randomizedDelaySec,
unit ? "mtls-renew.service",
}: {
Unit = {
Description = "Periodic Smallstep renewal for the mTLS certificate";
};
Timer = {
Persistent = true;
OnCalendar = onCalendar;
AccuracySec = "1us";
RandomizedDelaySec = randomizedDelaySec;
Unit = unit;
};
Install = {
WantedBy = [ "timers.target" ];
};
};
in
{
flake.modules.nixos.mtls = { config, lib, pkgs, ... }:
let
cfg = config.mtls;
certDir = "/etc/step/certs";
tlsKey = "${certDir}/${cfg.keyFilename}";
tlsCert = "${certDir}/${cfg.certFilename}";
mtlsBundle = "${certDir}/${cfg.bundleFilename}";
sanArgs = lib.concatMapStringsSep " " (san: "--san \"${san}\"") cfg.san;
in
{
options.mtls = opts;
config = lib.mkIf cfg.enable {
environment.systemPackages = with pkgs; lib.optionals cfg.enable [
(writeShellScriptBin "mtls-generate" ''
set -euo pipefail
${lib.getExe pkgs.step-cli} ca certificate \
${cfg.subject} ${tlsCert} ${tlsKey} \
--provisioner ${cfg.provisioner} \
--not-before=-5m --not-after=${cfg.lifetime} \
${sanArgs} \
"$@"
cat ${tlsCert} ${tlsKey} > ${mtlsBundle}
'')
(writeShellScriptBin "mtls-check" ''
${lib.getExe pkgs.openssl} x509 \
-noout -subject -issuer \
-ext subjectAltName,extendedKeyUsage \
-enddate -in ${mtlsBundle}
'')
];
systemd.services.mtls-renew = lib.mkIf cfg.renew.enable (mkNixosMtlsRenewService {
inherit pkgs tlsCert tlsKey mtlsBundle;
inherit (cfg.renew) reloadUnits postCommands user group;
});
systemd.timers.mtls-renew = lib.mkIf cfg.renew.enable (mkNixosMtlsRenewTimer {
inherit (cfg.renew) onCalendar randomizedDelaySec;
});
};
};
flake.modules.homeManager.mtls = { config, lib, pkgs, ... }:
let
cfg = config.mtls;
tlsKey = "${cfg.certDir}/${cfg.keyFilename}";
tlsCert = "${cfg.certDir}/${cfg.certFilename}";
mtlsBundle = "${cfg.certDir}/${cfg.bundleFilename}";
sanArgs = lib.concatMapStringsSep " " (san: "--san \"${san}\"") cfg.san;
in
{
options.mtls = opts // {
certDir = lib.mkOption {
description = "String path to where the mtls certs will be stored.";
type = lib.types.str;
default ="${config.home.homeDirectory}/.step/certs";
};
};
config = {
home.packages = with pkgs; lib.optionals cfg.enable [
step-cli
(writeShellScriptBin "mtls-generate" ''
set -euo pipefail
${lib.getExe pkgs.step-cli} ca certificate \
${cfg.subject} ${tlsCert} ${tlsKey} \
--not-before=-5m --not-after=${cfg.lifetime} \
--provisioner ${cfg.provisioner} \
${sanArgs} \
"$@"
cat ${tlsCert} ${tlsKey} > ${mtlsBundle}
'')
(writeShellScriptBin "mtls-check" ''
${lib.getExe pkgs.openssl} x509 \
-noout -subject -issuer \
-ext subjectAltName,extendedKeyUsage \
-enddate -in ${mtlsBundle}
'')
(mkMtlsRenewScript { inherit pkgs tlsCert tlsKey mtlsBundle; })
];
systemd.user.services.mtls-renew = lib.mkIf cfg.renew.enable (mkHomeManagerMtlsRenewService {
inherit pkgs tlsCert tlsKey mtlsBundle;
inherit (cfg.renew) reloadUnits postCommands;
});
systemd.user.timers.mtls-renew = lib.mkIf cfg.renew.enable (mkHomeManagerMtlsRenewTimer {
inherit (cfg.renew) onCalendar randomizedDelaySec;
});
};
};
}
+2 -2
View File
@@ -33,7 +33,7 @@
networking.nameservers = [ "192.168.1.150" ];
networking.dhcpcd.extraConfig = "nohook resolv.conf";
environment.systemPackages = with pkgs; [
# step-cli
step-cli
(writeShellScriptBin "ssh-host-cert-renew" ''
${lib.getExe pkgs.step-cli} ssh certificate \
--host --sign \
@@ -51,7 +51,7 @@
wantedBy = [ ];
after = [ "network-online.target" ];
wants = [ "network-online.target" ];
path = with pkgs; [ coreutils systemd step-cli openssh ];
path = [ pkgs.step-cli pkgs.openssh pkgs.coreutils pkgs.systemd ];
serviceConfig = {
Type = "oneshot";
User = "root";
+1 -4
View File
@@ -17,9 +17,6 @@ in
crt = "";
};
};
environment.systemPackages = with pkgs; [
step-ca
step-cli
];
environment.systemPackages = with pkgs; [ step-ca step-cli ];
};
}
+32 -34
View File
@@ -1,49 +1,47 @@
{ self, inputs, lib, ... }:
{ inputs, ... }:
let
username = "john";
baseUserModules = self.factory.user {
username = username;
isAdmin = true;
};
in
{
flake.meta.users."${username}" = {
email = "32917998+jsl12@users.noreply.github.com";
name = "John Lancaster";
inherit username;
key = "";
keygrip = [ ];
authorizedKeys = [
# "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIAUa4dcg1TWc4pW++uodyhX4eOqrX/QYIxFWtEP7HFJ john@john-pc-ubuntu"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMOkGLo4N/L3RYvaIZ1FmePlxa1HK0fMciZxKtRhN58F root@janus"
];
};
flake.modules = {
nixos."${username}" = { ... }: {
imports = [
baseUserModules.nixos."${username}"
flake = {
meta.users."${username}" = {
email = "32917998+jsl12@users.noreply.github.com";
name = "John Lancaster";
username = "${username}";
key = "";
keygrip = [
];
authorizedKeys = [
# "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIAUa4dcg1TWc4pW++uodyhX4eOqrX/QYIxFWtEP7HFJ john@john-pc-ubuntu"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMOkGLo4N/L3RYvaIZ1FmePlxa1HK0fMciZxKtRhN58F root@janus"
];
users.users."${username}" = {
openssh.authorizedKeys.keys = inputs.self.meta.users."${username}".authorizedKeys;
};
};
# This module will be imported by the user factory
homeManager."${username}" = { pkgs, ... }:
with inputs.self.meta.users."${username}"; {
home.stateVersion = "25.11";
modules = {
nixos."${username}" = {
imports = [
inputs.self.modules.homeManager.shell-tools
inputs.self.modules.homeManager.ssh
inputs.self.modules.homeManager.git
inputs.home-manager.nixosModules.home-manager
(inputs.self.factory.user username true).nixos."${username}"
];
# home.packages = [
# inputs.self.packages.${pkgs.stdenv.hostPlatform.system}.shell-tools
# ];
users.users."${username}" = {
openssh.authorizedKeys.keys = inputs.self.meta.users."${username}".authorizedKeys;
extraGroups = [ "docker" ];
};
};
homeManager."${username}" = with inputs.self.meta.users."${username}"; {
home.stateVersion = "25.11";
xdg.enable = true;
programs.git.settings.user.name = name;
programs.git.settings.user.email = email;
imports = with inputs.self.modules.homeManager; [
ssh
shell-tools
git
];
};
};
};
}
-212
View File
@@ -1,212 +0,0 @@
#!/usr/bin/env bash
set -u
# --- pretty output helpers ---
BOLD="\033[1m"
DIM="\033[2m"
GREEN="\033[32m"
YELLOW="\033[33m"
RED="\033[31m"
CYAN="\033[36m"
RESET="\033[0m"
section() {
echo
echo -e "${BOLD}${CYAN}==> $1${RESET}"
}
ok() { echo -e "${GREEN}[OK]${RESET} $1"; }
warn() { echo -e "${YELLOW}[WARN]${RESET} $1"; }
err() { echo -e "${RED}[ERR]${RESET} $1"; }
info() { echo -e "${DIM}$1${RESET}"; }
has_cmd() {
command -v "$1" >/dev/null 2>&1
}
found_process=0
found_render_node=0
found_gpu_libs=0
software_rasterizer=0
section "1) Find a running Ghostty process"
info "What this does: tries the same two process lookups you ran (pgrep and pidof)."
info "Good outcome: at least one PID is returned."
echo -e "${DIM}\$ pgrep -x ghostty${RESET}"
PGREP_OUT="$(pgrep -x ghostty || true)"
if [[ -n "${PGREP_OUT}" ]]; then
echo "${PGREP_OUT}"
else
echo "<no output>"
fi
echo -e "${DIM}\$ pidof ghostty${RESET}"
PIDOF_OUT="$(pidof ghostty || true)"
if [[ -n "${PIDOF_OUT}" ]]; then
echo "${PIDOF_OUT}"
else
echo "<no output>"
fi
if [[ -n "${PGREP_OUT}" || -n "${PIDOF_OUT}" ]]; then
found_process=1
ok "Ghostty process detected."
else
warn "No Ghostty process found yet by either command."
fi
section "2) Set GHOSTTY_PID"
info "What this does: picks one Ghostty PID for all later checks."
info "Good outcome: GHOSTTY_PID is set to a valid running Ghostty process."
# Prefer step-1 outputs first (avoids re-running lookups that may block on some systems).
GHOSTTY_PID=""
if [[ -n "${PGREP_OUT}" ]]; then
GHOSTTY_PID="$(printf '%s\n' "${PGREP_OUT}" | head -n1)"
info "Using PID from step-1 pgrep output."
elif [[ -n "${PIDOF_OUT}" ]]; then
GHOSTTY_PID="$(printf '%s\n' "${PIDOF_OUT}" | awk '{print $1}')"
info "Using PID from step-1 pidof output."
else
echo -e "${DIM}\$ pgrep -xo ghostty${RESET}"
if has_cmd timeout; then
PGREP_RAW="$(timeout 3s pgrep -xo ghostty 2>&1)"
pgrep_status=$?
if (( pgrep_status == 124 )); then
warn "pgrep timed out after 3 seconds."
fi
else
PGREP_RAW="$(pgrep -xo ghostty 2>&1)"
pgrep_status=$?
fi
if (( pgrep_status == 0 )); then
GHOSTTY_PID="$(printf '%s\n' "${PGREP_RAW}" | head -n1)"
else
if [[ -n "${PGREP_RAW}" ]]; then
warn "[pgrep] ${PGREP_RAW}"
fi
warn "pgrep exit code: ${pgrep_status} (1 usually means no matching process)."
echo -e "${DIM}\$ pidof ghostty${RESET}"
PIDOF_RAW="$(pidof ghostty 2>&1)"
pidof_status=$?
if (( pidof_status == 0 )); then
GHOSTTY_PID="$(printf '%s\n' "${PIDOF_RAW}" | awk '{print $1}')"
else
if [[ -n "${PIDOF_RAW}" ]]; then
warn "[pidof] ${PIDOF_RAW}"
fi
warn "pidof exit code: ${pidof_status}"
GHOSTTY_PID=""
fi
fi
fi
if [[ -z "${GHOSTTY_PID}" ]]; then
err "Could not find a running Ghostty process to inspect."
echo "Open Ghostty, then run this script again."
exit 1
fi
export GHOSTTY_PID
ok "Using GHOSTTY_PID=${GHOSTTY_PID}"
section "3) Check for an open GPU render device"
info "What this does: inspects /proc/<pid>/fd for /dev/dri entries used for rendering."
info "Good outcome: see /dev/dri/renderD* (for example renderD128)."
if has_cmd rg; then
echo -e "${DIM}\$ sudo ls -l /proc/\$GHOSTTY_PID/fd | rg dri${RESET}"
DRI_LINES="$(sudo ls -l "/proc/${GHOSTTY_PID}/fd" | rg dri || true)"
else
warn "'rg' not found; using grep instead"
echo -e "${DIM}\$ sudo ls -l /proc/\$GHOSTTY_PID/fd | grep dri${RESET}"
DRI_LINES="$(sudo ls -l "/proc/${GHOSTTY_PID}/fd" | grep dri || true)"
fi
if [[ -n "${DRI_LINES}" ]]; then
echo "${DRI_LINES}"
else
echo "<no dri-related file descriptors found>"
fi
if echo "${DRI_LINES}" | grep -q '/dev/dri/renderD'; then
found_render_node=1
ok "Found /dev/dri/renderD*: strong sign Ghostty is using GPU render infrastructure."
elif [[ -n "${DRI_LINES}" ]]; then
warn "Found /dev/dri entries, but no renderD node in this output."
else
warn "No /dev/dri entries seen for this process."
fi
section "4) Check mapped graphics libraries"
info "What this does: reads process memory mappings for GL/EGL/Vulkan/Mesa libraries."
info "Good outcome: matches like libGL, libEGL, libvulkan, mesa, libgbm, libgallium appear."
echo -e "${DIM}\$ sudo grep -E 'libEGL|libGL|libvulkan|mesa' /proc/\$GHOSTTY_PID/maps | head -n 30${RESET}"
LIB_LINES="$(sudo grep -E 'libEGL|libGL|libvulkan|mesa' "/proc/${GHOSTTY_PID}/maps" | head -n 30 || true)"
if [[ -n "${LIB_LINES}" ]]; then
echo "${LIB_LINES}"
found_gpu_libs=1
ok "Graphics stack libraries are mapped into Ghostty."
else
echo "<no matches>"
warn "No GL/EGL/Vulkan/Mesa mappings matched this filter."
fi
section "5) Optional probe for software rasterizer vs hardware driver hints"
info "What this does: scans for common software rasterizer strings (llvmpipe/swrast) and driver names."
info "Good outcome: no llvmpipe/swrast matches. Driver-name matches can vary by stack."
echo -e "${DIM}\$ sudo grep -Ei 'llvmpipe|swrast|iris|radeonsi|nouveau|zink|nvidia|_dri\\.so' /proc/\$GHOSTTY_PID/maps${RESET}"
PROBE_LINES="$(sudo grep -Ei 'llvmpipe|swrast|iris|radeonsi|nouveau|zink|nvidia|_dri\.so' "/proc/${GHOSTTY_PID}/maps" || true)"
if [[ -n "${PROBE_LINES}" ]]; then
echo "${PROBE_LINES}"
if echo "${PROBE_LINES}" | grep -Eiq 'llvmpipe|swrast'; then
software_rasterizer=1
warn "Software rasterizer markers detected (llvmpipe/swrast)."
else
ok "Driver-related markers detected and no software rasterizer markers found."
fi
else
ok "No probe matches found. This is often fine; absence of llvmpipe/swrast is usually good."
fi
section "Final interpretation"
if (( found_process == 1 )); then
ok "Process check: PASS"
else
warn "Process check: no process found initially (script still proceeded once PID was resolved)."
fi
if (( found_render_node == 1 )); then
ok "Render node check: PASS (/dev/dri/renderD* present)"
else
warn "Render node check: no explicit renderD node found"
fi
if (( found_gpu_libs == 1 )); then
ok "Graphics library check: PASS"
else
warn "Graphics library check: no matches"
fi
if (( software_rasterizer == 1 )); then
warn "Software rasterizer check: POSSIBLE software rendering path"
else
ok "Software rasterizer check: no llvmpipe/swrast markers detected"
fi
echo
if (( found_render_node == 1 && found_gpu_libs == 1 && software_rasterizer == 0 )); then
ok "Overall: strong evidence Ghostty is using GPU acceleration."
else
warn "Overall: mixed signals. Consider running a live GPU activity monitor during terminal redraw stress."
fi
ok "Summary complete for PID ${GHOSTTY_PID}"