Compare commits
3 Commits
a97a409c8e
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 7186d6a3a7 | |||
| c836396cbe | |||
| 3493519085 |
@@ -1,70 +0,0 @@
|
|||||||
# NixOS configuration instructions
|
|
||||||
|
|
||||||
## Desktop
|
|
||||||
|
|
||||||
- When adding a graphical application, register its workspace icon in `registry.window-icon`.
|
|
||||||
The workspace indicator uses this mapping to show an application icon instead of the generic default glyph.
|
|
||||||
Match the mapping on the window class.
|
|
||||||
Store the icon as the rendered string.
|
|
||||||
Run `hyprctl clients -j | jq -r '.[].class' | sort -u` to list classes in the running session.
|
|
||||||
- TODO: Verify this path after the NixOS configuration is migrated.
|
|
||||||
Set Hyprland session environment variables in `modules/desktop/session/hyprland/default.nix` with `env = KEY,VALUE`.
|
|
||||||
`home.sessionVariables` do **not** reach the Hyprland session because UWSM does not source `hm-session-vars.sh`.
|
|
||||||
Source cursor values from `config.stylix.cursor`.
|
|
||||||
Set `XCURSOR_*` and `HYPRCURSOR_*` to the same theme for Bibata.
|
|
||||||
|
|
||||||
## Firefox
|
|
||||||
|
|
||||||
- Manage Firefox search engines through `programs.firefox.search` with `force = true`.
|
|
||||||
Hide built-in engines with `<engine>.metaData.hidden = true` because omission does not remove them.
|
|
||||||
Use current engine ids, such as `default = "ddg"` instead of `"DuckDuckGo"`.
|
|
||||||
Decode the built `search.json.mozlz4` with `mozlz4a -d <search.json.mozlz4>` to check the result.
|
|
||||||
- Avoid Home Manager Firefox extension settings unless the whole profile can use the legacy extension-storage backend.
|
|
||||||
Any non-empty `profiles.<name>.extensions.settings.<id>.settings` sets `extensions.webextensions.ExtensionStorageIDB.enabled = false` for the whole profile.
|
|
||||||
|
|
||||||
## Neovim
|
|
||||||
|
|
||||||
- Nixvim's flake input follows the root nixpkgs source, but its Home Manager module does not reuse the host's `pkgs` instance.
|
|
||||||
Keep `programs.nixvim.nixpkgs.useGlobalPackages = true`.
|
|
||||||
That setting makes Nixvim use the shared package set without warning that its source default changed.
|
|
||||||
|
|
||||||
## AI tooling
|
|
||||||
|
|
||||||
- Use produced literals for Herdr key names that include shifted punctuation.
|
|
||||||
For example, use `prefix+<` instead of `prefix+shift+comma`.
|
|
||||||
- Put monorepo Pi extension sources in `.pi/extensions/`.
|
|
||||||
Pi discovers them from subdirectories through the local ancestor-resource patch.
|
|
||||||
- Treat Home Manager-managed Pi settings as generated output.
|
|
||||||
Do not edit them except for throwaway development or local experiments.
|
|
||||||
Promote finished settings into the NixOS configuration before treating them as deployed.
|
|
||||||
|
|
||||||
## Gotchas
|
|
||||||
|
|
||||||
- `configs/nixos/registry/stable-uid.nix` receives library functions through `lib`.
|
|
||||||
Qualify its library calls with `lib.`.
|
|
||||||
- Guest modules are host-agnostic wiring, while host files own deployment-specific service configuration such as served volumes and host paths.
|
|
||||||
For example, `guests/copyparty.nix` should expose options and translate them into guest mounts/service settings, while `hosts/pikachu/default.nix` chooses `/inbox`, `/media`, and their backing paths.
|
|
||||||
Guest addresses and routes are host-level configuration because a guest module can be used on multiple hosts.
|
|
||||||
- `sops.secrets.<name>.owner` must name a user known on the decrypting host.
|
|
||||||
Do not set the owner to a guest-only service user unless the host also declares that user.
|
|
||||||
Use a host-visible owner plus a shared group mode when the guest service can read the decrypted secret file through its group.
|
|
||||||
- A read-only guest bind mount cannot contain nested writable bind mounts unless their mount-point
|
|
||||||
directories already exist inside the read-only source.
|
|
||||||
For aggregate Copyparty volumes such as `/srv` with nested `/srv/inbox`, keep the guest bind mount
|
|
||||||
writable and enforce read-only behavior through Copyparty access plus service `ReadWritePaths`.
|
|
||||||
- `deploy-to` builds from the source checkout, records the imported closure as the target system profile,
|
|
||||||
and activates it. Do not rely on a target-local monorepo checkout for remote deployment.
|
|
||||||
- On Pikachu, `nix copy --to ssh://pikachu` accepted a small `nix store add-file` probe,
|
|
||||||
but rejected locally built system paths that lacked trusted signatures.
|
|
||||||
Use a sudo `nix-store --import` path for unsigned deploy closures.
|
|
||||||
- Public DNS entries are not managed through the NixOS configuration.
|
|
||||||
Do not describe NixOS route choices as public-DNS management.
|
|
||||||
- Do not hand-author `hardware-configuration.nix` for a host that has not been generated on that machine.
|
|
||||||
Generate it on the target system or live ISO, then stage it before evaluating the flake.
|
|
||||||
- Configure the Chaotic Nyx cache only on hosts that select `linuxPackages_cachyos` (currently Neogaia
|
|
||||||
and Zeus), not in the generic host module.
|
|
||||||
- Module-relative paths resolve from the module file. Update them when moving a module into a subdirectory.
|
|
||||||
- On Pikachu, `uptime` does not support `-s`. Use `who -b` to report the boot time.
|
|
||||||
- Evaluating a NixOS guest's inner `config.services.nginx` can force incomplete ACME values.
|
|
||||||
Evaluate the parent host's `config.system.build.toplevel` to validate an ACME-enabled guest.
|
|
||||||
- `pkgs.systemd` does not set `meta.mainProgram`. Use `lib.getExe' pkgs.systemd "systemctl"` instead of `lib.getExe pkgs.systemd`.
|
|
||||||
@@ -13,11 +13,6 @@ in
|
|||||||
modules.services.reverse-proxy.enable = true;
|
modules.services.reverse-proxy.enable = true;
|
||||||
|
|
||||||
services.nginx.virtualHosts = {
|
services.nginx.virtualHosts = {
|
||||||
"games.alexion.dev" = {
|
|
||||||
forceSSL = true;
|
|
||||||
enableACME = true;
|
|
||||||
locations."/".proxyPass = "http://10.23.20.32";
|
|
||||||
};
|
|
||||||
"git.alexion.dev" = {
|
"git.alexion.dev" = {
|
||||||
forceSSL = true;
|
forceSSL = true;
|
||||||
enableACME = true;
|
enableACME = true;
|
||||||
|
|||||||
181
configs/nixos/guests/romm.nix
Normal file
181
configs/nixos/guests/romm.nix
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
cfg = config.guests.romm;
|
||||||
|
rommUid = config.registry."stable-uid".${config.networking.hostName}.romm;
|
||||||
|
redisUid = config.registry."stable-uid".${config.networking.hostName}."redis-romm";
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.guests.romm = {
|
||||||
|
dataPath = lib.mkOption {
|
||||||
|
type = lib.types.nullOr lib.types.str;
|
||||||
|
default = null;
|
||||||
|
example = "/srv/romm";
|
||||||
|
description = "Host path mounted as RomM application data.";
|
||||||
|
};
|
||||||
|
|
||||||
|
postgresqlPath = lib.mkOption {
|
||||||
|
type = lib.types.nullOr lib.types.str;
|
||||||
|
default = null;
|
||||||
|
example = "/srv/romm/postgresql";
|
||||||
|
description = "Host path mounted as RomM's PostgreSQL data.";
|
||||||
|
};
|
||||||
|
|
||||||
|
redisPath = lib.mkOption {
|
||||||
|
type = lib.types.nullOr lib.types.str;
|
||||||
|
default = null;
|
||||||
|
example = "/srv/romm/redis";
|
||||||
|
description = "Host path mounted as RomM's Redis data.";
|
||||||
|
};
|
||||||
|
|
||||||
|
romsPath = lib.mkOption {
|
||||||
|
type = lib.types.nullOr lib.types.str;
|
||||||
|
default = null;
|
||||||
|
example = "/srv/roms";
|
||||||
|
description = "Host path mounted as RomM's ROM library.";
|
||||||
|
};
|
||||||
|
|
||||||
|
biosPath = lib.mkOption {
|
||||||
|
type = lib.types.nullOr lib.types.str;
|
||||||
|
default = null;
|
||||||
|
example = "/srv/bios";
|
||||||
|
description = "Host path mounted as RomM's BIOS library.";
|
||||||
|
};
|
||||||
|
|
||||||
|
usersPath = lib.mkOption {
|
||||||
|
type = lib.types.nullOr lib.types.str;
|
||||||
|
default = null;
|
||||||
|
example = "/srv/romm/users";
|
||||||
|
description = "Host path mounted as RomM's user assets.";
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkIf cfg.enable {
|
||||||
|
assertions =
|
||||||
|
map
|
||||||
|
(path: {
|
||||||
|
assertion = path.value != null;
|
||||||
|
message = "guests.romm.${path.name} must point at the host directory that stores ${path.description}.";
|
||||||
|
})
|
||||||
|
[
|
||||||
|
{
|
||||||
|
name = "dataPath";
|
||||||
|
value = cfg.dataPath;
|
||||||
|
description = "RomM application data";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "postgresqlPath";
|
||||||
|
value = cfg.postgresqlPath;
|
||||||
|
description = "RomM PostgreSQL data";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "redisPath";
|
||||||
|
value = cfg.redisPath;
|
||||||
|
description = "RomM Redis data";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "romsPath";
|
||||||
|
value = cfg.romsPath;
|
||||||
|
description = "RomM's ROM library";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "biosPath";
|
||||||
|
value = cfg.biosPath;
|
||||||
|
description = "RomM's BIOS library";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "usersPath";
|
||||||
|
value = cfg.usersPath;
|
||||||
|
description = "RomM's user assets";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
guests.romm = {
|
||||||
|
interior = {
|
||||||
|
modules.services.romm.enable = true;
|
||||||
|
services.romm.nginx.virtualHost = cfg.reverseProxy.route.domain;
|
||||||
|
};
|
||||||
|
|
||||||
|
mounts = {
|
||||||
|
"/var/lib/romm" = {
|
||||||
|
directory = cfg.dataPath;
|
||||||
|
owner = rommUid;
|
||||||
|
group = "storage";
|
||||||
|
mode = "2770";
|
||||||
|
};
|
||||||
|
"/var/lib/postgresql" = {
|
||||||
|
directory = cfg.postgresqlPath;
|
||||||
|
owner = config.ids.uids.postgres;
|
||||||
|
group = "storage";
|
||||||
|
mode = "0700";
|
||||||
|
};
|
||||||
|
"/var/lib/redis-romm" = {
|
||||||
|
directory = cfg.redisPath;
|
||||||
|
owner = redisUid;
|
||||||
|
group = "storage";
|
||||||
|
mode = "0700";
|
||||||
|
};
|
||||||
|
"/var/lib/romm/library/roms" = {
|
||||||
|
directory = cfg.romsPath;
|
||||||
|
owner = rommUid;
|
||||||
|
group = "storage";
|
||||||
|
mode = "2770";
|
||||||
|
};
|
||||||
|
"/var/lib/romm/library/bios" = {
|
||||||
|
directory = cfg.biosPath;
|
||||||
|
owner = rommUid;
|
||||||
|
group = "storage";
|
||||||
|
mode = "2770";
|
||||||
|
};
|
||||||
|
"/var/lib/romm/assets/users" = {
|
||||||
|
directory = cfg.usersPath;
|
||||||
|
owner = rommUid;
|
||||||
|
group = "storage";
|
||||||
|
mode = "2770";
|
||||||
|
};
|
||||||
|
"/run/secrets/romm.env" = {
|
||||||
|
file = config.sops.templates."romm.env".path;
|
||||||
|
readOnly = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
limits = {
|
||||||
|
memory = lib.mkDefault "4G";
|
||||||
|
cpu = lib.mkDefault "200%";
|
||||||
|
tasksMax = lib.mkDefault 512;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
sops.secrets =
|
||||||
|
lib.genAttrs
|
||||||
|
[
|
||||||
|
"services/romm/igdb/client-id"
|
||||||
|
"services/romm/igdb/client-secret"
|
||||||
|
"services/romm/retroachievements/api-key"
|
||||||
|
"services/romm/screenscraper/user"
|
||||||
|
"services/romm/screenscraper/password"
|
||||||
|
"services/romm/steamgriddb/api-key"
|
||||||
|
]
|
||||||
|
(_: {
|
||||||
|
sopsFile = ../secrets + "/${config.networking.hostName}.yaml";
|
||||||
|
});
|
||||||
|
|
||||||
|
sops.templates."romm.env" = {
|
||||||
|
owner = "root";
|
||||||
|
group = "storage";
|
||||||
|
mode = "0440";
|
||||||
|
content = ''
|
||||||
|
IGDB_CLIENT_ID=${config.sops.placeholder."services/romm/igdb/client-id"}
|
||||||
|
IGDB_CLIENT_SECRET=${config.sops.placeholder."services/romm/igdb/client-secret"}
|
||||||
|
SCREENSCRAPER_USER=${config.sops.placeholder."services/romm/screenscraper/user"}
|
||||||
|
SCREENSCRAPER_PASSWORD=${config.sops.placeholder."services/romm/screenscraper/password"}
|
||||||
|
RETROACHIEVEMENTS_API_KEY=${config.sops.placeholder."services/romm/retroachievements/api-key"}
|
||||||
|
STEAMGRIDDB_API_KEY=${config.sops.placeholder."services/romm/steamgriddb/api-key"}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -88,6 +88,26 @@
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
guests.romm = {
|
||||||
|
enable = true;
|
||||||
|
vlan = 20;
|
||||||
|
address = "10.23.20.112";
|
||||||
|
dataPath = "/pikachu/data/romm";
|
||||||
|
postgresqlPath = "/pikachu/data/romm/postgresql";
|
||||||
|
redisPath = "/pikachu/data/romm/redis";
|
||||||
|
romsPath = "/pikachu/media/games/emulation/roms";
|
||||||
|
biosPath = "/pikachu/media/games/emulation/bios";
|
||||||
|
usersPath = "/pikachu/media/games/emulation/users";
|
||||||
|
reverseProxy = {
|
||||||
|
enable = true;
|
||||||
|
route = {
|
||||||
|
domain = "games.alexion.dev";
|
||||||
|
protocol = "https";
|
||||||
|
port = 80;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
guests.reverse-proxy = {
|
guests.reverse-proxy = {
|
||||||
enable = true;
|
enable = true;
|
||||||
vlan = 20;
|
vlan = 20;
|
||||||
|
|||||||
@@ -67,6 +67,8 @@ in
|
|||||||
|
|
||||||
# Stylix uses a near-white selection background.
|
# Stylix uses a near-white selection background.
|
||||||
# Use the Nord selection color because its text stays readable.
|
# Use the Nord selection color because its text stays readable.
|
||||||
|
# Home Manager writes extension settings through Firefox's legacy storage backend.
|
||||||
|
# Extension settings disable `ExtensionStorageIDB` for the whole profile.
|
||||||
extensions.settings."FirefoxColor@mozilla.com".settings.theme.colors.popup_highlight =
|
extensions.settings."FirefoxColor@mozilla.com".settings.theme.colors.popup_highlight =
|
||||||
let
|
let
|
||||||
c = hm.config.lib.stylix.colors;
|
c = hm.config.lib.stylix.colors;
|
||||||
|
|||||||
47
configs/nixos/modules/services/romm.nix
Normal file
47
configs/nixos/modules/services/romm.nix
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
cfg = config.modules.services.romm;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.modules.services.romm = {
|
||||||
|
enable = lib.mkEnableOption "RomM";
|
||||||
|
|
||||||
|
environmentFile = lib.mkOption {
|
||||||
|
type = lib.types.path;
|
||||||
|
default = "/run/secrets/romm.env";
|
||||||
|
readOnly = true;
|
||||||
|
description = "Path to RomM's environment file.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkMerge [
|
||||||
|
{
|
||||||
|
registry."stable-uid".${config.networking.hostName} = {
|
||||||
|
romm = 20005;
|
||||||
|
"redis-romm" = 20006;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
(lib.mkIf cfg.enable {
|
||||||
|
networking.firewall.allowedTCPPorts = [ 80 ];
|
||||||
|
|
||||||
|
services.romm = {
|
||||||
|
enable = true;
|
||||||
|
group = "storage";
|
||||||
|
database.createLocally = true;
|
||||||
|
redis.createLocally = true;
|
||||||
|
watcher.enable = true;
|
||||||
|
inherit (cfg) environmentFile;
|
||||||
|
};
|
||||||
|
|
||||||
|
users.users = {
|
||||||
|
romm.uid = config.registry."stable-uid".${config.networking.hostName}.romm;
|
||||||
|
redis-romm.uid = config.registry."stable-uid".${config.networking.hostName}."redis-romm";
|
||||||
|
};
|
||||||
|
})
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -24,6 +24,9 @@ in
|
|||||||
home-manager.users.${user} = hm: {
|
home-manager.users.${user} = hm: {
|
||||||
programs.nixvim = {
|
programs.nixvim = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
|
||||||
|
# Aligns Nixvim plugins with this flake's `Nixpkgs`.
|
||||||
|
# `nixpkgs.useGlobalPackages` prevents Home Manager from creating a separate package set.
|
||||||
nixpkgs.useGlobalPackages = true;
|
nixpkgs.useGlobalPackages = true;
|
||||||
|
|
||||||
extraPackages = with pkgs; [
|
extraPackages = with pkgs; [
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ let
|
|||||||
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
# Next stable UID: 20005
|
# Next stable UID: 20007
|
||||||
|
|
||||||
type = lib.types.ints.positive;
|
type = lib.types.ints.positive;
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,17 @@ services:
|
|||||||
lastfm:
|
lastfm:
|
||||||
api-key: ENC[AES256_GCM,data:Zb0I5TpdEqOxLAD6ed2XywFGZp7dfY69vcGiWOUJkJQ=,iv:pO53pHr09jWfzJyTsQbTHtzTmiQ9+RQkDwZJmInIiX8=,tag:io9jkSN7TF2I8+qlSymaYw==,type:str]
|
api-key: ENC[AES256_GCM,data:Zb0I5TpdEqOxLAD6ed2XywFGZp7dfY69vcGiWOUJkJQ=,iv:pO53pHr09jWfzJyTsQbTHtzTmiQ9+RQkDwZJmInIiX8=,tag:io9jkSN7TF2I8+qlSymaYw==,type:str]
|
||||||
secret: ENC[AES256_GCM,data:rKhd9i0/tbJlXhc9Wf81IDydjpD8khSdwJEEMK260E4=,iv:btBXWBKJp9br5a3yZsRVegN8Ytzc45Qbk2iNlPbENkQ=,tag:LUbu+6gxfqQvVpddZto7Tg==,type:str]
|
secret: ENC[AES256_GCM,data:rKhd9i0/tbJlXhc9Wf81IDydjpD8khSdwJEEMK260E4=,iv:btBXWBKJp9br5a3yZsRVegN8Ytzc45Qbk2iNlPbENkQ=,tag:LUbu+6gxfqQvVpddZto7Tg==,type:str]
|
||||||
|
romm:
|
||||||
|
igdb:
|
||||||
|
client-id: ENC[AES256_GCM,data:tme7NzzbOlCmkMwVObWz793mOsCyVIzSjGoFvxpe,iv:5aOMvsDi8Z9aBymbqhbK79X4kJsqaET1y2Py6YUZZ/U=,tag:ih3GghlxiLsVyeTJoqU9/Q==,type:str]
|
||||||
|
client-secret: ENC[AES256_GCM,data:AqF7B9VaxM/X3sRqY6C5JVWPWqgRIoqy09P9ZUFj,iv:D+vDPUuGGdzKYnTVTRkI9TV/EOJgVECIeNDt9g8NGl0=,tag:MRaTT7TdEZSd+98xthVw1A==,type:str]
|
||||||
|
retroachievements:
|
||||||
|
api-key: ENC[AES256_GCM,data:LMzETQCMi9Ib783W0BZDe2PaSUSTVM5a61502RtZLg8=,iv:Hf5nBoVtZdJszQuc2n0LVNMtJCCpJrh40y4g+l+az70=,tag:esfKLpI1W8BG3Pfsg3xxKQ==,type:str]
|
||||||
|
steamgriddb:
|
||||||
|
api-key: ENC[AES256_GCM,data:+kBRieIB/dHbvQLbH2gQTQh2j6DQhybVWEGkSCRZrZY=,iv:3h6dhfiV7HfaZhMv93x8+7F3utzYG6fIbTDScA14PVI=,tag:Wo24YnvsIxe7iMRLyxrj2w==,type:str]
|
||||||
|
screenscraper:
|
||||||
|
user: ENC[AES256_GCM,data:7hGcFF4xqg==,iv:SFOdYej94Thfn+Te0kXsC9Kq568A1dsnaR+JB90MgBQ=,tag:inIWuFY7f5Tvv8EyV71dYA==,type:str]
|
||||||
|
password: ENC[AES256_GCM,data:eUWwDDQUSytm/6QZlG5XjbSi41nsBQbm4w==,iv:82S2a3rAVjaF0CXcjXUXErNHMY7MEkjpDL7EVX9cIHw=,tag:uGuVJCv8ThsaNnA0C0WR2Q==,type:str]
|
||||||
sops:
|
sops:
|
||||||
age:
|
age:
|
||||||
- enc: |
|
- enc: |
|
||||||
@@ -26,7 +37,7 @@ sops:
|
|||||||
dyjzy0/9Ui1IIXH36AImO5hiEZcrGePV2qPLLQc7ZLngC1jlJuEKJA==
|
dyjzy0/9Ui1IIXH36AImO5hiEZcrGePV2qPLLQc7ZLngC1jlJuEKJA==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
recipient: age1wf5s0n0tgt6ld2ysgu9dc67mj8ylwecgl4utzg7hqwy3kut9zyms7aglmh
|
recipient: age1wf5s0n0tgt6ld2ysgu9dc67mj8ylwecgl4utzg7hqwy3kut9zyms7aglmh
|
||||||
lastmodified: "2026-09-12T22:54:25Z"
|
lastmodified: "2026-10-20T01:27:06Z"
|
||||||
mac: ENC[AES256_GCM,data:4R3a0ytf4i8aM8iBQ9vRC/q+hvtjLrCTNgbpCB5DbqBMBYmhtGsBmO/0i1yT0N4tydyLaGOMkNfuuuVi3ASLEOKnViy9w3uD9IN0F79W4f2vguWUVIp/T8skvN9wbV9o9xd0LkJzmeVVgzb1H6HKw+rTGjbquleoIHEBDcDDM+k=,iv:0NlckMXlS+idY6UGBF+F0KFLt+pzD7Q/YEQOdXraAYg=,tag:JBBQduV8uhnKz9gPf6HfAQ==,type:str]
|
mac: ENC[AES256_GCM,data:NCt6e/ctZwaBADfrhu1RXtILlClbHEqOAnIu8rE/zMPSwMIPB/kTtfd/ujf+1iRszoHaX4Se1xc0wz1th6NVToFVNarxBBgTk4JMUU8gdMweOjmwlh3GXU3ZcYY/xAXxWF1hxyXSEW78j3S2844H9XXENAq/Bf3mjdCYQ/qr/d4=,iv:4nFyBSmRvF4zSAwvL+xccMwtWrVbHtFKNoTb7f3rSH4=,tag:eCV/7P0TjyMZXKcWBUPXQA==,type:str]
|
||||||
unencrypted_suffix: _unencrypted
|
unencrypted_suffix: _unencrypted
|
||||||
version: 3.13.3
|
version: 3.13.3
|
||||||
|
|||||||
130
docs/00 INBOX/ai/nixos-agents-style-scope-design-tree.md
Normal file
130
docs/00 INBOX/ai/nixos-agents-style-scope-design-tree.md
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
# NixOS AGENTS and style-skill scope design tree
|
||||||
|
|
||||||
|
This tree reviews `configs/nixos/AGENTS.md`.
|
||||||
|
It decides which instructions belong in Nix style rules.
|
||||||
|
|
||||||
|
## Known facts
|
||||||
|
|
||||||
|
- `configs/nixos/AGENTS.md` has 70 lines.
|
||||||
|
- Each frontier question quotes the complete instruction under review.
|
||||||
|
- This review decides instruction placement only.
|
||||||
|
- It supplies guidance for desktop, Firefox, Neovim, AI tooling, and NixOS gotchas.
|
||||||
|
- The `/review` prompt loads `nix-style` for Nix files.
|
||||||
|
- `nix-style` loads every rule under `src/skills/style/nix/rules/`.
|
||||||
|
- The current Nix rules cover Nix syntax and flake structure.
|
||||||
|
- `configs/nixos/AGENTS.md` includes a guest-module ownership rule.
|
||||||
|
- The RomM session missed that ownership rule during implementation.
|
||||||
|
- `registry.window-icon` exists in `configs/nixos/registry/window-icon.nix`.
|
||||||
|
- Desktop application modules declare window classes and rendered icons in that registry.
|
||||||
|
- Hyprland reads the registry to generate workspace icon mappings.
|
||||||
|
- The AGENTS instruction named the registry after its introduction on 2026-10-11.
|
||||||
|
- The `class` option describes its window-class match.
|
||||||
|
- The `icon` option describes a string icon value.
|
||||||
|
- The registry schema does not document the rendered-string requirement or the class-discovery command.
|
||||||
|
- Hyprland renders `config.registry.env` as its `env` configuration.
|
||||||
|
- The `env` registry replaced direct Hyprland environment declarations in commit `370cab8f8`.
|
||||||
|
- `configs/nixos/modules/desktop/apps/r2modman.nix` enables a graphical application without a `registry.window-icon` declaration.
|
||||||
|
- The current registry modules already validate value uniqueness and field combinations.
|
||||||
|
- They do not validate required producer declarations.
|
||||||
|
- The registry collector is specific to the NixOS dotfiles configuration.
|
||||||
|
- A non-discoverable environment fact records local behavior that configuration, source code, and normal tool help do not reveal.
|
||||||
|
- The current instruction groups are:
|
||||||
|
- Desktop: window icons, Hyprland environment variables, and cursor settings.
|
||||||
|
- Firefox: search-engine declarations and extension-storage compatibility.
|
||||||
|
- Neovim: Nixvim package-set selection.
|
||||||
|
- AI tooling: Herdr key names and Pi configuration locations.
|
||||||
|
- Gotchas: registry APIs, guest boundaries, SOPS ownership, guest mounts, deployment, DNS, hardware, module paths, and host commands.
|
||||||
|
|
||||||
|
## Decision tree
|
||||||
|
|
||||||
|
- Instruction review
|
||||||
|
- Settled: Review each complete instruction in source order.
|
||||||
|
- Registry conventions
|
||||||
|
- Settled: Create `src/skills/style/nix/rules/dotfiles.md`.
|
||||||
|
- Graphical application icon instruction
|
||||||
|
- Settled: Move the complete instruction to `src/skills/style/nix/rules/dotfiles.md`.
|
||||||
|
- Hyprland session environment instruction
|
||||||
|
- Settled: Remove it because `registry.env` superseded it.
|
||||||
|
- Firefox search-engine instruction
|
||||||
|
- Settled: Remove it because the Firefox module documents the configuration.
|
||||||
|
- Firefox extension-storage instruction
|
||||||
|
- Settled: Move it to a comment above `extensions.settings` in `modules/desktop/apps/firefox.nix`.
|
||||||
|
- Neovim package-set instruction
|
||||||
|
- Settled: Move it to a comment above `nixpkgs.useGlobalPackages` in `modules/shell/nvim/default.nix`.
|
||||||
|
- Herdr key-name instruction
|
||||||
|
- Settled: Remove it.
|
||||||
|
- Pi extension location instruction
|
||||||
|
- Settled: Remove it. It recorded a completed migration from NixOS-managed sources.
|
||||||
|
- Pi generated-settings instruction
|
||||||
|
- Settled: Remove it because the Pi module documents the managed boundary.
|
||||||
|
- Library-function qualification instruction
|
||||||
|
- Settled: Generalize it and move it to `src/skills/style/nix/rules/general.md`.
|
||||||
|
- Guest ownership instruction
|
||||||
|
- Settled: Move it to `src/skills/style/nix/rules/dotfiles.md`.
|
||||||
|
- SOPS guest-owner instruction
|
||||||
|
- Settled: Move it to `src/skills/style/nix/rules/dotfiles.md`.
|
||||||
|
- Nested guest-mount instruction
|
||||||
|
- Settled: Remove it because the example describes no active layout.
|
||||||
|
- `deploy-to` instruction
|
||||||
|
- Settled: Remove it. Agents cannot complete its remote sudo steps.
|
||||||
|
- Pikachu deploy-closure instruction
|
||||||
|
- Settled: Remove it because `deploy-to` encodes the required import path.
|
||||||
|
- Public-DNS instruction
|
||||||
|
- Settled: Remove it.
|
||||||
|
- Hardware-configuration instruction
|
||||||
|
- Settled: Remove it because the root auto-generated-file rule owns the policy.
|
||||||
|
- Chaotic-cache instruction
|
||||||
|
- Settled: Remove it because `modules.host.cachyos.enable` owns the cache configuration.
|
||||||
|
- Module-relative-path instruction
|
||||||
|
- Settled: Remove it because Nix evaluation reports invalid paths.
|
||||||
|
- Pikachu `uptime` instruction
|
||||||
|
- Settled: Remove it because the command exposes its supported options.
|
||||||
|
- Guest-ACME-evaluation instruction
|
||||||
|
- Settled: Remove it because current direct guest evaluation succeeds.
|
||||||
|
- systemd executable instruction
|
||||||
|
- Settled: Remove it because `general.md` already requires `lib.getExe'` for non-main executables.
|
||||||
|
|
||||||
|
## Frontier
|
||||||
|
|
||||||
|
The frontier is empty.
|
||||||
|
|
||||||
|
## Shared understanding
|
||||||
|
|
||||||
|
Remove these instructions from `configs/nixos/AGENTS.md`:
|
||||||
|
|
||||||
|
- Hyprland session environment
|
||||||
|
- Firefox search engines
|
||||||
|
- Herdr shifted punctuation
|
||||||
|
- Pi extension source location
|
||||||
|
- Pi managed settings
|
||||||
|
- Nested guest mounts
|
||||||
|
- `deploy-to` behavior
|
||||||
|
- Pikachu deploy closures
|
||||||
|
- Public DNS boundary
|
||||||
|
- Hardware configuration
|
||||||
|
- Chaotic cache selection
|
||||||
|
- Module-relative paths
|
||||||
|
- Pikachu boot time
|
||||||
|
- Guest ACME evaluation
|
||||||
|
- systemd executable selection
|
||||||
|
|
||||||
|
Move these instructions intact to `src/skills/style/nix/rules/dotfiles.md`:
|
||||||
|
|
||||||
|
- Graphical application icons
|
||||||
|
- Guest ownership
|
||||||
|
- SOPS guest ownership
|
||||||
|
|
||||||
|
Move the generalized library-function qualification convention to
|
||||||
|
`src/skills/style/nix/rules/general.md`.
|
||||||
|
|
||||||
|
Move these instructions to code comments:
|
||||||
|
|
||||||
|
- Firefox extension storage: above `extensions.settings` in
|
||||||
|
`configs/nixos/modules/desktop/apps/firefox.nix`
|
||||||
|
- Nixvim package set: above `nixpkgs.useGlobalPackages` in
|
||||||
|
`configs/nixos/modules/shell/nvim/default.nix`
|
||||||
|
|
||||||
|
The review does not decide how to restore the auto-generated status of
|
||||||
|
`configs/nixos/hosts/pikachu/hardware-configuration.nix`.
|
||||||
|
|
||||||
|
The operator confirmed this design and authorized the edits.
|
||||||
@@ -4,7 +4,7 @@ aliases:
|
|||||||
- RomM Guest
|
- RomM Guest
|
||||||
date-created: 2026-10-19 19:40
|
date-created: 2026-10-19 19:40
|
||||||
date-modified: 2026-10-19 19:40
|
date-modified: 2026-10-19 19:40
|
||||||
status: open
|
status: closed
|
||||||
area: "[[homelab]]"
|
area: "[[homelab]]"
|
||||||
blocked-by: []
|
blocked-by: []
|
||||||
subtasks: []
|
subtasks: []
|
||||||
|
|||||||
43
src/skills/style/nix/rules/dotfiles.md
Normal file
43
src/skills/style/nix/rules/dotfiles.md
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
## Register window icons
|
||||||
|
|
||||||
|
### Rule
|
||||||
|
|
||||||
|
Each graphical application has a `registry.window-icon` entry with a window-class match and rendered icon.
|
||||||
|
|
||||||
|
### Rationale
|
||||||
|
|
||||||
|
Hyprland uses this registry to render workspace icons for open application windows.
|
||||||
|
|
||||||
|
Run this command to list window classes from the running compositor:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
hyprctl clients -j | jq -r '.[].class' | sort -u
|
||||||
|
```
|
||||||
|
|
||||||
|
## Separate guest wrappers from service modules
|
||||||
|
|
||||||
|
### Rule
|
||||||
|
|
||||||
|
Each guest module wraps one service module.
|
||||||
|
The guest enables its service through `guests.<name>.interior`.
|
||||||
|
Service modules own service configuration.
|
||||||
|
Guest modules configure bind mounts, secret delivery, resource limits, and registry entries.
|
||||||
|
Host configuration supplies deployment-specific paths, volumes, addresses, and routes.
|
||||||
|
|
||||||
|
### Rationale
|
||||||
|
|
||||||
|
Service modules support a service without guest infrastructure.
|
||||||
|
Guest modules adapt a service module to a guest.
|
||||||
|
Host configuration provides deployment-specific values.
|
||||||
|
|
||||||
|
## Decrypt guest secrets on the host
|
||||||
|
|
||||||
|
### Rule
|
||||||
|
|
||||||
|
A host user owns each guest secret before SOPS decrypts it.
|
||||||
|
Each guest service group accesses its secrets through a read-only bind mount.
|
||||||
|
|
||||||
|
### Rationale
|
||||||
|
|
||||||
|
Guest service accounts do not exist on the host, where SOPS creates secrets.
|
||||||
|
The shared service group preserves read access across the guest boundary.
|
||||||
@@ -22,6 +22,17 @@ Do not call `lib.removeSuffix ".nix"` outside the utility definition.
|
|||||||
|
|
||||||
The utility centralizes Nix file-name normalization.
|
The utility centralizes Nix file-name normalization.
|
||||||
|
|
||||||
|
## Qualify library functions
|
||||||
|
|
||||||
|
### Rule
|
||||||
|
|
||||||
|
Keep library functions qualified as `lib.<function>`.
|
||||||
|
|
||||||
|
### Rationale
|
||||||
|
|
||||||
|
Qualification makes library ownership explicit.
|
||||||
|
It prevents local namespace collisions.
|
||||||
|
|
||||||
## Use executable helpers for package commands
|
## Use executable helpers for package commands
|
||||||
|
|
||||||
### Rule
|
### Rule
|
||||||
|
|||||||
Reference in New Issue
Block a user