Commit Graph

187 Commits

Author SHA1 Message Date
e6ea8a0060 feat(guests): place networked guests on tagged VLANs (task 0004)
A guest sets `vlan` to attach to its host's `br-vlan<id>` bridge, `mac`
to reuse an existing address (else a stable one is derived from its
namespace path and read back via `nix eval`), and `address` for a static
IP (else DHCP). The MAC and address are pinned inside the guest by its
own networkd, the only stable MAC pin for a nested container. A guest
naming a VLAN its host has not declared fails the build with an
actionable message.

The `br-vlan<id>` naming moves into a shared `bridgeName` in the lib, so
the bridge a guest attaches to and the bridge the host emits have one
source.
2026-07-25 16:45:26 -04:00
83106239d4 chore(agent-skills): update skills input, drop grill and domain-modeling
Bump the skills flake input and remove the grill and domain-modeling
skills, which are no longer carried in the agent toolset.
2026-07-25 16:36:33 -04:00
e05adef7b7 feat(network): add host VLAN-bridge networking foundation
Introduce `modules.network`, the host-level networking foundation a Host
declares once. A Host states its trunk interface and the tagged VLAN ids to
materialize, and the module emits one systemd-networkd bridge per VLAN, named
by the `br-vlan<id>` convention, plus the host's own management address on a
chosen VLAN's bridge.

The trunk and every bridge set `RequiredForOnline = "no"` so wait-online never
blocks boot on a carrier-less link, and the module owns its own NetworkManager
`unmanaged` guard so enabling it is self-sufficient. Two assertions tie the
management VLAN to the declared VLANs and a management address to a VLAN, so an
address can never be silently dropped.

No host commits an enablement: the only host is a wifi laptop that cannot
present guests as L2 citizens and whose DNS the module's networkd/resolved
would disturb, so the standing enablement waits for the first wired server
host. The build and bridge-name evaluation were verified by enabling the
module ad hoc, and stay reproducible through `extendModules`.
2026-07-25 16:18:10 -04:00
e7d7eb14e1 feat: introduce guests as nested-container definitions
Add a third auto-loaded kind beside the host and the module: the guest, a
reusable definition under guests/ that a host enables like a module and that
realizes its interior as a systemd-nspawn nested container.

Split the shared base config so a guest can stand on it.
base.nix now holds the substrate both bases share — the primary user,
home-manager, and the unstable/stable overlays.
system.nix keeps the host-only machinery, and a new guest.nix is the slim
guest-base: it imports the full modules tree, pins the interior release, and
auto-enables the toolkit bundle and SSH so any guest is workable on sight.

Give modules.ssh a guest flavor.
A host restores its host keys from secrets as before, while a guest sets
hostKeys.restore = false, names no sops files, and self-generates a host key,
so it holds no age key of its own.

The lib grows a guest helper that declares the guests.<path> namespace with an
enable and a backend field.
Only the container backend is built; microvm is a reserved value that trips a
clear build-time assertion rather than silently building nothing.

A sample guest exercises the whole path, and neogaia enables it, so the guest
interior builds through the existing nix flake check seam.
2026-07-25 13:19:07 -04:00
8cd59cb292 feat(toolkit): bundle the baseline interactive toolset
Add modules.toolkit, an aggregator that enables fish, tmux, nvim, git,
and direnv as one unit, and makes fish the login shell, so any Host or
Guest shell feels identical. Each member is set via mkDefault, so a Host
can still override any single piece while the one flag brings up the
whole bundle.

neogaia now enables the bundle as a single line in place of its five
individual enables and its explicit default-shell line, keeping the Host
a flat checklist.

Also lands the domain model, ADR 0006, and the guests spec: the toolkit
is the first piece of the wider guests work, and the guest-base will
auto-enable this bundle.
2026-07-25 12:48:37 -04:00
582800d548 chore: retire consumed specs and tasks, trim CLAUDE.md gotchas
The .claude specs and tasks have been distilled into the notes vault, so
remove the 37 consumed files. Trim the CLAUDE.md gotchas to their
project-specific residue: drop the entries now captured as general notes,
and fix the two references that pointed at the deleted specs.
2026-07-24 21:40:50 -04:00
8885ffae67 feat(agent-skills): install the wiki and consume vault skills
Source the wiki and consume skills from the skills flake, placing them
globally so they are active in every project. wiki reads the personal
Obsidian vault read-only, and consume mines a source into it, pairing
with the Obsidian vault this branch installs.
2026-07-24 20:11:07 -04:00
40ae623094 feat(hyprland): enable native Wayland for Electron apps
Set NIXOS_OZONE_WL in the compositor env so Chromium and Electron
wrappers select native Wayland rather than XWayland, which blurs at this
DPI. Deliver it through Hyprland's env directive because UWSM starts the
session without the shell profile home-manager's sessionVariables rely
on.
2026-07-24 17:48:35 -04:00
7409e4e6a0 feat(obsidian): install Obsidian as the desktop note-taking vault
Add a standalone modules.desktop.obsidian module and enable it on
neogaia. Give it a waybar window-rewrite icon so its windows show a
recognisable glyph on the workspace indicator.
2026-07-24 17:48:29 -04:00
2cb47ef2cf feat(direnv): add direnv with nix-direnv shell caching 2026-07-24 16:29:57 -04:00
48a81bb8a2 style: align comments with the project conventions
Apply a codebase-wide comment audit against the comment conventions:
split banned semicolons and multi-sentence lines into one sentence per
line, cut cross-file and history narration, trim file-top headers to a
single purpose line, and drop verbosity that did not earn its place.
Prose docs (CLAUDE.md, install.md) get the same one-sentence-per-line
and no-semicolon treatment.
2026-07-24 16:17:09 -04:00
d2fbf78927 fix(fish): fit the fastfetch greeting to the terminal width
The greeting always drew the full NixOS logo beside the info block,
which overflows in a narrow terminal. Choose the logo by column count:
the full logo when it fits, the compact one when it does not, and none
below that.
2026-07-24 14:47:49 -04:00
eca87c74a6 fix(firefox): scale the browser to 1.25x on the high-DPI panel
Left at auto, Firefox reads the panel's 1.5x and inflates its whole
chrome while point-sized apps stay put. Pin devPixelsPerPx a shade under
that so the UI and pages match without dropping to a too-small 1:1.
2026-07-24 14:47:49 -04:00
bb9a92b25b feat(desktop): shrink Stylix font sizes for the laptop panel
The graphical layer inherited Stylix's default font sizes, which read
oversized on this display. Step them down a point across applications,
desktop, popups, and the terminal.
2026-07-24 14:47:49 -04:00
68e0aafbbb fix(desktop): apply the configured cursor theme to the Hyprland session
home.sessionVariables (XCURSOR_THEME/XCURSOR_SIZE from home.pointerCursor)
land in hm-session-vars.sh, which only a login shell sources. UWSM starts
the graphical session without it, so Hyprland never saw the theme and drew
its built-in cursor instead of the configured Bibata-Modern-Ice.

Declare the cursor through Hyprland's own env directive, read at compositor
startup regardless of the shell profile, sourced from config.stylix.cursor
so the theme and size stay defined in one place.
2026-07-24 14:29:38 -04:00
f967bc47bc fix(hyprland): cap output volume at 100 percent 2026-07-24 14:11:04 -04:00
75373ebdc1 feat(desktop): show volume and brightness changes with a SwayOSD popup
Route the volume and brightness keys through swayosd-client, which raises
a transient on-screen popup that auto-hides a moment after the last press,
so the level is visible while adjusting without a permanent widget. A new
osd module runs the server for the graphical session and makes the
backlight node group-writable so it dims without root.

Supersedes the plain brightnessctl binds, whose control swayosd bundles.
2026-07-24 14:07:00 -04:00
ac095ba0e4 feat(desktop): add screen brightness controls
Bind XF86MonBrightnessUp/Down to brightnessctl in the repeat-while-held
block, mirroring the volume keys. A new brightness module installs
brightnessctl's udev rule and adds the user to the video group so the
backlight can be dimmed without root.
2026-07-24 13:57:45 -04:00
7f7fc327fd fix(firefox): darken the unreadable address-bar result highlight (task 0032)
Stylix's Nord Firefox Color mapping paints the selected address-bar result in
base04, a near-white grey, against base05 text — light on light, so the text
of the highlighted result is illegible. Override that one highlight to the Nord
selection grey (base03), derived from the same Stylix scheme, restoring
contrast without touching the rest of the theme.
2026-07-24 08:51:24 -04:00
bd32795e23 fix(firefox): auto-enable the sideloaded Firefox Color add-on (task 0032)
Firefox disables profile-sideloaded extensions by default, so the Firefox
Color add-on that carries the Nord chrome theme installed but stayed disabled
and the chrome kept its default look. Set extensions.autoDisableScopes = 0 so
it enables on detection; the policy-installed functional extensions are
force-enabled by policy and unaffected.
2026-07-24 08:41:34 -04:00
6484f9466c docs(firefox): record the profiles.ini and chrome-theming fixes in the task (task 0032) 2026-07-24 08:33:55 -04:00
e42101e08b feat(firefox): Nord-theme the chrome via Stylix colorTheme (task 0032)
The base Stylix Firefox target themes only fonts and the reader view, leaving
the toolbar and tabs at Firefox's default look. Enable colorTheme so Stylix
recolours the chrome Nord from the shared scheme through its managed Firefox
Color add-on, with no hand-written chrome CSS and no flake input of ours.

Scope ADR 0005's no-pinned-add-on rule to the three functional extensions and
record the signed, Stylix-managed theming add-on as a deliberate exception.
2026-07-24 08:33:19 -04:00
09eb9a983d docs: correct the home.file key gotcha — keyed by the writer's path form, not always absolute 2026-07-24 08:25:04 -04:00
e24f808b63 fix(firefox): take ownership of profiles.ini so the profile deploys (task 0032)
Firefox writes ~/.config/mozilla/firefox/profiles.ini itself on first launch,
pointing at an auto-created random-prefix profile. Home-manager then refuses to
clobber that file and fails the whole activation, so the declarative default
profile — search.json.mozlz4 (DuckDuckGo) and the Stylix user.js — never lands
and Firefox keeps running the stale profile with Google as default and no theme.

Set force = true on the generated profiles.ini home.file entry so home-manager
owns it, deploys the default profile, and Firefox switches to it.
2026-07-24 08:25:04 -04:00
3e64bd0b7e docs: record that firefox search force does not prune builtins by omission (task 0032) 2026-07-23 23:05:38 -04:00
ee672d2479 feat(firefox): add the desktop Firefox browser module (task 0032)
Add Firefox as a single-purpose desktop Module configured entirely through
home-manager's programs.firefox, and fan it out from the desktop aggregator so
any Host with the desktop enabled brings the browser up.

Ship stock mainline Firefox, hardened and de-monetized through locked
enterprise policies (telemetry, studies, Pocket, offer-to-save-logins, the
default-browser check, Firefox accounts and sync, and the sponsored new-tab
surfaces all off). Force-install three extensions by policy, keyed by their
verified add-on ids: uBlock Origin, Proton Pass, and SponsorBlock.

Default search to DuckDuckGo over a lean list: the general-purpose commercial
engines are hidden with metaData.hidden so Firefox does not reconcile them back
in, leaving DuckDuckGo and Wikipedia. Theme the browser Nord from the shared
Stylix scheme against the one profile, and register it as the default handler
for the web-link schemes and HTML.

Record the stock-Firefox-plus-policy-extensions decision as ADR 0005.
2026-07-23 23:04:19 -04:00
039802b9e2 docs: record the git-add and dirty-tree gotchas behind the task 0031 miss 2026-07-23 21:47:22 -04:00
6f6f0178b1 fix: apply the namespace edits dropped from the task 0031 merge
The task 0031 commit moved the module files but a mis-staged `git add`
left the option-path rewrites out, so main declared the old namespaces
(`modules.claude-code`, `modules.gitea-axi`, `modules.pi`,
`modules.desktop.hyprlock`/`hypridle`) while CONTEXT.md and ADR 0004
already documented the new ones. Rewrite the paths in the moved and
in-place modules, the neogaia host, and the two live CLAUDE.md gotchas
so the code matches the recorded convention.
2026-07-23 21:44:21 -04:00
e684ac481e refactor: mirror module namespaces to their directories (task 0031)
Adopt the convention that a Module's option path mirrors its directory
under modules/, with an index file naming the directory's own segment.

- Group agent Modules under modules.agents.*: claude-code (whole
  directory), pi (flattened to a file), skills (renamed from
  agent-skills), and gitea-axi under an agents/tools/ subgroup. The
  agents/ and tools/ folders are pure namespace prefixes with no
  aggregator enable.
- Nest hypridle and hyprlock under modules.desktop.hyprland.*, with
  hyprland.nix as the index, and update the desktop aggregator.
- Remove the obsolete example Module.
- Record the convention in CONTEXT.md and ADR 0004, and update the
  neogaia host, the two live CLAUDE.md gotchas, and the skills Module's
  intentional Enable-convention exception comment.
2026-07-23 19:21:12 -04:00
02bb345fd7 feat(fish): exclude .git from the lt tree alias
The lt alias walks the tree with -a, pulling the noisy .git directory
into every listing. Bake -I '.git' into it so the git internals are
skipped by default; extra ignores can still be passed at call time.
2026-07-23 08:42:51 -04:00
a3e3e80c83 feat(agent-skills): wire skills flake for global agent skills
Add the skills flake as an input and a module that imports its
home-manager module into every host via home-manager.sharedModules,
exposing programs.agents.skills. The global skill selection lives in
modules/agent-skills.nix and is empty for now; per-skill granularity
comes from the flake's own listOf-package option, and the placement
self-gates on the agent harness being enabled.
2026-07-23 08:35:32 -04:00
23b1a30c2a fix(fish): bind eza --icons so ls accepts a path argument
The ls/la/ll/lt aliases ended with a bare --icons, whose WHEN value is
optional, so a trailing path was parsed as that value: `ls .claude`
failed with "invalid value '.claude' for --icons". Bind it as
--icons=always, matching --color=always.
2026-07-22 23:33:11 -04:00
ac96639c20 feat(desktop): add XDG portals module for in-app screen sharing (task 0030)
Add modules.desktop.portals, enabled by the desktop aggregator, pinning the
XDG desktop portal routing explicitly: the three interfaces the Hyprland
portal implements (screencast, screenshot, global shortcuts) go to Hyprland,
and GTK is the default for file dialogs and appearance.

The backend packages already arrive with the Hyprland compositor integration,
so this module owns only the routing, which was previously empty and rode on
the config file the Hyprland package happens to ship. Making it a first-class
module so in-app screen sharing does not depend on that incidental default.
2026-07-22 23:27:55 -04:00
ab89ba8391 feat(desktop): resolve media output paths through XDG user-dirs
Add a modules.desktop.userdirs module that declares the XDG user directories,
and route the recorder and screenshot tools through xdg-user-dir so their
output folders (Videos/Recordings, Pictures/Screenshots) follow one relocatable
source instead of a hardcoded $HOME path.
2026-07-22 23:11:16 -04:00
0e92de7eea feat(desktop): add screen recording (task 0029)
Add a wf-recorder module, enabled by the desktop aggregator. Super+Shift+R
picks a region with slurp and toggles a video-only capture (no audio), stopped
with SIGINT so the file finalises, saved to ~/Videos/Recordings. Start and
saved notifications fire via notify-send.

A Waybar custom/recording widget samples the wf-recorder process once a second
and shows a video glyph while a capture runs.
2026-07-22 22:56:56 -04:00
111b985d7d feat(fish): restore ctrl-r history pager in vi insert mode 2026-07-22 22:37:01 -04:00
37ddf4342a fix(desktop): use windowrule v3 syntax for the satty float rule (task 0028) 2026-07-22 22:22:20 -04:00
708a3ee963 feat(desktop): float the satty annotator window (task 0028) 2026-07-22 22:09:56 -04:00
9963a0dbe4 feat(desktop): add screenshot capture (task 0028)
Add a screenshot module wiring grimblast (grim + slurp) through the satty
annotation editor, enabled by the desktop aggregator. Region, active-window,
and full-screen captures each open in satty and, on confirm, land in both the
clipboard and a dated file under ~/Pictures/Screenshots.

Bound to Print / Shift+Print / Ctrl+Print rather than the spec's Super+L
family, whose keys task 0021 already holds for hjkl focus and window movement.
2026-07-22 22:04:38 -04:00
210a260735 feat(desktop): add clipboard history (task 0027)
Add a clipboard module to the desktop group, enabled through the
aggregator. Home-manager's services.cliphist runs the text and image
watchers as systemd user services bound to the graphical-session
target, and a rofi-dmenu picker recalls history on Super+Shift+V.
wl-clipboard is on PATH so the shell can pipe to and from the clipboard.
2026-07-22 21:42:53 -04:00
005928ef88 feat(desktop): add hyprlock and hypridle (task 0026)
Add a session-lock screen and idle management to the Hyprland-native
subdirectory, enabled through the desktop aggregator.

hyprlock draws through the compositor session-lock protocol, themed by
Stylix, with a centered field and clock. Super+X locks directly through a
guarded launch so the key works without the idle daemon.

hypridle locks on idle and powers the displays off, with tunable timeouts,
and locks before every suspend. Lid-close routes through logind to suspend,
which the pre-sleep lock covers, so the lid lands at a locked screen.
2026-07-22 18:37:32 -04:00
900cb6b8e8 feat(desktop): add mako notifications (task 0025)
Add a mako notification daemon module to the desktop group, enabled by
the aggregator. Toasts auto-dismiss into history after five seconds, a
do-not-disturb mode ([mode=dnd] invisible=true) suppresses display while
still recording to history, and Super+N recalls the last notification
via makoctl restore. Colors and the popup font come from Stylix's mako
target. The bar-side do-not-disturb toggle and media controls already
live in waybar.nix, so this task adds only the daemon.
2026-07-22 17:15:32 -04:00
7d9a0dae36 feat(desktop): add the rofi launcher (task 0024)
Add a rofi module to the desktop group, enabled by the aggregator.
It runs the drun application launcher, bound on Super+R. A session menu
built on the same themed rofi is bound on Super+Shift+X, and every
rofi -dmenu call inherits the Stylix theme, so later utility menus
reuse it for free.

Also give layer surfaces their own fade-in (layersIn and fadeLayersIn)
a touch quicker than the window animation, so the launcher fades in
briskly rather than snapping into place.
2026-07-22 17:03:54 -04:00
aca09d87a1 docs: move the comment conventions to the global agent instructions
The comment 'why not what', self-contained, sentence-per-line rules are
project-agnostic, so they belong in the global agent instructions rather
than this repo's conventions. Genericise the four Nix- and domain-specific
examples to their underlying principles on the way across.
2026-07-22 15:33:07 -04:00
10e68957b1 docs: require a Waybar icon mapping for new GUI apps 2026-07-22 15:27:21 -04:00
5a7593a793 feat(desktop): bind the hardware volume and media keys 2026-07-22 15:25:50 -04:00
ba881998bc fix(desktop): highlight the current day in the Waybar calendar 2026-07-22 15:20:32 -04:00
b21b4ff77e feat(desktop): add the Waybar status bar (task 0023)
Add a Waybar module to the desktop group, enabled through the aggregator,
showing workspaces with per-application icons plus a clock, MPRIS media
controls, audio, network, battery, and a do-not-disturb toggle.

The do-not-disturb widget drives mako's dnd mode and degrades gracefully
until the notifications module lands. Module glyphs are Nerd Font codepoints
decoded through JSON, backed by a system symbols font.

Add a PipeWire audio server so the bar's volume widget has a running sink to
read and drive.
2026-07-22 12:03:30 -04:00
6d1afa41ed feat(desktop): let Stylix manage tmux and fish theming (task 0022)
Narrow the terminal tools excluded from Stylix to nvim alone. nvim keeps
its dedicated gbprod/nord.nvim colorscheme, which is richer than the
base16 mapping Stylix would apply.

tmux and fish are now Stylix-managed. Drop the hand-written Nord colours
from the tmux status bar so Stylix themes the status and pane styles,
keeping the minimal layout (session name plus window list) reapplied
after Stylix so it still wins.
2026-07-22 11:38:34 -04:00
27a28b1ae7 feat(desktop): theme the graphical layer Nord with Stylix (task 0022)
Add Stylix as a flake input and a desktop theming module that resolves a
single Nord base16 scheme across the graphical surface: GTK, Qt, cursor,
and the system monospace font, plus a static Nord wallpaper drawn as a
Polar Night gradient.

Scope the theming to the graphical layer alone. The Stylix targets for
fish (at both system and home-manager level), tmux, and nixvim stay off
so the terminal tools keep their hand-written themes.
2026-07-22 10:28:03 -04:00