Compare commits
4 Commits
main
...
7a933fba6d
| Author | SHA1 | Date | |
|---|---|---|---|
| 7a933fba6d | |||
| 9940df122b | |||
| 36eb4a231e | |||
| 5b87edd090 |
@@ -1,8 +0,0 @@
|
||||
---
|
||||
description: Summarize an agent response
|
||||
argument-hint: "[source request]"
|
||||
---
|
||||
|
||||
Summarize `${ARGUMENTS:-the preceding agent response}`.
|
||||
Keep the summary compact.
|
||||
Use no fixed format.
|
||||
@@ -88,6 +88,8 @@
|
||||
- Ariadne Rust tests are declared in Bazel `BUILD` files; do not add `[[test]]` entries to
|
||||
`src/ariadne/Cargo.toml` unless explicitly requested.
|
||||
- In this monorepo, "dotfiles" refers to the dotfiles/NixOS configuration under `src/dotfiles/` and `configs/nixos/`.
|
||||
- The flake does not currently define `formatter.x86_64-linux`.
|
||||
Do not use `nix fmt` as a validation command until a formatter is added.
|
||||
- The `fix` app passes its current directory to Deadnix.
|
||||
Run `nix run .#fix` from the repository root so path exclusions match.
|
||||
- Git pathspec searches started in a subdirectory default to that subtree.
|
||||
@@ -111,6 +113,3 @@
|
||||
- The default Pi model for NixOS user configuration is set in `configs/nixos/modules/shell/pi/default.nix`, not in vendored Pi source.
|
||||
- The `monorepo` and `status` libraries do not have `README.md` files.
|
||||
- Check root and `src/skills/.pi/skills/` for enabled symlinks before removing a monorepo skill.
|
||||
- Ariadne Rust source files live directly under `src/ariadne/`. The binary entry point is `ariadne_main.rs.
|
||||
- Bazel build-style rules live under `src/skills/style/build/rules/`, not `src/skills/style/rules/`.
|
||||
- Bazel passes arguments after `--` to the executed binary verbatim. A scaffold can define `//path` as a destination-path syntax.
|
||||
|
||||
70
configs/nixos/AGENTS.md
Normal file
70
configs/nixos/AGENTS.md
Normal file
@@ -0,0 +1,70 @@
|
||||
# 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`.
|
||||
@@ -1,51 +0,0 @@
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
registry,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.guests.adguard;
|
||||
in
|
||||
{
|
||||
options.guests.adguard.adminPasswordHash = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "Bcrypt verifier for the AdGuard Home administrator password.";
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
assertions = [
|
||||
{
|
||||
assertion = cfg.address != null;
|
||||
message = "guests.adguard.address must identify the AdGuard Home guest.";
|
||||
}
|
||||
];
|
||||
|
||||
registry.homepage.${config.networking.hostName}.adguard = {
|
||||
tab = "System";
|
||||
section = "Network";
|
||||
name = "AdGuard Home";
|
||||
description = "Internal DNS";
|
||||
icon = "adguard-home.svg";
|
||||
href = "http://${cfg.address}";
|
||||
};
|
||||
|
||||
guests.adguard = {
|
||||
interior = {
|
||||
modules.services.adguardhome = {
|
||||
enable = true;
|
||||
inherit (cfg) adminPasswordHash;
|
||||
reverseProxyAddress = registry.address.${config.networking.hostName}."reverse-proxy";
|
||||
};
|
||||
|
||||
services.resolved.settings.Resolve.DNSStubListener = false;
|
||||
};
|
||||
|
||||
limits = {
|
||||
memory = lib.mkDefault "256M";
|
||||
cpu = lib.mkDefault "100%";
|
||||
tasksMax = lib.mkDefault 128;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -13,6 +13,11 @@ in
|
||||
modules.services.reverse-proxy.enable = true;
|
||||
|
||||
services.nginx.virtualHosts = {
|
||||
"games.alexion.dev" = {
|
||||
forceSSL = true;
|
||||
enableACME = true;
|
||||
locations."/".proxyPass = "http://10.23.20.32";
|
||||
};
|
||||
"git.alexion.dev" = {
|
||||
forceSSL = true;
|
||||
enableACME = true;
|
||||
|
||||
@@ -1,189 +0,0 @@
|
||||
{
|
||||
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";
|
||||
}
|
||||
];
|
||||
|
||||
registry.homepage.${config.networking.hostName}.romm = {
|
||||
tab = "Apps";
|
||||
section = "Media";
|
||||
name = "RomM";
|
||||
description = "Games";
|
||||
icon = "romm.png";
|
||||
};
|
||||
|
||||
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"}
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -15,8 +15,6 @@
|
||||
|
||||
modules.host.cachyos.enable = true;
|
||||
|
||||
services.xserver.xkb.options = "caps:swapescape";
|
||||
|
||||
modules.network.enable = true;
|
||||
|
||||
registry.address.neogaia.self = "10.23.50.146";
|
||||
@@ -40,6 +38,7 @@
|
||||
modules.desktop.enable = true;
|
||||
modules.desktop.apps.firefox.uiScale = "1.25";
|
||||
modules.desktop.apps.obsidian.enable = true;
|
||||
modules.desktop.apps.steam.enable = true;
|
||||
|
||||
registry.waybar.neogaia.battery = {
|
||||
order = 70;
|
||||
|
||||
@@ -29,14 +29,6 @@
|
||||
|
||||
registry.address.pikachu.self = "10.23.10.102";
|
||||
|
||||
guests.adguard = {
|
||||
enable = true;
|
||||
vlan = 20;
|
||||
mac = "bc:24:11:91:60:bf";
|
||||
address = "10.23.20.33";
|
||||
adminPasswordHash = "$2b$12$wariglnrCvJOnh4b8KnB9eDX4RdQRaaTZtyXMUOilaYSn7xTR0g1e";
|
||||
};
|
||||
|
||||
guests.valheim = {
|
||||
enable = true;
|
||||
vlan = 20;
|
||||
@@ -96,27 +88,6 @@
|
||||
};
|
||||
};
|
||||
|
||||
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;
|
||||
clientMaxBodySize = "0";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
guests.reverse-proxy = {
|
||||
enable = true;
|
||||
vlan = 20;
|
||||
|
||||
@@ -44,10 +44,9 @@
|
||||
modules.desktop.enable = true;
|
||||
modules.desktop.apps.obsidian.enable = true;
|
||||
modules.desktop.apps.r2modman.enable = true;
|
||||
modules.desktop.emulation.enable = true;
|
||||
modules.desktop.apps.signal.enable = true;
|
||||
modules.desktop.apps.steam.enable = true;
|
||||
modules.desktop.apps.discord.enable = true;
|
||||
modules.desktop.apps.vencord.enable = true;
|
||||
|
||||
home-manager.users.${config.user.name}.wayland.windowManager.hyprland.settings.monitor = [
|
||||
{
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
util,
|
||||
...
|
||||
}:
|
||||
# Official Discord provides chat, voice, and screen sharing.
|
||||
let
|
||||
user = config.user.name;
|
||||
in
|
||||
{
|
||||
options.modules.desktop.apps.discord.enable = lib.mkEnableOption "official Discord";
|
||||
|
||||
config = lib.mkIf config.modules.desktop.apps.discord.enable {
|
||||
home-manager.users.${user}.home.packages = [ pkgs.discord ];
|
||||
|
||||
registry.window-icon.${config.networking.hostName}.discord = {
|
||||
class = "[Dd]iscord";
|
||||
icon = util.glyph "f1ff";
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -67,8 +67,6 @@ in
|
||||
|
||||
# Stylix uses a near-white selection background.
|
||||
# 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 =
|
||||
let
|
||||
c = hm.config.lib.stylix.colors;
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
|
||||
programs.steam = {
|
||||
enable = true;
|
||||
remotePlay.openFirewall = true;
|
||||
package = pkgs.steam.override {
|
||||
extraEnv.STEAM_FORCE_DESKTOPUI_SCALING = "1.5";
|
||||
};
|
||||
@@ -22,10 +21,9 @@
|
||||
|
||||
hardware.steam-hardware.enable = true;
|
||||
|
||||
programs.gamescope = {
|
||||
enable = true;
|
||||
capSysNice = true;
|
||||
};
|
||||
environment.systemPackages = [
|
||||
pkgs.gamescope
|
||||
];
|
||||
|
||||
registry.window-icon.${config.networking.hostName}.steam = {
|
||||
class = "[Ss]team";
|
||||
|
||||
24
configs/nixos/modules/desktop/apps/vencord.nix
Normal file
24
configs/nixos/modules/desktop/apps/vencord.nix
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
util,
|
||||
...
|
||||
}:
|
||||
# Vesktop, a Discord client with Vencord built in.
|
||||
let
|
||||
user = config.user.name;
|
||||
in
|
||||
{
|
||||
options.modules.desktop.apps.vencord.enable =
|
||||
lib.mkEnableOption "Vesktop, a Discord client with Vencord built in";
|
||||
|
||||
config = lib.mkIf config.modules.desktop.apps.vencord.enable {
|
||||
home-manager.users.${user}.home.packages = [ pkgs.vesktop ];
|
||||
|
||||
registry.window-icon.${config.networking.hostName}.vesktop = {
|
||||
class = "[Vv]esktop";
|
||||
icon = util.glyph "f1ff";
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
# Shared directories for emulators.
|
||||
let
|
||||
user = config.user.name;
|
||||
directory = "/home/${user}/emulation";
|
||||
in
|
||||
{
|
||||
options.modules.desktop.emulation.enable = lib.mkEnableOption "shared emulation directories";
|
||||
|
||||
config = lib.mkIf config.modules.desktop.emulation.enable {
|
||||
modules.desktop.emulation.retroarch = {
|
||||
enable = lib.mkDefault true;
|
||||
browserDirectory = "${directory}/roms";
|
||||
};
|
||||
|
||||
home-manager.users.${user} = hm: {
|
||||
home.file = {
|
||||
".config/retroarch/saves".source = hm.config.lib.file.mkOutOfStoreSymlink "${directory}/saves";
|
||||
".config/retroarch/states".source = hm.config.lib.file.mkOutOfStoreSymlink "${directory}/states";
|
||||
".config/retroarch/system".source = hm.config.lib.file.mkOutOfStoreSymlink "${directory}/bios";
|
||||
};
|
||||
|
||||
systemd.user.tmpfiles.rules = map (path: "d ${path} 0700 - - -") [
|
||||
directory
|
||||
"${directory}/bios"
|
||||
"${directory}/roms"
|
||||
"${directory}/saves"
|
||||
"${directory}/states"
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
util,
|
||||
...
|
||||
}:
|
||||
# RetroArch with the selected PlayStation and Sega Saturn cores.
|
||||
let
|
||||
cfg = config.modules.desktop.emulation.retroarch;
|
||||
user = config.user.name;
|
||||
in
|
||||
{
|
||||
options.modules.desktop.emulation.retroarch = {
|
||||
enable = lib.mkEnableOption "RetroArch with PlayStation and Sega Saturn cores";
|
||||
|
||||
browserDirectory = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "";
|
||||
description = "Directory where RetroArch starts its content browser.";
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
home-manager.users.${user} = {
|
||||
home.packages = [
|
||||
(pkgs.wrapRetroArch {
|
||||
cores = with pkgs.libretro; [
|
||||
beetle-psx-hw
|
||||
beetle-saturn
|
||||
];
|
||||
settings = {
|
||||
savefiles_in_content_dir = "false";
|
||||
savestates_in_content_dir = "false";
|
||||
system_directory = "/home/${user}/.config/retroarch/system";
|
||||
video_fullscreen = "true";
|
||||
sort_savefiles_enable = "true";
|
||||
sort_savestates_enable = "true";
|
||||
cloud_sync_enable = "true";
|
||||
cloud_sync_driver = "webdav";
|
||||
webdav_url = "https://games.alexion.dev/api/sync/retroarch/";
|
||||
cloud_sync_sync_saves = "true";
|
||||
cloud_sync_sync_configs = "true";
|
||||
cloud_sync_sync_thumbs = "false";
|
||||
cloud_sync_sync_system = "false";
|
||||
cloud_sync_sync_mode = "0";
|
||||
cloud_sync_destructive = "false";
|
||||
}
|
||||
// lib.optionalAttrs (cfg.browserDirectory != "") {
|
||||
rgui_browser_directory = cfg.browserDirectory;
|
||||
};
|
||||
})
|
||||
];
|
||||
|
||||
};
|
||||
|
||||
registry.window-icon.${config.networking.hostName}.retroarch = {
|
||||
class = "com.libretro.RetroArch";
|
||||
icon = util.glyph "f11b";
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -37,7 +37,6 @@
|
||||
config = {
|
||||
input = {
|
||||
kb_layout = config.services.xserver.xkb.layout;
|
||||
kb_options = config.services.xserver.xkb.options;
|
||||
repeat_delay = 250;
|
||||
repeat_rate = 45;
|
||||
accel_profile = "flat";
|
||||
|
||||
@@ -1,277 +0,0 @@
|
||||
{ config, lib, ... }:
|
||||
let
|
||||
cfg = config.modules.services.adguardhome;
|
||||
in
|
||||
{
|
||||
options.modules.services.adguardhome = {
|
||||
enable = lib.mkEnableOption "AdGuard Home";
|
||||
|
||||
adminPasswordHash = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "Bcrypt verifier for the AdGuard Home administrator password.";
|
||||
};
|
||||
|
||||
reverseProxyAddress = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "IP address of the guest that handles internal HTTP requests.";
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
services.adguardhome = {
|
||||
enable = true;
|
||||
mutableSettings = false;
|
||||
openFirewall = true;
|
||||
settings = {
|
||||
http = {
|
||||
pprof = {
|
||||
port = 6060;
|
||||
enabled = false;
|
||||
};
|
||||
address = "0.0.0.0:80";
|
||||
session_ttl = "720h";
|
||||
};
|
||||
users = [
|
||||
{
|
||||
name = config.user.name;
|
||||
password = cfg.adminPasswordHash;
|
||||
}
|
||||
];
|
||||
auth_attempts = 5;
|
||||
block_auth_min = 15;
|
||||
http_proxy = "";
|
||||
language = "";
|
||||
theme = "auto";
|
||||
|
||||
dns = {
|
||||
bind_hosts = [ "0.0.0.0" ];
|
||||
port = 53;
|
||||
anonymize_client_ip = false;
|
||||
ratelimit = 20;
|
||||
ratelimit_subnet_len_ipv4 = 24;
|
||||
ratelimit_subnet_len_ipv6 = 56;
|
||||
ratelimit_whitelist = [ ];
|
||||
refuse_any = true;
|
||||
upstream_dns = [
|
||||
"https://dns.cloudflare.com/dns-query"
|
||||
"tls://1.1.1.1"
|
||||
"https://dns.google/dns-query"
|
||||
"tls://8.8.8.8"
|
||||
"https://dns10.quad9.net/dns-query"
|
||||
"tls://9.9.9.9"
|
||||
];
|
||||
upstream_dns_file = "";
|
||||
bootstrap_dns = [
|
||||
"9.9.9.10"
|
||||
"149.112.112.10"
|
||||
"2620:fe::10"
|
||||
"2620:fe::fe:10"
|
||||
];
|
||||
fallback_dns = [ ];
|
||||
upstream_mode = "parallel";
|
||||
fastest_timeout = "1s";
|
||||
allowed_clients = [ ];
|
||||
disallowed_clients = [ ];
|
||||
blocked_hosts = [
|
||||
"version.bind"
|
||||
"id.server"
|
||||
"hostname.bind"
|
||||
];
|
||||
trusted_proxies = [
|
||||
"127.0.0.0/8"
|
||||
"::1/128"
|
||||
];
|
||||
cache_enabled = true;
|
||||
cache_size = 4194304;
|
||||
cache_ttl_min = 0;
|
||||
cache_ttl_max = 0;
|
||||
cache_optimistic = false;
|
||||
cache_optimistic_answer_ttl = "30s";
|
||||
cache_optimistic_max_age = "12h";
|
||||
bogus_nxdomain = [ ];
|
||||
aaaa_disabled = false;
|
||||
enable_dnssec = false;
|
||||
edns_client_subnet = {
|
||||
custom_ip = "";
|
||||
enabled = false;
|
||||
use_custom = false;
|
||||
};
|
||||
max_goroutines = 300;
|
||||
handle_ddr = true;
|
||||
ipset = [ ];
|
||||
ipset_file = "";
|
||||
bootstrap_prefer_ipv6 = false;
|
||||
upstream_timeout = "10s";
|
||||
private_networks = [ ];
|
||||
use_private_ptr_resolvers = true;
|
||||
local_ptr_upstreams = [ ];
|
||||
use_dns64 = false;
|
||||
dns64_prefixes = [ ];
|
||||
serve_http3 = false;
|
||||
use_http3_upstreams = false;
|
||||
serve_plain_dns = true;
|
||||
hostsfile_enabled = true;
|
||||
pending_requests.enabled = true;
|
||||
};
|
||||
|
||||
tls = {
|
||||
enabled = false;
|
||||
server_name = "";
|
||||
force_https = false;
|
||||
port_https = 443;
|
||||
port_dns_over_tls = 853;
|
||||
port_dns_over_quic = 853;
|
||||
port_dnscrypt = 0;
|
||||
dnscrypt_config_file = "";
|
||||
allow_unencrypted_doh = false;
|
||||
certificate_chain = "";
|
||||
private_key = "";
|
||||
certificate_path = "";
|
||||
private_key_path = "";
|
||||
strict_sni_check = false;
|
||||
};
|
||||
|
||||
querylog = {
|
||||
dir_path = "";
|
||||
ignored = [ ];
|
||||
interval = "2160h";
|
||||
size_memory = 1000;
|
||||
enabled = true;
|
||||
ignored_enabled = false;
|
||||
file_enabled = true;
|
||||
};
|
||||
|
||||
statistics = {
|
||||
dir_path = "";
|
||||
ignored = [ ];
|
||||
interval = "24h";
|
||||
enabled = true;
|
||||
ignored_enabled = false;
|
||||
};
|
||||
|
||||
filters = [
|
||||
{
|
||||
enabled = true;
|
||||
url = "https://adguardteam.github.io/HostlistsRegistry/assets/filter_1.txt";
|
||||
name = "AdGuard DNS filter";
|
||||
id = 1;
|
||||
}
|
||||
{
|
||||
enabled = false;
|
||||
url = "https://adguardteam.github.io/HostlistsRegistry/assets/filter_2.txt";
|
||||
name = "AdAway Default Blocklist";
|
||||
id = 2;
|
||||
}
|
||||
];
|
||||
whitelist_filters = [ ];
|
||||
user_rules = [ ];
|
||||
|
||||
dhcp = {
|
||||
enabled = false;
|
||||
interface_name = "";
|
||||
local_domain_name = "lan";
|
||||
dhcpv4 = {
|
||||
gateway_ip = "";
|
||||
subnet_mask = "";
|
||||
range_start = "";
|
||||
range_end = "";
|
||||
lease_duration = 86400;
|
||||
icmp_timeout_msec = 1000;
|
||||
options = [ ];
|
||||
};
|
||||
dhcpv6 = {
|
||||
range_start = "";
|
||||
lease_duration = 86400;
|
||||
ra_slaac_only = false;
|
||||
ra_allow_slaac = false;
|
||||
};
|
||||
};
|
||||
|
||||
filtering = {
|
||||
blocking_ipv4 = "";
|
||||
blocking_ipv6 = "";
|
||||
blocked_services = {
|
||||
schedule.time_zone = "Local";
|
||||
ids = [ ];
|
||||
};
|
||||
protection_disabled_until = null;
|
||||
safe_search = {
|
||||
enabled = false;
|
||||
bing = true;
|
||||
duckduckgo = true;
|
||||
ecosia = true;
|
||||
google = true;
|
||||
pixabay = true;
|
||||
yandex = true;
|
||||
youtube = true;
|
||||
};
|
||||
blocking_mode = "default";
|
||||
parental_block_host = "family-block.dns.adguard.com";
|
||||
safebrowsing_block_host = "standard-block.dns.adguard.com";
|
||||
rewrites = [
|
||||
{
|
||||
domain = "*.home";
|
||||
answer = "10.23.20.32";
|
||||
enabled = true;
|
||||
}
|
||||
{
|
||||
domain = "*.alexion.dev";
|
||||
answer = cfg.reverseProxyAddress;
|
||||
enabled = true;
|
||||
}
|
||||
{
|
||||
domain = "home";
|
||||
answer = cfg.reverseProxyAddress;
|
||||
enabled = true;
|
||||
}
|
||||
];
|
||||
safe_fs_patterns = [ "/opt/AdGuardHome/userfilters/*" ];
|
||||
safebrowsing_cache_size = 1048576;
|
||||
safesearch_cache_size = 1048576;
|
||||
parental_cache_size = 1048576;
|
||||
cache_time = 30;
|
||||
filters_update_interval = 24;
|
||||
blocked_response_ttl = 10;
|
||||
filtering_enabled = true;
|
||||
rewrites_enabled = true;
|
||||
parental_enabled = false;
|
||||
safebrowsing_enabled = false;
|
||||
protection_enabled = true;
|
||||
};
|
||||
|
||||
clients = {
|
||||
runtime_sources = {
|
||||
whois = true;
|
||||
arp = true;
|
||||
rdns = true;
|
||||
dhcp = true;
|
||||
hosts = true;
|
||||
};
|
||||
persistent = [ ];
|
||||
};
|
||||
|
||||
log = {
|
||||
enabled = true;
|
||||
file = "";
|
||||
max_backups = 0;
|
||||
max_size = 100;
|
||||
max_age = 3;
|
||||
compress = false;
|
||||
local_time = false;
|
||||
verbose = false;
|
||||
};
|
||||
|
||||
os = {
|
||||
group = "";
|
||||
user = "";
|
||||
rlimit_nofile = 0;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
networking.firewall = {
|
||||
allowedTCPPorts = [ 53 ];
|
||||
allowedUDPPorts = [ 53 ];
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -71,10 +71,7 @@ let
|
||||
extraConfig = lib.optionalString (route.clientMaxBodySize != null) ''
|
||||
client_max_body_size ${route.clientMaxBodySize};
|
||||
'';
|
||||
locations."/" = {
|
||||
proxyPass = mkBackend address port;
|
||||
proxyWebsockets = true;
|
||||
};
|
||||
locations."/".proxyPass = mkBackend address port;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
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;
|
||||
package = pkgs.alexion.romm;
|
||||
group = "storage";
|
||||
database.createLocally = true;
|
||||
redis.createLocally = true;
|
||||
watcher.enable = true;
|
||||
extraEnvironment = {
|
||||
HASHEOUS_API_ENABLED = "true";
|
||||
PLAYMATCH_API_ENABLED = "true";
|
||||
ROM_CONVERTO_ENABLED = "false";
|
||||
};
|
||||
inherit (cfg) environmentFile;
|
||||
};
|
||||
|
||||
systemd.services.romm-worker.serviceConfig.ExecStart =
|
||||
lib.mkForce "${lib.getExe' config.services.romm.package "romm-worker"} scans";
|
||||
|
||||
systemd.tmpfiles.settings."10-romm"."${config.services.romm.dataDir}/config/config.yml"."L+".argument =
|
||||
toString (
|
||||
(pkgs.formats.yaml { }).generate "romm-config.yml" {
|
||||
emulatorjs.auto_save_sync = false;
|
||||
filesystem.structure = {
|
||||
default = "roms/{platform}/{game}";
|
||||
firmware = "bios/{platform}";
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
users.users = {
|
||||
romm.uid = config.registry."stable-uid".${config.networking.hostName}.romm;
|
||||
redis-romm.uid = config.registry."stable-uid".${config.networking.hostName}."redis-romm";
|
||||
};
|
||||
})
|
||||
];
|
||||
}
|
||||
@@ -47,8 +47,6 @@ or begin
|
||||
return $status
|
||||
end
|
||||
|
||||
set remote_nar
|
||||
|
||||
if test (count $missing_paths) -gt 0
|
||||
set nar_file (mktemp --suffix=.nar)
|
||||
or return $status
|
||||
@@ -72,23 +70,26 @@ if test (count $missing_paths) -gt 0
|
||||
return $scp_status
|
||||
end
|
||||
|
||||
echo "deploy-to: importing the closure, recording $system_path, and activating it on $host" >&2
|
||||
echo "deploy-to: importing closure on $host" >&2
|
||||
ssh -t $host "sudo sh -c 'nix-store --import < $remote_nar' && rm -f $remote_nar"
|
||||
or begin
|
||||
echo "deploy-to: closure import failed for $host" >&2
|
||||
return $status
|
||||
end
|
||||
else
|
||||
echo "deploy-to: $host already has the full closure for $system_path" >&2
|
||||
echo "deploy-to: recording $system_path and activating it on $host" >&2
|
||||
end
|
||||
|
||||
set remote_script (
|
||||
string join '; ' \
|
||||
'system_path=$1' \
|
||||
'remote_nar=$2' \
|
||||
'if test -n "$remote_nar"; then nix-store --import < "$remote_nar"; rm -f "$remote_nar"; fi' \
|
||||
'nix-env --profile /nix/var/nix/profiles/system --set "$system_path"' \
|
||||
'"$system_path/bin/switch-to-configuration" switch'
|
||||
)
|
||||
|
||||
ssh -t $host sudo sh -e -c (string escape -- $remote_script) sh (string escape -- $system_path $remote_nar)
|
||||
echo "deploy-to: recording $system_path as the system profile on $host" >&2
|
||||
ssh -t $host sudo nix-env --profile /nix/var/nix/profiles/system --set $system_path
|
||||
or begin
|
||||
echo "deploy-to: deployment failed for $host" >&2
|
||||
echo "deploy-to: system profile update failed for $host" >&2
|
||||
return $status
|
||||
end
|
||||
|
||||
echo "deploy-to: activating $system_path on $host" >&2
|
||||
ssh -t $host sudo "$system_path/bin/switch-to-configuration" switch
|
||||
or begin
|
||||
echo "deploy-to: activation failed for $host" >&2
|
||||
return $status
|
||||
end
|
||||
|
||||
@@ -24,9 +24,6 @@ in
|
||||
home-manager.users.${user} = hm: {
|
||||
programs.nixvim = {
|
||||
enable = true;
|
||||
|
||||
# Aligns Nixvim plugins with this flake's `Nixpkgs`.
|
||||
# `nixpkgs.useGlobalPackages` prevents Home Manager from creating a separate package set.
|
||||
nixpkgs.useGlobalPackages = true;
|
||||
|
||||
extraPackages = with pkgs; [
|
||||
|
||||
@@ -12,7 +12,7 @@ let
|
||||
|
||||
in
|
||||
{
|
||||
# Next stable UID: 20007
|
||||
# Next stable UID: 20005
|
||||
|
||||
type = lib.types.ints.positive;
|
||||
|
||||
|
||||
@@ -6,17 +6,6 @@ services:
|
||||
lastfm:
|
||||
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]
|
||||
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:
|
||||
age:
|
||||
- enc: |
|
||||
@@ -37,7 +26,7 @@ sops:
|
||||
dyjzy0/9Ui1IIXH36AImO5hiEZcrGePV2qPLLQc7ZLngC1jlJuEKJA==
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
recipient: age1wf5s0n0tgt6ld2ysgu9dc67mj8ylwecgl4utzg7hqwy3kut9zyms7aglmh
|
||||
lastmodified: "2026-10-20T01:27:06Z"
|
||||
mac: ENC[AES256_GCM,data:NCt6e/ctZwaBADfrhu1RXtILlClbHEqOAnIu8rE/zMPSwMIPB/kTtfd/ujf+1iRszoHaX4Se1xc0wz1th6NVToFVNarxBBgTk4JMUU8gdMweOjmwlh3GXU3ZcYY/xAXxWF1hxyXSEW78j3S2844H9XXENAq/Bf3mjdCYQ/qr/d4=,iv:4nFyBSmRvF4zSAwvL+xccMwtWrVbHtFKNoTb7f3rSH4=,tag:eCV/7P0TjyMZXKcWBUPXQA==,type:str]
|
||||
lastmodified: "2026-09-12T22:54:25Z"
|
||||
mac: ENC[AES256_GCM,data:4R3a0ytf4i8aM8iBQ9vRC/q+hvtjLrCTNgbpCB5DbqBMBYmhtGsBmO/0i1yT0N4tydyLaGOMkNfuuuVi3ASLEOKnViy9w3uD9IN0F79W4f2vguWUVIp/T8skvN9wbV9o9xd0LkJzmeVVgzb1H6HKw+rTGjbquleoIHEBDcDDM+k=,iv:0NlckMXlS+idY6UGBF+F0KFLt+pzD7Q/YEQOdXraAYg=,tag:JBBQduV8uhnKz9gPf6HfAQ==,type:str]
|
||||
unencrypted_suffix: _unencrypted
|
||||
version: 3.13.3
|
||||
|
||||
2
docs/.obsidian/app.json
vendored
2
docs/.obsidian/app.json
vendored
@@ -5,7 +5,7 @@
|
||||
"livePreview": false,
|
||||
"readableLineLength": false,
|
||||
"strictLineBreaks": true,
|
||||
"propertiesInDocument": "hidden",
|
||||
"propertiesInDocument": "visible",
|
||||
"showLineNumber": true,
|
||||
"autoPairMarkdown": false,
|
||||
"useTab": false,
|
||||
|
||||
62
docs/.obsidian/workspace.json
vendored
62
docs/.obsidian/workspace.json
vendored
@@ -4,21 +4,21 @@
|
||||
"type": "split",
|
||||
"children": [
|
||||
{
|
||||
"id": "880908ac22302761",
|
||||
"id": "81c4860f495113bf",
|
||||
"type": "tabs",
|
||||
"children": [
|
||||
{
|
||||
"id": "10ea163afc7dbc62",
|
||||
"id": "daab69fc3faab88f",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "markdown",
|
||||
"state": {
|
||||
"file": "TODO.md",
|
||||
"file": "00 INBOX/credits.md",
|
||||
"mode": "source",
|
||||
"source": true
|
||||
},
|
||||
"icon": "lucide-file",
|
||||
"title": "TODO"
|
||||
"title": "credits"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -91,7 +91,7 @@
|
||||
"state": {
|
||||
"type": "localgraph",
|
||||
"state": {
|
||||
"file": "TODO.md",
|
||||
"file": "00 INBOX/credits.md",
|
||||
"options": {
|
||||
"collapse-filter": true,
|
||||
"search": "",
|
||||
@@ -217,34 +217,34 @@
|
||||
"table-editor-obsidian:Advanced Tables Toolbar": false
|
||||
}
|
||||
},
|
||||
"active": "10ea163afc7dbc62",
|
||||
"active": "daab69fc3faab88f",
|
||||
"lastOpenFiles": [
|
||||
"00 INBOX/ai/adguard-pikachu-migration-design-tree.md",
|
||||
"00 INBOX/ai/caps-escape-design.md",
|
||||
"03 wrk/tasks/0016-romm-manual-rom-migration.md",
|
||||
"03 wrk/tasks/0015-romm-5-4-package.md",
|
||||
"00 INBOX/ai/retroarch-zeus-design-tree.md",
|
||||
"TODO.md",
|
||||
"00 INBOX/ai/romm-5-4-deployment-changes-design-tree.md",
|
||||
"00 INBOX/ai/romm-migration-design-tree.md",
|
||||
"00 INBOX/ai/romm-5-4-package-design-tree.md",
|
||||
"00 INBOX/ai/steamvr-quest-3-design-tree.md",
|
||||
"AGENTS.md",
|
||||
"03 wrk/homelab.md",
|
||||
"00 INBOX/ai/nixos-agents-style-scope-design-tree.md",
|
||||
"03 wrk/tasks/0014-romm-guest.md",
|
||||
"00 INBOX/ai/tldr-design-tree.md",
|
||||
"00 INBOX/ai/tldr-design.md",
|
||||
"00 INBOX/ai/valheim-items.md",
|
||||
"03 wrk/tasks/0013-oidc-provider.md",
|
||||
"03 wrk/tasks/0002-nixos-refactor.md",
|
||||
"03 wrk/tasks/0011-gitea-axi-rust-migration.md",
|
||||
"00 INBOX/credits.md",
|
||||
"00 INBOX/ai/grill-skill-design.md",
|
||||
"03 wrk/tasks/0012-gitea-axi-pr-merge-guardrail.md",
|
||||
"00 INBOX/ai/gitea-axi-pr-merge-guardrail.md",
|
||||
"00 INBOX/ai/gitea-axi-rust-migration.md",
|
||||
"00 INBOX/valheim progression.md",
|
||||
"03 wrk/tasks/0010-fix-main-monitor-detection.md",
|
||||
"00 INBOX/credits.md",
|
||||
"03 wrk/tasks/0009-bazel-project-scaffolding.md",
|
||||
"03 wrk/tasks/0005-rust-cli-scaffolding.md",
|
||||
"03 wrk/tasks/0008-vim-obsidian-markdown-renderer.md",
|
||||
"03 wrk/tasks/0002-nixos-refactor.md",
|
||||
"TODO.md",
|
||||
"technical-writing.md",
|
||||
"dictionary.md",
|
||||
"03 wrk/tasks/0007-obsidian-source-notes.md",
|
||||
"03 wrk/tasks/0004-dictionary-cli.md",
|
||||
"03 wrk/tasks/0006-wallpaper-randomizer.md",
|
||||
"03 wrk/tasks/0003-whisper-dictation.md",
|
||||
"03 wrk/monorepo.md",
|
||||
"03 wrk/tasks/0001-todo-cli.md",
|
||||
"03 wrk/productivity.md",
|
||||
"99 meta/templates/task.md",
|
||||
"03 wrk/dotfiles.md",
|
||||
"03 wrk/machoke.md",
|
||||
"03 wrk/falinks.md",
|
||||
"03 wrk/ariadne.md",
|
||||
"03 wrk/tasks/0005-modules-services-navidrome.md",
|
||||
"03 wrk/tasks/0003-registry-homepage.md",
|
||||
"03 wrk/tasks/0039-hosts-zeus-hardware-configuration.md",
|
||||
"03 wrk/tasks/0030-dotfiles-hosts.md",
|
||||
"03 wrk/tasks",
|
||||
"00 INBOX/let order name resolution.md~",
|
||||
"00 INBOX/nix name resolution.md~",
|
||||
|
||||
@@ -1,110 +0,0 @@
|
||||
# AdGuard migration to Pikachu: design tree
|
||||
|
||||
## Research facts
|
||||
|
||||
- The pinned Nixpkgs revision provides `adguardhome` version `0.107.79`.
|
||||
- NixOS provides the `services.adguardhome` module.
|
||||
- The module runs AdGuard Home as a hardened dynamic systemd user.
|
||||
- It stores state in `/var/lib/AdGuardHome`.
|
||||
- It grants `CAP_NET_BIND_SERVICE` for DNS port 53.
|
||||
- It grants `CAP_NET_RAW` when AdGuard Home DHCP is enabled.
|
||||
- The module supports an arbitrary YAML configuration through `settings`.
|
||||
- The module supports fully declarative operation with `mutableSettings = false`.
|
||||
- In fully declarative operation, the module replaces `/var/lib/AdGuardHome/AdGuardHome.yaml` on each service start.
|
||||
- Fully declarative operation requires `dns.bootstrap_dns` as a list.
|
||||
- Declarative settings pass an AdGuard configuration check during the build.
|
||||
- `mutableSettings = true` merges declared settings into UI changes at service start.
|
||||
- `mutableSettings = false` replaces the state configuration on every service start.
|
||||
- `openFirewall` opens only the web interface port.
|
||||
- The migration must explicitly open DNS ports 53/TCP and 53/UDP when clients need them.
|
||||
- Pikachu has address `10.23.10.102` on its untagged network.
|
||||
- Pikachu carries VLAN 20 and creates bridge `br-vlan20`.
|
||||
- The guest framework runs each guest in its own systemd-nspawn container.
|
||||
- Each guest attaches to a host VLAN bridge and receives an address through DHCP.
|
||||
- Pikachu currently configures only VLAN 20 for guests.
|
||||
- The repository has no AdGuard configuration, Proxmox inventory, or DHCP-provider configuration.
|
||||
- The current DHCP provider remains unestablished.
|
||||
- The current Proxmox AdGuard container uses MAC `bc:24:11:91:60:bf`.
|
||||
- The current Proxmox AdGuard container uses address `10.23.20.33` on VLAN 20.
|
||||
- This environment has no Proxmox SSH target or local Proxmox control utility.
|
||||
- The current AdGuard configuration listens on all guest addresses at DNS port 53.
|
||||
- The current AdGuard configuration has no allowed-client or disallowed-client restrictions.
|
||||
- The current AdGuard configuration uses Cloudflare, Google, and Quad9 encrypted upstreams in parallel.
|
||||
- The current AdGuard configuration disables DNSSEC.
|
||||
- The current AdGuard configuration defines three local rewrites.
|
||||
- The repository configures no host backup system or monitoring system.
|
||||
- Hosts update through explicit `nixos-rebuild switch --flake` commands.
|
||||
- The working tree has unrelated operator changes.
|
||||
|
||||
## Service tradeoffs
|
||||
|
||||
| Service | NixOS support | Domain filtering | Administration UI | DHCP | Encrypted DNS | Main tradeoff |
|
||||
| :--- | :--- | :--- | :--- | :--- | :--- | :--- |
|
||||
| AdGuard Home | `services.adguardhome`; checked YAML and mutable or immutable state | Native block lists, rules, and per-client policy | Native | Native | Upstream and client-facing encrypted DNS | Direct migration with the closest state and feature match. UI state conflicts with fully declarative operation unless configuration is immutable. |
|
||||
| Blocky | `services.blocky`; checked declarative YAML | Native lists, allow lists, rewrites, and per-client groups | No UI; REST API and metrics only | No | Encrypted upstreams and client-facing DoH, DoT, and DoQ | Strong Git-managed configuration and low operational surface. It lacks an interactive administration interface and DHCP. |
|
||||
| Pi-hole | `services.pihole-ftl` plus `services.pihole-web`; declarative TOML, lists, and dashboard | Native gravity lists, allow lists, regex rules, and groups | Native | Native | Use a companion for encrypted upstream DNS | Familiar filtering product with a dashboard. The migration changes product data and uses more coupled services. |
|
||||
| Technitium DNS Server | `services.technitium-dns-server`; service and firewall options only | Native block lists and DNS applications | Native | Native | Upstream and client-facing encrypted DNS | Broadest DNS feature set, including authoritative DNS. NixOS does not expose its runtime configuration declaratively. |
|
||||
| Unbound | `services.unbound`; structured server and free-form settings | Local zones or response-policy zones require custom policy management | No | No | DNS-over-TLS support | Mature validating recursive resolver. Filtering and user operations require separate design. |
|
||||
| dnscrypt-proxy | `services.dnscrypt-proxy`; declarative TOML | Local block and allow rules, without a list-management UI | No | No | Encrypted upstream DNS, including DNSCrypt and DoH | Strong upstream privacy and resolver selection. It is not a full network filtering appliance. |
|
||||
| Knot Resolver | `services.kresd`; structured listener settings and extra Lua | Custom policy modules | No | No | Client-facing DoH and TLS listeners | Flexible high-performance recursive resolver. Filtering requires resolver expertise and custom policy. |
|
||||
| SmartDNS | `services.smartdns`; free-form settings | Domain and address rules | No NixOS-integrated UI | No | Encrypted upstream and client-facing DNS | Optimizes upstream selection and latency. It offers less complete filtering management than dedicated blockers. |
|
||||
|
||||
`Native` means that the product provides the feature without a companion service.
|
||||
|
||||
## Decision tree
|
||||
|
||||
- **D1 — DNS service product** [settled]
|
||||
- Retain AdGuard Home on Pikachu.
|
||||
- Constraint: Preserve AdGuard Home as the DNS service product.
|
||||
- **D2A — AdGuard Home role** [settled]
|
||||
- Provides DNS filtering and resolution only.
|
||||
- Constraint: The existing DHCP provider continues to assign addresses and DHCP options.
|
||||
- **D3A — Execution placement** [settled]
|
||||
- Runs in a dedicated guest on Pikachu's existing VLAN 20.
|
||||
- Constraint: Isolate AdGuard Home from the Pikachu host.
|
||||
- **D4A — Guest network identity** [settled]
|
||||
- Reuses MAC `bc:24:11:91:60:bf` and address `10.23.20.33` on VLAN 20.
|
||||
- Constraint: Stop the Proxmox container before the guest starts.
|
||||
- **D5A — Network exposure** [settled]
|
||||
- Listens on all guest addresses at port 53.
|
||||
- Accepts requests from every reachable internal device.
|
||||
- **D6A — Resolver policy** [settled]
|
||||
- Preserves the existing upstream resolvers and parallel resolver mode.
|
||||
- Preserves disabled DNSSEC and the three local rewrites.
|
||||
- **D7A — Configuration authority** [settled]
|
||||
- Uses fully declarative Nix configuration.
|
||||
- Constraint: Set `services.adguardhome.mutableSettings = false`.
|
||||
- Constraint: Declare `dns.bootstrap_dns` as a list.
|
||||
- Constraint: Web UI changes do not persist.
|
||||
- Constraint: Match existing service behavior except for the administrator password.
|
||||
- **D8A — Migration data** [settled]
|
||||
- Migrate functional DNS configuration only.
|
||||
- Constraint: Do not migrate query history or statistics.
|
||||
- **D9A — Operations** [settled]
|
||||
- Declares a newly generated bcrypt verifier in Nix.
|
||||
- Constraint: Keep the plaintext password outside the repository and Nix store.
|
||||
- Constraint: The verifier is readable from the Nix store.
|
||||
- **D10A — Cutover and rollback** [settled]
|
||||
- Uses a planned atomic cutover with rollback.
|
||||
- Stop the Proxmox container before starting the guest.
|
||||
- Validate the guest before restoring the Proxmox container during rollback.
|
||||
- **D11A — Runtime-state backup** [settled]
|
||||
- Do not back up rebuildable AdGuard Home runtime state.
|
||||
- **D12A — Monitoring** [settled]
|
||||
- Do not add DNS availability monitoring.
|
||||
- **D13A — Upgrade ownership** [settled]
|
||||
- The normal flake update and explicit NixOS rebuild workflow owns upgrades.
|
||||
- Replace AdGuard Home. [not selected]
|
||||
- **D2B — Replacement product** [not applicable]
|
||||
- Choose Blocky, Pi-hole, Technitium, or a composed resolver.
|
||||
|
||||
## Frontier
|
||||
|
||||
None.
|
||||
|
||||
## Sources
|
||||
|
||||
- Pinned Nixpkgs module: `nixos/modules/services/networking/adguardhome.nix`.
|
||||
- Pinned Nixpkgs modules: `blocky.nix`, `pihole-ftl.nix`, `pihole-web.nix`, `technitium-dns-server.nix`, `unbound.nix`, `dnscrypt-proxy.nix`, `knot-resolver.nix`, and `smartdns.nix`.
|
||||
- AdGuard Home README, release `v0.107.79`.
|
||||
- Technitium DNS Server README, current upstream.
|
||||
@@ -1,130 +0,0 @@
|
||||
# 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.
|
||||
@@ -1,98 +0,0 @@
|
||||
---
|
||||
date-created: 2026-10-21 08:47
|
||||
date-modified: 2026-10-21 09:26
|
||||
---
|
||||
# RetroArch on Zeus design tree
|
||||
|
||||
## Facts
|
||||
|
||||
- Zeus is an `x86_64-linux` desktop with AMD graphics, 32-bit graphics support, Bluetooth, Hyprland, and Home Manager.
|
||||
- Nixpkgs provides `pkgs.retroarch.withCores` for an explicit set of Libretro cores.
|
||||
- Nixpkgs provides `pkgs.retroarch-free` and `pkgs.retroarch-full` for broad core sets.
|
||||
- The packaged RetroArch version is 1.22.2 and includes Cloud Sync on Linux.
|
||||
- Linux Cloud Sync uses a WebDAV endpoint.
|
||||
- RomM requires RetroArch 1.22 or newer for this integration.
|
||||
- RomM documents configuration synchronization as part of its Cloud Sync setup.
|
||||
- Cloud Sync supports saves, configuration, thumbnails, and system files as separate categories.
|
||||
- RomM matches a save or state to a ROM by its file name without the extension.
|
||||
- Local ROM files must use the same names as their RomM library records.
|
||||
- RomM 5.4 provides a WebDAV endpoint for RetroArch Cloud Sync.
|
||||
- The endpoint is `https://games.alexion.dev/api/sync/retroarch/`.
|
||||
- RomM stores its ROMs and BIOS files on Pikachu.
|
||||
|
||||
## Constraints
|
||||
|
||||
- Install RetroArch on Zeus.
|
||||
- Start with PlayStation and Sega Saturn cores.
|
||||
- Evaluate RetroArch Cloud Sync.
|
||||
- Keep package selection declarative.
|
||||
- Keep credentials out of the Nix store and version control.
|
||||
|
||||
## Decision tree
|
||||
|
||||
- D1 — Initial core selection [settled]
|
||||
- Use `beetle-psx-hw` for PlayStation.
|
||||
- Use `beetle-saturn` for Sega Saturn.
|
||||
- D4 — Core package viability [settled]
|
||||
- Both core packages support `x86_64-linux`.
|
||||
- Neither package is marked broken or unfree.
|
||||
- D5 — Package composition [settled]
|
||||
- Build a RetroArch wrapper with `beetle-psx-hw` and `beetle-saturn` only.
|
||||
- D2 — Cloud Sync data scope [settled]
|
||||
- Sync saves, states, and configuration.
|
||||
- Do not sync thumbnails or system files.
|
||||
- D6 — Sync endpoint [settled]
|
||||
- Use RomM's RetroArch WebDAV endpoint.
|
||||
- D7 — Sync identity [settled]
|
||||
- Use the existing RomM account.
|
||||
- D15 — Credential injection [settled]
|
||||
- Store the RomM password in RetroArch's writable user configuration.
|
||||
- D8 — Synchronization mode [settled]
|
||||
- Synchronize automatically at startup and after play sessions.
|
||||
- D16 — Launch order [settled]
|
||||
- Do not add a game-launching frontend.
|
||||
- Start RetroArch before loading local ROM content.
|
||||
- D9 — Conflict and destructive-sync policy [settled]
|
||||
- Use non-destructive synchronization.
|
||||
- RetroArch backs up a local file when RomM reports its deletion.
|
||||
- D3 — ROM and firmware access [settled]
|
||||
- Keep ROMs and BIOS files locally on Zeus.
|
||||
- D10 — Mount or replication method [settled]
|
||||
- Do not configure a remote mount or replication method.
|
||||
- D11 — RetroArch directory settings [settled]
|
||||
- Use `~/.local/share/retroarch/`.
|
||||
- Use `roms/`, `system/`, `saves/`, and `states/` subdirectories.
|
||||
- Keep saves and states outside the content directory.
|
||||
- Sort saves and states into per-core folders.
|
||||
- D12 — User-configuration policy [settled]
|
||||
- Nix declares the package, cores, and directory defaults.
|
||||
- RetroArch manages and synchronizes its writable configuration.
|
||||
- D13 — User-facing integration [settled]
|
||||
- Do not add a user-facing integration beyond RetroArch's desktop entry.
|
||||
- D14 — Validation [settled]
|
||||
- Verify the NixOS evaluation and a manual Cloud Sync round trip.
|
||||
|
||||
## Frontier
|
||||
|
||||
No decisions remain open.
|
||||
|
||||
## Decision log
|
||||
|
||||
- D13: Do not add a user-facing integration beyond RetroArch's desktop entry.
|
||||
- D16: Do not add a game-launching frontend. Start RetroArch before loading local ROM content.
|
||||
- D14: Verify the NixOS evaluation and a manual Cloud Sync round trip.
|
||||
- D12: Nix declares the package, cores, and directory defaults. RetroArch manages and synchronizes its writable configuration.
|
||||
- D11: Use `~/.local/share/retroarch/` with `roms/`, `system/`, `saves/`, and `states/` subdirectories.
|
||||
- D9: Use non-destructive synchronization.
|
||||
- D8: Synchronize automatically at startup and after play sessions.
|
||||
- D15: Store the RomM password in RetroArch's writable user configuration.
|
||||
- D5: Build a RetroArch wrapper with `beetle-psx-hw` and `beetle-saturn` only.
|
||||
- D3: Keep ROMs and BIOS files locally on Zeus.
|
||||
- D10: Do not configure a remote mount or replication method.
|
||||
- D7: Use the existing RomM account.
|
||||
- D7 note: RomM accepts API client tokens as Bearer authentication. RetroArch Cloud Sync only supports WebDAV Basic authentication.
|
||||
- D6: Use RomM's RetroArch WebDAV endpoint.
|
||||
- D2: Sync saves, states, and configuration. Do not sync thumbnails or system files.
|
||||
- D11: Keep saves and states outside the content directory. Sort them into per-core folders.
|
||||
- D1: Use `beetle-psx-hw` for PlayStation and `beetle-saturn` for Sega Saturn.
|
||||
- D4: Both selected core packages support Zeus and have no broken or unfree package status.
|
||||
@@ -1,107 +0,0 @@
|
||||
---
|
||||
date-created: 2026-10-20 19:15
|
||||
date-modified: 2026-10-20 19:20
|
||||
---
|
||||
# RomM 5.4 deployment changes
|
||||
|
||||
## Known facts
|
||||
|
||||
- The deployed Nixpkgs package is RomM 5.2.0.
|
||||
- RomM 5.4.0 requires Python 3.14 or later.
|
||||
- The pinned Nixpkgs contains Python 3.14.
|
||||
- The pinned Python package set lacks some RomM 5.4.0 dependency versions.
|
||||
- For example, it provides RQ 2.10 while RomM requires RQ 2.12 or later.
|
||||
- In this design, upstream means the Nixpkgs repository.
|
||||
- Nixpkgs upstream still packages RomM 5.2.0.
|
||||
- Two open Nixpkgs pull requests update RomM to 5.4.0.
|
||||
- Pull request 574089 adds a required compatibility patch for Nixpkgs Redis 8.0.1.
|
||||
- Pull request 574089 has not run the NixOS module test.
|
||||
- The existing `romm` derivation already uses Python 3.14 from the pin.
|
||||
- The package can override the current derivation without copying it.
|
||||
- The override evaluates successfully with the RomM 5.4.0 source and frontend hashes.
|
||||
- RomM reads `config.yml` from `/var/lib/romm/config/config.yml`.
|
||||
- Systemd tmpfiles `L+` replaces that path with a generated Nix-store symlink.
|
||||
- NixOS modules use `L+` symlinks for declaratively managed files in persistent application directories.
|
||||
- The existing Nixpkgs RomM NixOS test starts the application with PostgreSQL, Redis, nginx, and the filesystem watcher in a local VM.
|
||||
- The test does not deploy to Pikachu.
|
||||
- The automated update changes only the version, source hash, and frontend dependency hash.
|
||||
- The manual update also removes a Redis maintenance-notification import and argument.
|
||||
- RomM does not require the `rom-converto` executable while `ROM_CONVERTO_ENABLED` is false.
|
||||
- As checked on 2026-10-20, both pull requests remain open.
|
||||
- Neither pull request has a review or a discussion comment.
|
||||
- Both pull requests request review from the RomM package maintainers.
|
||||
- Both pull requests pass evaluation and lint checks.
|
||||
- Their package-test checks have a neutral result.
|
||||
- The Darwin package-test jobs remain queued.
|
||||
- RomM 5.3.0 added a required `filesystem.structure` declaration.
|
||||
- RomM will not start without that declaration.
|
||||
- The existing mounts match Structure A.
|
||||
- Structure A maps ROMs to `roms/{platform}/{game}`.
|
||||
- Structure A maps firmware to `bios/{platform}`.
|
||||
- RomM 5.4.0 enables `emulatorjs.auto_save_sync` by default.
|
||||
- RomM 5.4.0 adds optional in-place library conversion through `rom-converto`.
|
||||
- Library conversion requires `ROM_CONVERTO_ENABLED=true`.
|
||||
- Conversion deletes original files after conversion.
|
||||
- The task requires declarative `config.yml` management.
|
||||
- The task requires disabled browser save synchronization.
|
||||
- The task requires disabled library conversion.
|
||||
- The task retains the IGDB, ScreenScraper, and SteamGridDB configuration.
|
||||
- The task reuses the Nixpkgs `services.romm` module.
|
||||
|
||||
## Sources
|
||||
|
||||
- [RomM 5.3.0 release](https://github.com/rommapp/romm/releases/tag/5.3.0)
|
||||
- [RomM 5.4.0 release](https://github.com/rommapp/romm/releases/tag/5.4.0)
|
||||
- [RomM 5.4.0 configuration example](https://github.com/rommapp/romm/blob/5.4.0/examples/config.example.yml)
|
||||
- [RomM 5.4.0 Python requirement](https://github.com/rommapp/romm/blob/5.4.0/pyproject.toml)
|
||||
- [[0015-romm-5-4-package]]
|
||||
|
||||
## Decision tree
|
||||
|
||||
- RomM 5.4 deployment
|
||||
- Package runtime
|
||||
- Settled: Build the local package with Python 3.14.
|
||||
- Package source
|
||||
- Settled: Maintain a local package.
|
||||
- Constraint: Do not rely on either unreviewed Nixpkgs pull request.
|
||||
- Package exposure
|
||||
- Settled: Expose the package as `pkgs.alexion.romm`.
|
||||
- Library layout
|
||||
- Settled: Manage `config.yml` declaratively with Nix.
|
||||
- Configuration materialization
|
||||
- Settled: Replace the configuration file with a Nix-store symlink on activation.
|
||||
- Settled: Declare Structure A.
|
||||
- Settled: Set `filesystem.structure.default` to `roms/{platform}/{game}`.
|
||||
- Settled: Set `filesystem.structure.firmware` to `bios/{platform}`.
|
||||
- File safety
|
||||
- Settled: Keep library conversion disabled.
|
||||
- Settled: Do not enable `ROM_CONVERTO_ENABLED`.
|
||||
- Browser emulation
|
||||
- Settled: Set `emulatorjs.auto_save_sync` to `false`.
|
||||
- Existing integration
|
||||
- Settled: Reuse `services.romm`.
|
||||
- Settled: Keep IGDB, ScreenScraper, and SteamGridDB configuration unchanged.
|
||||
- Validation
|
||||
- Settled: Build the local package and the Pikachu configuration.
|
||||
- Settled: Do not run a local NixOS VM test.
|
||||
- Settled: The operator validates the deployment on Pikachu.
|
||||
|
||||
## Settled decisions
|
||||
|
||||
- Build a local RomM 5.4.0 package.
|
||||
- Do not rely on either unreviewed Nixpkgs pull request.
|
||||
- Expose the package as `pkgs.alexion.romm`.
|
||||
- Reuse `services.romm`.
|
||||
- Generate `config.yml` with Nix.
|
||||
- Replace `config.yml` with a Nix-store symlink on activation.
|
||||
- Declare Structure A with `roms/{platform}/{game}` and `bios/{platform}`.
|
||||
- Keep `ROM_CONVERTO_ENABLED` disabled.
|
||||
- Set `emulatorjs.auto_save_sync` to `false`.
|
||||
- Keep IGDB, ScreenScraper, and SteamGridDB configuration unchanged.
|
||||
- Build the local package and Pikachu configuration.
|
||||
- Do not run a local NixOS VM test.
|
||||
- Let the operator validate the deployment on Pikachu.
|
||||
|
||||
## Frontier
|
||||
|
||||
No in-scope decisions remain.
|
||||
@@ -1,106 +0,0 @@
|
||||
# RomM migration design tree
|
||||
|
||||
## Known facts
|
||||
|
||||
- RomM is listed under the Pikachu and Pichu Proxmox migration.
|
||||
- Pikachu already runs VLAN 20 guests with persistent storage.
|
||||
- Nixpkgs provides a tested `services.romm` module at version 5.2.0.
|
||||
- `services.romm.dataDir` defaults to `/var/lib/romm`.
|
||||
- RomM stores the game library below `/var/lib/romm/library`.
|
||||
- RomM stores saves, states, user-uploaded screenshots, and profile avatars below `/var/lib/romm/assets/users/<user-id>/`.
|
||||
- RomM derives `<user-id>` from its database ID, so it is not a stable configured username.
|
||||
- RomM stores scraped covers and provider-sourced game screenshots below `/var/lib/romm/resources/roms/<platform-id>/<rom-id>/`.
|
||||
- RomM 5.4.0 exists upstream but Nixpkgs does not package it.
|
||||
- RomM 5.3.0 requires an explicit library-structure declaration in `config.yml`.
|
||||
- The 5.4.0 package phase must add the Structure A declaration for `roms/{platform}/{game}` and `bios/{platform}`.
|
||||
- RomM 5.4.0 can modify ROM files in place only when library conversion is explicitly enabled.
|
||||
- RomM 5.3.0 identifies hashed ROMs by CRC, MD5, and SHA-1 after a scan.
|
||||
- RomM 5.3.0 matches moved or renamed hashed ROMs to their existing database records.
|
||||
- This migration is a fresh installation.
|
||||
- The new library will receive selected ROMs from the old installation.
|
||||
- Copyparty serves `/pikachu/media` as its writable `/media` volume.
|
||||
|
||||
## Decision tree
|
||||
|
||||
- RomM deployment
|
||||
- Target location
|
||||
- Settled: Deploy a dedicated guest on Pikachu.
|
||||
- Package source
|
||||
- Settled: Start with Nixpkgs's RomM 5.2.0 package.
|
||||
- Settled: Package RomM 5.4.0 locally as follow-up work.
|
||||
- Persistent storage
|
||||
- Settled: Use `/var/lib/romm` in the guest.
|
||||
- Settled: Mount `/pikachu/data/romm` at `/var/lib/romm`.
|
||||
- Settled: Mount the ROM library separately at `/var/lib/romm/library`.
|
||||
- Settled: Mount user assets separately for Copyparty browsing.
|
||||
- Settled: Keep scraped resources in `/pikachu/data/romm`.
|
||||
- Settled: Mount `/pikachu/media/games/emulation/roms` at `/var/lib/romm/library/roms`.
|
||||
- Settled: Mount `/pikachu/media/games/emulation/bios` at `/var/lib/romm/library/bios`.
|
||||
- Settled: Mount `/pikachu/media/games/emulation/users` at `/var/lib/romm/assets/users`.
|
||||
- Settled: Mount `/pikachu/data/romm/postgresql` at `/var/lib/postgresql`.
|
||||
- Settled: Mount `/pikachu/data/romm/redis` at `/var/lib/redis-romm`.
|
||||
- Guest networking
|
||||
- Settled: Follow the standard guest-provisioning protocol, including its VLAN 20 DHCP reservation.
|
||||
- Public route
|
||||
- Settled: Retain the old installation's public domain.
|
||||
- Metadata and authentication secrets
|
||||
- Settled: Configure IGDB, ScreenScraper, and SteamGridDB.
|
||||
- Settled: Use RomM's default metadata and artwork priority.
|
||||
- Settled: Manage the 5.4.0 `config.yml` declaratively with Nix.
|
||||
- Settled: Use RomM's default region and English-language priority.
|
||||
- Save synchronization
|
||||
- Settled: Disable automatic browser save synchronization in 5.4.0.
|
||||
- Authentication
|
||||
- Settled: Use RomM's built-in accounts initially.
|
||||
- Deferred: Configure RomM as an OpenID Connect client after an identity provider exists.
|
||||
- ROM selection and import
|
||||
- Settled: The operator manually selects and copies ROMs after deployment.
|
||||
- Settled: Do not create automatic import tooling or a ROM manifest.
|
||||
- Capacity and service limits
|
||||
- Settled: Limit the guest to `4G` memory, `200%` CPU, and `512` tasks.
|
||||
- Library scanning
|
||||
- Settled: Enable the filesystem watcher for library changes.
|
||||
- Cutover and retirement
|
||||
- Out of scope: The operator manages the old installation and public-domain cutover outside this monorepo.
|
||||
- Work phases
|
||||
- Settled: Create a RomM guest module with Nixpkgs's package in [[0014-romm-guest]].
|
||||
- Settled: Package RomM 5.4.0 locally and reuse `services.romm` in [[0015-romm-5-4-package]].
|
||||
- Settled: Move selected ROMs after the service deployment is ready in [[0016-romm-manual-rom-migration]].
|
||||
|
||||
## Settled decisions
|
||||
|
||||
- **Migration model:** Start with a fresh RomM installation.
|
||||
- **Data scope:** Copy selected ROMs from the old installation.
|
||||
- **Old application state:** Do not migrate the database, users, sessions, or job queue.
|
||||
- **Deployment target:** Deploy a dedicated guest on Pikachu.
|
||||
- **Initial package:** Use Nixpkgs's RomM 5.2.0 package.
|
||||
- **Package follow-up:** Package RomM 5.4.0 locally.
|
||||
- **Work phases:** Deploy the guest, update the package, then import selected ROMs.
|
||||
- **Guest data directory:** Use RomM's default `/var/lib/romm`.
|
||||
- **Host application data:** Mount `/pikachu/data/romm` at `/var/lib/romm`.
|
||||
- **ROM library:** Mount it separately at `/var/lib/romm/library`.
|
||||
- **Storage classification:** Preserve the distinction between application data and browsable media.
|
||||
- **User assets:** Mount `/pikachu/media/games/emulation/users` at `/var/lib/romm/assets/users` to expose saves, states, user-uploaded screenshots, and profile avatars through Copyparty.
|
||||
- **ROM paths:** Mount `/pikachu/media/games/emulation/roms` and `/pikachu/media/games/emulation/bios` at the corresponding children of `/var/lib/romm/library`.
|
||||
- **Guest networking:** Follow the standard guest-provisioning protocol.
|
||||
- **Public route:** Retain the old installation's public domain.
|
||||
- **Metadata providers:** Configure IGDB, ScreenScraper, and SteamGridDB in both package phases.
|
||||
- **Metadata priority:** Use RomM's default metadata and artwork priority.
|
||||
- **RomM configuration:** Manage the required 5.4.0 `config.yml` declaratively with Nix.
|
||||
- **Scan region and language priority:** Use RomM's defaults.
|
||||
- **Initial authentication:** Use RomM's built-in accounts.
|
||||
- **OIDC follow-up:** Track it in [[0013-oidc-provider]].
|
||||
- **ROM migration:** The operator manually selects and copies ROMs after deployment.
|
||||
- **Guest limits:** Cap the guest at `4G` memory, `200%` CPU, and `512` tasks.
|
||||
- **Library watcher:** Enable the filesystem watcher.
|
||||
- **PostgreSQL:** Mount `/pikachu/data/romm/postgresql` at `/var/lib/postgresql`.
|
||||
- **Redis:** Mount `/pikachu/data/romm/redis` at `/var/lib/redis-romm`.
|
||||
- **Browser save synchronization:** Disable it in 5.4.0 until browser emulation is needed.
|
||||
|
||||
## Frontier
|
||||
|
||||
No in-scope decisions remain.
|
||||
|
||||
## Next question
|
||||
|
||||
None. Present the settled design for confirmation before implementation.
|
||||
@@ -1,109 +0,0 @@
|
||||
---
|
||||
date-created: 2026-10-20 17:45
|
||||
date-modified: 2026-10-20 17:52
|
||||
---
|
||||
# SteamVR to Meta Quest 3 design tree
|
||||
|
||||
## Goal
|
||||
|
||||
Stream PCVR from Zeus to a Meta Quest 3.
|
||||
|
||||
The design must produce a repeatable and diagnosable SteamVR session.
|
||||
|
||||
## Observed facts
|
||||
|
||||
- Zeus runs NixOS with a CachyOS kernel.
|
||||
- Zeus uses an AMD Radeon RX 9070-series GPU with the `amdgpu` driver.
|
||||
- Zeus runs Hyprland on Wayland.
|
||||
- Zeus has Steam and Gamescope configured.
|
||||
- SteamVR 2.17.10 is installed.
|
||||
- SteamVR includes Valve's Linux `vrlink` driver.
|
||||
- A previous `vrlink` session detected the Quest 3 and its Touch controllers.
|
||||
- That session selected 90 Hz from the Quest's 72, 80, 90, and 120 Hz modes.
|
||||
- SteamVR setup lacks `cap_sys_nice` on `vrcompositor-launcher`.
|
||||
- NixOS can declare host packages, permissions, services, and diagnostics.
|
||||
- Steam owns the Quest pairing and SteamVR user settings.
|
||||
- Zeus connects by Ethernet at `10.23.50.221`.
|
||||
- The local Wi-Fi scan finds the `Tracion` 5 GHz network at 5220 MHz.
|
||||
- The scan reports a 1170 Mbit/s link rate.
|
||||
- The operator has no external tracked devices.
|
||||
|
||||
## Decision tree
|
||||
|
||||
- **D1: Host platform commitment** — Settled: Linux-native on the current NixOS installation.
|
||||
- Linux-native on the current NixOS installation.
|
||||
- **D2: Transport** — Blocked by D1
|
||||
- Wireless LAN. — Settled: primary transport.
|
||||
- **D3: Streaming stack** — Settled: SteamVR with its built-in VRLink driver.
|
||||
- SteamVR with its built-in VRLink driver.
|
||||
- **D3a: SteamVR acceptance criteria** — Settled: reliable session test.
|
||||
- Run a SteamVR game for 30 minutes at 90 Hz.
|
||||
- Verify visuals, audio, and Touch controllers.
|
||||
- Verify one reconnect.
|
||||
- ALVR only after SteamVR fails the agreed acceptance criteria.
|
||||
- **D4: Wi-Fi topology** — Settled: keep the current access point.
|
||||
- Keep the current access point.
|
||||
- Add a dedicated access point.
|
||||
- Use a direct Wi-Fi link.
|
||||
- **D5: Session performance target** — Settled: selectable refresh-rate profiles.
|
||||
- Selectable refresh-rate profiles.
|
||||
- **D5a: Default refresh rate** — Settled: 90 Hz.
|
||||
- **D5b: Included refresh rates** — Settled: 72, 80, 90, and 120 Hz.
|
||||
- **D6: Network isolation** — Settled: existing trusted LAN.
|
||||
- Existing trusted LAN.
|
||||
- Dedicated VR VLAN or SSID.
|
||||
- **D7: Controller and tracking scope** — Settled: Quest Touch controllers only.
|
||||
- Quest Touch controllers only.
|
||||
- Also support external tracked devices.
|
||||
- **D8: Launch workflow** — Settled: automatic service and headset connection.
|
||||
- Start SteamVR when Steam Link connects from the Quest.
|
||||
- **D9: Diagnostics and recovery** — Settled: capture health checks and logs.
|
||||
- Capture SteamVR logs and health checks.
|
||||
- Recover failed sessions manually.
|
||||
- USB tether.
|
||||
- **D10: USB streaming stack** — Blocked by D2
|
||||
- ALVR over USB.
|
||||
- Meta Quest Link through a Windows installation.
|
||||
- **D11: USB role** — Blocked by D2
|
||||
- Primary transport.
|
||||
- Fallback transport.
|
||||
- Both wireless LAN and USB tether.
|
||||
- **D12: Primary transport** — Blocked by D2
|
||||
- **D13: Fallback behavior** — Blocked by D12
|
||||
- **D14: System integration** — Settled: hybrid configuration.
|
||||
- NixOS manages system integration.
|
||||
- Steam manages Quest pairing and user settings.
|
||||
- Dual-boot or replace NixOS with Windows.
|
||||
- **D15: Windows streaming stack** — Blocked by D1
|
||||
- Steam Link.
|
||||
- Virtual Desktop.
|
||||
- Meta Quest Link or Air Link.
|
||||
- ALVR.
|
||||
- **D16: Windows installation scope** — Blocked by D1
|
||||
- Dedicated VR installation.
|
||||
- General-use Windows installation.
|
||||
- **D2: Transport** — Blocked by D1
|
||||
- Keep NixOS and add a separate Windows VR host.
|
||||
- **D17: Host placement** — Blocked by D1
|
||||
- **D15: Windows streaming stack** — Blocked by D17
|
||||
- **D2: Transport** — Blocked by D17
|
||||
|
||||
## Frontier
|
||||
|
||||
None.
|
||||
|
||||
## Settled decisions
|
||||
|
||||
- **D1: Host platform commitment** — Linux-native on the current NixOS installation.
|
||||
- **D2: Transport** — Wireless LAN as the primary transport.
|
||||
- **D4: Wi-Fi topology** — Keep the current access point.
|
||||
- **D5: Session performance target** — Selectable profiles at 72, 80, 90, and 120 Hz.
|
||||
- **D5a: Default refresh rate** — 90 Hz.
|
||||
- **D5b: Included refresh rates** — 72, 80, 90, and 120 Hz.
|
||||
- **D6: Network isolation** — Existing trusted LAN.
|
||||
- **D7: Controller and tracking scope** — Quest Touch controllers only.
|
||||
- **D3: Streaming stack** — SteamVR with its built-in VRLink driver.
|
||||
- **D3a: SteamVR acceptance criteria** — 30-minute session at 90 Hz, with visuals, audio, Touch controllers, and one reconnect.
|
||||
- **D8: Launch workflow** — Start SteamVR when Steam Link connects from the Quest.
|
||||
- **D9: Diagnostics and recovery** — Capture health checks and logs. Recover failed sessions manually.
|
||||
- **D14: System integration** — NixOS manages system integration. Steam manages Quest pairing and user settings.
|
||||
@@ -1,36 +0,0 @@
|
||||
# TL;DR command design tree
|
||||
|
||||
This tree reviews the implementation decisions in `tldr-design.md`.
|
||||
|
||||
## Settled decisions
|
||||
|
||||
- **Command form and scope:** Add the project prompt template `.pi/prompts/tldr.md`.
|
||||
- **Explicit source interface:** Interpret command arguments as a free-form source request.
|
||||
- **Default source:** Summarize the preceding agent response when no source request exists.
|
||||
- **Summary shape:** Produce a compact summary without a fixed format.
|
||||
- **Unavailable or unclear sources:** Add no special handling policy.
|
||||
|
||||
## Decision tree
|
||||
|
||||
- Command behavior
|
||||
- Source selection
|
||||
- Settled: Use the preceding agent response without arguments.
|
||||
- Settled: Interpret arguments as a free-form source request.
|
||||
- Output
|
||||
- Settled: Produce a compact summary without a fixed format.
|
||||
- Summary language
|
||||
- Settled: Do not prescribe a language policy.
|
||||
- Error behavior
|
||||
- Settled: Add no special handling policy for unavailable or unclear sources.
|
||||
|
||||
## Frontier
|
||||
|
||||
The frontier is empty.
|
||||
|
||||
## Shared understanding pending confirmation
|
||||
|
||||
The project gets a `/tldr` prompt command in `.pi/prompts/tldr.md`.
|
||||
Without arguments, it summarizes the preceding agent response.
|
||||
With arguments, it interprets them as a free-form source request.
|
||||
It produces a compact summary with no fixed format or language policy.
|
||||
The prompt adds no special policy for unavailable or unclear sources.
|
||||
@@ -1,39 +0,0 @@
|
||||
# TL;DR command design
|
||||
|
||||
## Goal
|
||||
|
||||
Add a Pi command that summarizes the preceding agent response by default.
|
||||
An explicit command argument identifies the source to summarize.
|
||||
|
||||
## Decision tree
|
||||
|
||||
- Command form and scope
|
||||
- Settled: Create a project prompt template at `.pi/prompts/tldr.md`.
|
||||
- Command interface
|
||||
- Settled: Pass arguments through as a free-form source request.
|
||||
- Default source selection
|
||||
- Settled: Summarize the preceding agent response when no source request exists.
|
||||
- Summary format and length
|
||||
- Settled: Use a compact, format-free summary.
|
||||
- Ambiguous-source behavior
|
||||
- Settled: Add no special handling policy.
|
||||
|
||||
## Settled decisions
|
||||
|
||||
- **Q1 — Command form and scope:** Create a project prompt template.
|
||||
- **Q2 — Explicit source interface:** Accept a free-form source request.
|
||||
- **Default source selection:** Summarize the preceding agent response when the command has no arguments.
|
||||
- **Q3 — Summary shape:** Use a compact, format-free summary.
|
||||
- **Q4 — Unavailable source:** Add no special handling policy.
|
||||
|
||||
## Frontier
|
||||
|
||||
The frontier is empty.
|
||||
|
||||
## Shared understanding pending confirmation
|
||||
|
||||
The project gets a `/tldr` prompt command in `.pi/prompts/tldr.md`.
|
||||
Without arguments, it summarizes the preceding agent response.
|
||||
With arguments, it interprets them as a free-form source request.
|
||||
It produces a compact summary with no fixed format.
|
||||
The prompt adds no special policy for unavailable or unclear sources.
|
||||
@@ -3,8 +3,8 @@ aliases:
|
||||
- "0002"
|
||||
- NixOS Refactor
|
||||
date-created: 2026-10-14 16:07
|
||||
date-modified: 2026-10-19 16:44
|
||||
status: closed
|
||||
date-modified: 2026-10-14 18:17
|
||||
status: open
|
||||
area: "[[dotfiles]]"
|
||||
blocked-by: []
|
||||
subtasks: []
|
||||
@@ -72,32 +72,32 @@ Complete the remaining Nix refactor work.
|
||||
- [x] `configs/nixos/modules/services/games/valheim.nix`
|
||||
- [x] `configs/nixos/modules/shell/bazel.nix`
|
||||
- [x] `configs/nixos/modules/shell/direnv/default.nix`
|
||||
- [x] `configs/nixos/modules/shell/fish/default.nix`
|
||||
- [x] `configs/nixos/modules/shell/gitea-axi.nix`
|
||||
- [x] `configs/nixos/modules/shell/git.nix`
|
||||
- [x] `configs/nixos/modules/shell/herdr.nix`
|
||||
- [x] `configs/nixos/modules/shell/nvim/default.nix`
|
||||
- [x] `configs/nixos/modules/shell/omp.nix`
|
||||
- [x] `configs/nixos/modules/shell/pi/default.nix`
|
||||
- [x] `configs/nixos/modules/shell/python.nix`
|
||||
- [x] `configs/nixos/modules/shell/rust.nix`
|
||||
- [x] `configs/nixos/modules/shell/tmux/default.nix`
|
||||
- [x] `configs/nixos/modules/shell/yazi.nix`
|
||||
- [x] `src/dotfiles/guests.nix`
|
||||
- [ ] `configs/nixos/modules/shell/fish/default.nix`
|
||||
- [ ] `configs/nixos/modules/shell/gitea-axi.nix`
|
||||
- [ ] `configs/nixos/modules/shell/git.nix`
|
||||
- [ ] `configs/nixos/modules/shell/herdr.nix`
|
||||
- [ ] `configs/nixos/modules/shell/nvim/default.nix`
|
||||
- [ ] `configs/nixos/modules/shell/omp.nix`
|
||||
- [ ] `configs/nixos/modules/shell/pi/default.nix`
|
||||
- [ ] `configs/nixos/modules/shell/python.nix`
|
||||
- [ ] `configs/nixos/modules/shell/rust.nix`
|
||||
- [ ] `configs/nixos/modules/shell/tmux/default.nix`
|
||||
- [ ] `configs/nixos/modules/shell/yazi.nix`
|
||||
- [ ] `src/dotfiles/guests.nix`
|
||||
- [x] `configs/nixos/guests/actualbudget.nix`
|
||||
- [x] `configs/nixos/guests/copyparty.nix`
|
||||
- [x] `configs/nixos/guests/homepage.nix`
|
||||
- [x] `configs/nixos/guests/navidrome.nix`
|
||||
- [x] `configs/nixos/guests/palworld.nix`
|
||||
- [x] `configs/nixos/guests/reverse-proxy.nix`
|
||||
- [x] `configs/nixos/guests/valheim.nix`
|
||||
- [x] `src/dotfiles/hosts.nix`
|
||||
- [x] `configs/nixos/hosts/neogaia/default.nix`
|
||||
- [x] `configs/nixos/hosts/neogaia/disk.nix`
|
||||
- [x] `configs/nixos/hosts/neogaia/hardware-configuration.nix`
|
||||
- [x] `configs/nixos/hosts/pikachu/default.nix`
|
||||
- [x] `configs/nixos/hosts/pikachu/disk.nix`
|
||||
- [x] `configs/nixos/hosts/pikachu/hardware-configuration.nix`
|
||||
- [x] `configs/nixos/hosts/zeus/default.nix`
|
||||
- [x] `configs/nixos/hosts/zeus/disk.nix`
|
||||
- [x] `configs/nixos/hosts/zeus/hardware-configuration.nix`
|
||||
- [ ] `configs/nixos/guests/copyparty.nix`
|
||||
- [ ] `configs/nixos/guests/homepage.nix`
|
||||
- [ ] `configs/nixos/guests/navidrome.nix`
|
||||
- [ ] `configs/nixos/guests/palworld.nix`
|
||||
- [ ] `configs/nixos/guests/reverse-proxy.nix`
|
||||
- [ ] `configs/nixos/guests/valheim.nix`
|
||||
- [ ] `src/dotfiles/hosts.nix`
|
||||
- [ ] `configs/nixos/hosts/neogaia/default.nix`
|
||||
- [ ] `configs/nixos/hosts/neogaia/disk.nix`
|
||||
- [ ] `configs/nixos/hosts/neogaia/hardware-configuration.nix`
|
||||
- [ ] `configs/nixos/hosts/pikachu/default.nix`
|
||||
- [ ] `configs/nixos/hosts/pikachu/disk.nix`
|
||||
- [ ] `configs/nixos/hosts/pikachu/hardware-configuration.nix`
|
||||
- [ ] `configs/nixos/hosts/zeus/default.nix`
|
||||
- [ ] `configs/nixos/hosts/zeus/disk.nix`
|
||||
- [ ] `configs/nixos/hosts/zeus/hardware-configuration.nix`
|
||||
|
||||
@@ -3,7 +3,7 @@ aliases:
|
||||
- "0011"
|
||||
- Gitea Axi Rust Migration
|
||||
date-created: 2026-10-18 15:43
|
||||
date-modified: 2026-10-19 16:46
|
||||
date-modified: 2026-10-18 15:44
|
||||
status: open
|
||||
area: "[[monorepo]]"
|
||||
blocked-by:
|
||||
@@ -16,5 +16,3 @@ subtasks: []
|
||||
Rewrite `gitea-axi` in Rust and migrate it into this monorepo.
|
||||
|
||||
Preserve its Gitea workflow and command behavior.
|
||||
|
||||
Replace the external Home Manager module in `configs/nixos/modules/shell/gitea-axi.nix`.
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
---
|
||||
aliases:
|
||||
- "0013"
|
||||
- OIDC Provider
|
||||
date-created: 2026-10-19 19:40
|
||||
date-modified: 2026-10-19 19:40
|
||||
status: open
|
||||
area: "[[homelab]]"
|
||||
blocked-by: []
|
||||
subtasks: []
|
||||
---
|
||||
|
||||
# OIDC Provider
|
||||
|
||||
Deploy an OpenID Connect provider for homelab services.
|
||||
|
||||
Configure a RomM client after the provider is available.
|
||||
@@ -1,42 +0,0 @@
|
||||
---
|
||||
aliases:
|
||||
- "0014"
|
||||
- RomM Guest
|
||||
date-created: 2026-10-19 19:40
|
||||
date-modified: 2026-10-19 19:40
|
||||
status: closed
|
||||
area: "[[homelab]]"
|
||||
blocked-by: []
|
||||
subtasks: []
|
||||
---
|
||||
|
||||
# RomM Guest
|
||||
|
||||
Deploy a dedicated RomM guest on Pikachu with Nixpkgs RomM 5.2.0.
|
||||
|
||||
Follow the standard guest-provisioning protocol.
|
||||
Keep the old installation's public domain.
|
||||
|
||||
Use the local PostgreSQL and Redis instances from `services.romm`.
|
||||
Set guest limits to `4G` memory, `200%` CPU, and `512` tasks.
|
||||
Enable the library watcher.
|
||||
Use RomM built-in accounts.
|
||||
|
||||
Mount persistent application data:
|
||||
|
||||
```text
|
||||
/pikachu/data/romm -> /var/lib/romm
|
||||
/pikachu/data/romm/postgresql -> /var/lib/postgresql
|
||||
/pikachu/data/romm/redis -> /var/lib/redis-romm
|
||||
```
|
||||
|
||||
Mount browsable media:
|
||||
|
||||
```text
|
||||
/pikachu/media/games/emulation/roms -> /var/lib/romm/library/roms
|
||||
/pikachu/media/games/emulation/bios -> /var/lib/romm/library/bios
|
||||
/pikachu/media/games/emulation/users -> /var/lib/romm/assets/users
|
||||
```
|
||||
|
||||
Configure IGDB, ScreenScraper, and SteamGridDB from secrets.
|
||||
Use RomM's default metadata, artwork, region, and language priorities.
|
||||
@@ -1,31 +0,0 @@
|
||||
---
|
||||
aliases:
|
||||
- "0015"
|
||||
- RomM 5.4 Package
|
||||
date-created: 2026-10-19 19:40
|
||||
date-modified: 2026-10-21 00:01
|
||||
status: closed
|
||||
area: "[[homelab]]"
|
||||
blocked-by:
|
||||
- "[[0014-romm-guest]]"
|
||||
subtasks: []
|
||||
---
|
||||
|
||||
# RomM 5.4 Package
|
||||
|
||||
Package RomM 5.4.0 locally.
|
||||
Reuse `services.romm`.
|
||||
|
||||
Manage `config.yml` declaratively with Nix.
|
||||
Declare the Structure A library layout:
|
||||
|
||||
```yaml
|
||||
filesystem:
|
||||
structure:
|
||||
default: "roms/{platform}/{game}"
|
||||
firmware: "bios/{platform}"
|
||||
```
|
||||
|
||||
Keep library conversion disabled.
|
||||
Disable automatic browser save synchronization until browser emulation is needed.
|
||||
Keep the IGDB, ScreenScraper, and SteamGridDB configuration unchanged.
|
||||
@@ -1,24 +0,0 @@
|
||||
---
|
||||
aliases:
|
||||
- "0016"
|
||||
- RomM Manual ROM Migration
|
||||
date-created: 2026-10-19 19:40
|
||||
date-modified: 2026-10-19 19:40
|
||||
status: open
|
||||
area: "[[homelab]]"
|
||||
blocked-by:
|
||||
- "[[0014-romm-guest]]"
|
||||
subtasks: []
|
||||
---
|
||||
|
||||
# RomM Manual ROM Migration
|
||||
|
||||
Manually copy selected ROMs and BIOS files from the old installation.
|
||||
|
||||
Copy ROMs to `/pikachu/media/games/emulation/roms`.
|
||||
Copy BIOS files to `/pikachu/media/games/emulation/bios`.
|
||||
|
||||
Do not migrate the old database, users, sessions, job queue, or other application state.
|
||||
Do not add automatic import tooling or a ROM manifest.
|
||||
|
||||
This task does not change monorepo configuration.
|
||||
@@ -1,7 +1,3 @@
|
||||
---
|
||||
date-created: 2026-10-19 02:39
|
||||
date-modified: 2026-10-20 17:45
|
||||
---
|
||||
# Brain Agent Instructions
|
||||
|
||||
This repository is an Obsidian vault for an execution-focused second brain.
|
||||
@@ -36,3 +32,6 @@ Include negative decisions only when they prevent a likely, harmful, or repeated
|
||||
|
||||
Remove template instruction text when writing a note from a template.
|
||||
|
||||
## Gotchas
|
||||
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
# Migration
|
||||
|
||||
- [[technical-writing]] -> skill?
|
||||
- gitea-axi
|
||||
- skills
|
||||
- claude-code/codebase-design
|
||||
- claude-code/implement
|
||||
@@ -51,8 +52,11 @@
|
||||
|
||||
- lidarr (requires raichu)
|
||||
- immich
|
||||
- romm
|
||||
- gitea
|
||||
- nginxproxymanager (gradual migration to nginx guest)
|
||||
- adguard
|
||||
- manage DNS rewrites for local service names such as `home`
|
||||
|
||||
## Raichu Proxmox -> Nixos
|
||||
|
||||
@@ -107,3 +111,8 @@
|
||||
- Investigate Rust Bazel target structure.
|
||||
Prefer clear per-file dependency edges, but `rust_library` maps each target to a crate.
|
||||
Decide whether wrapper rules or another pattern can improve graph clarity without forcing artificial crate boundaries.
|
||||
|
||||
## Nix Refactor
|
||||
|
||||
Review Nix files in entrypoint order:
|
||||
|
||||
|
||||
@@ -4,7 +4,5 @@
|
||||
inherit root;
|
||||
};
|
||||
|
||||
romm = pkgs.callPackage ./romm.nix { };
|
||||
|
||||
waybar = pkgs.callPackage ./waybar.nix { };
|
||||
}
|
||||
|
||||
@@ -1,124 +0,0 @@
|
||||
{
|
||||
buildNpmPackage,
|
||||
fetchFromGitHub,
|
||||
lib,
|
||||
python3,
|
||||
romm,
|
||||
}:
|
||||
romm.overrideAttrs (
|
||||
finalAttrs: previousAttrs:
|
||||
let
|
||||
frontend = buildNpmPackage {
|
||||
pname = "romm-frontend";
|
||||
inherit (finalAttrs) version;
|
||||
src = "${finalAttrs.src}/frontend";
|
||||
|
||||
npmDepsHash = "sha256-f5IcpOVv7JYFzUPkZIq5nbT3dS1f/KCSllh8Vkgl1P0=";
|
||||
npmFlags = [ "--ignore-scripts" ];
|
||||
makeCacheWritable = true;
|
||||
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
cp -r dist $out
|
||||
chmod u+w $out/assets
|
||||
cp -r assets/. $out/assets/
|
||||
runHook postInstall
|
||||
'';
|
||||
};
|
||||
|
||||
# RomM 5.4 requires packages absent from the Nixpkgs 5.2 environment.
|
||||
pythonEnv = python3.withPackages (
|
||||
ps: with ps; [
|
||||
aiohttp
|
||||
alembic
|
||||
anyio
|
||||
apprise
|
||||
asyncssh
|
||||
authlib
|
||||
bcrypt
|
||||
colorama
|
||||
croniter
|
||||
cryptography
|
||||
defusedxml
|
||||
email-validator
|
||||
fastapi
|
||||
gunicorn
|
||||
httpx
|
||||
httpx2
|
||||
itsdangerous
|
||||
jinja2
|
||||
joserfc
|
||||
mariadb
|
||||
mutagen
|
||||
mysql-connector-python
|
||||
opentelemetry-distro
|
||||
opentelemetry-exporter-otlp
|
||||
opentelemetry-instrumentation-aiohttp-client
|
||||
opentelemetry-instrumentation-fastapi
|
||||
opentelemetry-instrumentation-httpx
|
||||
opentelemetry-instrumentation-redis
|
||||
opentelemetry-instrumentation-sqlalchemy
|
||||
passlib
|
||||
pillow
|
||||
psycopg
|
||||
pydantic
|
||||
pydash
|
||||
python-dotenv
|
||||
python-magic
|
||||
python-multipart
|
||||
python-socketio
|
||||
pyyaml
|
||||
redis
|
||||
rq
|
||||
rq-scheduler
|
||||
sentry-sdk
|
||||
sqlalchemy
|
||||
starlette
|
||||
streaming-form-data
|
||||
strsimpy
|
||||
ua-parser
|
||||
unidecode
|
||||
uvicorn
|
||||
uvicorn-worker
|
||||
watchfiles
|
||||
yarl
|
||||
zipfile-inflate64
|
||||
zstandard
|
||||
]
|
||||
);
|
||||
in
|
||||
{
|
||||
version = "5.4.0";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "rommapp";
|
||||
repo = "romm";
|
||||
tag = finalAttrs.version;
|
||||
hash = "sha256-oIYEdIAJgXkcqjZtWlWKD/NFiQ3gbo7gxwLhqZOCWpM=";
|
||||
};
|
||||
|
||||
postPatch = previousAttrs.postPatch + ''
|
||||
# Redis 8.0.1 removed this argument.
|
||||
# RomM disables the related maintenance notifications.
|
||||
substituteInPlace backend/handler/redis_handler.py \
|
||||
--replace-fail 'from redis.maint_notifications import MaintNotificationsConfig' "" \
|
||||
--replace-fail ' "maint_notifications_config": MaintNotificationsConfig(enabled=False),' ""
|
||||
'';
|
||||
|
||||
installPhase =
|
||||
lib.replaceStrings
|
||||
[
|
||||
"${previousAttrs.passthru.frontend}"
|
||||
"${previousAttrs.passthru.pythonEnv}"
|
||||
]
|
||||
[
|
||||
"${frontend}"
|
||||
"${pythonEnv}"
|
||||
]
|
||||
previousAttrs.installPhase;
|
||||
|
||||
passthru = previousAttrs.passthru // {
|
||||
inherit frontend pythonEnv;
|
||||
};
|
||||
}
|
||||
)
|
||||
@@ -3,20 +3,16 @@ name: grill
|
||||
description: Grill the user relentlessly about a plan, decision, or idea. Use when the user wants to stress-test their thinking, or uses any 'grill' trigger phrases.
|
||||
---
|
||||
|
||||
Interview the user relentlessly until you reach a shared understanding. Map this as a **design tree**: each decision branches into the decisions it unblocks.
|
||||
Interview the user relentlessly until you reach a shared understanding. Map this as a **design tree**: every decision branches into the decisions that hang off it.
|
||||
|
||||
Maintain the design tree in a new Markdown file. Use the file as the source of truth.
|
||||
|
||||
Build the complete **frontier** before the first question.
|
||||
After each answer, recompute and display every frontier decision.
|
||||
|
||||
Ask one frontier decision per turn.
|
||||
A clarification leaves that decision open.
|
||||
Work the tree one question at a time. The **frontier** is every decision whose prerequisites are already settled: the questions you can ask now without guessing at answers you have not heard yet. Record the next frontier question in the file, then ask it and wait for the user's answer.
|
||||
|
||||
Format a question like so:
|
||||
|
||||
```
|
||||
❓ **D1 — <decision title>**: <question body, might be multiple paragraphs, including multiple choices>
|
||||
❓ **Q1** - **<question title>**: <question body, might be multiple paragraphs, including multiple choices>
|
||||
|
||||
**Options**
|
||||
|
||||
@@ -26,7 +22,7 @@ Format a question like so:
|
||||
|
||||
---
|
||||
|
||||
❓ **D2 — <decision title>**: <question body, might be multiple paragraphs, including multiple choices>
|
||||
❓ **Q2** - **<question title>**: <question body, might be multiple paragraphs, including multiple choices>
|
||||
|
||||
**Options**
|
||||
|
||||
@@ -38,10 +34,7 @@ Format a question like so:
|
||||
List every viable answer in an ordered list and mark your recommended answer.
|
||||
Word each question so "yes" accepts your recommended answer.
|
||||
|
||||
Before asking the next question, account for every decision that the response settles.
|
||||
Record preferences as constraints.
|
||||
Settle an implementation decision only after facts establish that it is viable.
|
||||
A decision with unresolved prerequisites belongs outside the frontier.
|
||||
Each user answer reshapes the tree: settled decisions push the frontier outward and unblock questions that depended on them. Recompute the frontier in the file and ask the next question. A question whose answer depends on another open question belongs later, not next.
|
||||
|
||||
Finding _facts_ is your job, never the user's. When a frontier question needs a fact from the environment (filesystem, tools, etc.), find it with available tools; don't ask the user for anything you could look up yourself. The _decisions_ are the user's: put each to them and wait.
|
||||
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
## 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,17 +22,6 @@ Do not call `lib.removeSuffix ".nix"` outside the utility definition.
|
||||
|
||||
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
|
||||
|
||||
### Rule
|
||||
|
||||
Reference in New Issue
Block a user