Introduction
This repository is a Nix Flakes-based dotfiles and system configuration, managing NixOS systems and Home Manager user environments across multiple machines.
It uses the nixos-unified framework with autowire for automatic module discovery and composition.
Repository Structure
flake.nix # Entry point, declares all inputs
→ modules/flake/toplevel.nix # Flake-level glue (formatter, packages)
→ modules/{home,nixos}/ # Reusable modules (autowired)
→ mixins/{home,nixos}/ # Platform/version-specific configs
→ configurations/{home,nixos}/ # Per-host final configurations
modules/— Core reusable NixOS/Home Manager modulesmixins/— Platform-specific (linux/darwin) and version-specific configuration layersconfigurations/— Per-host configs that compose modules + mixinsoverlays/— Nixpkgs overlays and custom packagessecrets/— SOPS-encrypted secrets (age keys, per-host)private/— Host-specific private configs (gitignored, present on deployed machines)
Architecture
Autowiring Pattern
Most default.nix files contain a single line:
{flake, ...}: flake.inputs.autowire.default ./.
This auto-discovers and composes all .nix files in the same directory into a merged NixOS/Home Manager module. Adding a new .nix file to an autowired directory automatically includes it — no manual imports needed.
Layered Composition
Configuration is built up in layers:
flake.nix— Declares all inputs; outputs are generated vianixos-unifiedmodules/flake/— Flake-level glue (formatter, packages, apps). Each.nixfile here is aflake-partsmodule auto-included by the frameworkmodules/{home,nixos}/— Reusable Home Manager / NixOS modules, autowiredmixins/{home,nixos}/— Platform-specific layers (linux, darwin) and host/container-specific configconfigurations/{home,nixos}/— Per-host final configurations that compose modules + mixins
Custom Options
modules/home/options.nix defines the me option set used throughout Home Manager modules:
me.usernameme.fullnameme.email
These are set in each host’s configuration file (e.g., configurations/home/yjpark.nix).
Overlays and Custom Packages
overlays/default.nix autowires from ./, picking up packages.nix in the overlays directory. Custom packages are automatically available as nixpkgs overlays throughout the configuration.
Key Commands
Tasks are defined in mise.toml. Use mise tasks to list all available tasks.
Daily Use
# Home Manager activation (applies user-level config)
mise run _activate-home
# Build NixOS config for current host (builds without switching by default)
mise run build-host
# Switch NixOS config (applies system-level config, requires sudo)
mise run _switch-host
Flake Maintenance
# Update all flake inputs
mise run flake-update
# Update specific inputs
mise run flake-update nixpkgs home-manager
# Show flake outputs
mise run show
# Format Nix files (uses nixpkgs-fmt)
nix fmt
Documentation
# Serve docs locally (hot-reload)
mise run docs-serve
# Build static docs site
mise run docs-build
Nix Directly
# Activate Home Manager (same as mise run _activate-home)
nix run
# Build a specific package
nix build .#docs
Home Manager
User Profiles
Two user profiles exist in this configuration:
| Username | Used On | Base Config | State Version |
|---|---|---|---|
yjpark | Physical hosts | configurations/home/yjpark.nix | 25.05 |
yj | Containers | configurations/home/yj.nix | 26.05 |
Host and Container Mixins
Host-specific configurations live in:
mixins/home/hosts/— One.nixfile per physical hostmixins/home/containers/— One.nixfile per container
Adding a .nix file to either directory automatically registers a new host or container via the autowire pattern — no manual imports needed.
Activation Logic
modules/flake/activate-home.nix defines how mise run _activate-home resolves the correct Home Manager configuration:
- Matches current hostname against known hosts → activates
yjpark@<host> - Matches current hostname against known containers → activates
yj@<host> - Falls back to
<username>@for unknown hosts (the trailing@is required by nixos-unified for bare username fallback configs)
Configuration Generation
modules/flake/home-configs.nix generates username@host entries for each host/container mixin, plus a bare username fallback (without host suffix). This populates homeConfigurations in the flake outputs.
Secrets
Secrets are managed with SOPS-nix using age encryption.
Setup
- Each host has its own age key
- Encrypted secret files live in
secrets/directories and any files matchingsecret.* - The
.sops.yamlfile at the repo root defines which age keys can decrypt which secrets
Usage
Encrypted files are automatically decrypted at activation time by sops-nix and made available to NixOS/Home Manager modules as paths under /run/secrets/.
Refer to the sops-nix documentation for creating and editing secrets.
Flake Inputs
All inputs are declared in flake.nix. This page documents the purpose of each.
Core / Framework
| Input | Purpose |
|---|---|
nixpkgs | nixos-unstable — the primary package set |
home-manager | User environment management |
flake-parts | Flake composition framework |
nixos-unified | Opinionated wrapper around flake-parts for NixOS + HM |
autowire | Custom fork (github:yjpark/autowire.nix) — auto-discovers and composes .nix files in a directory |
Software
| Input | Purpose |
|---|---|
sops-nix | Declarative secret management with SOPS + age |
nixvim | Neovim configured via Nix |
flox | Developer environments |
nixidy | Kubernetes GitOps (k8s manifest generation) |
nixos-vscode-server | VS Code remote server support on NixOS |
solaar | Logitech device manager |
llm-agents | LLM agent tools |
Desktop / UI
| Input | Purpose |
|---|---|
claude-desktop | Claude desktop application |
niri | Wayland compositor |
xremap-flake | Key remapper for Wayland/X11 |
antigravity | (UI tool) |
jjui | TUI for the jj (Jujutsu) VCS |
Active Tasks
In Progress
Setup code on incus (flakes-6zwu)
Task · normal
Todo
Done (24)
Disable terminal bell sound (flakes-0u0f)
Task · normal
Create mise-run-or-just fish function (flakes-0ycg)
Task · normal
Project-wise pane configuration in Zellij (flakes-1zlj)
Feature · normal
Deploy OneCLI as credential proxy for yolo container (flakes-2aqm)
Feature · normal
Host Caddy LAN proxy for Incus container services (flakes-68h7)
Feature · normal
Incus container ingress auto-discovery (flakes-7ckd)
Feature · normal
Unify terminal theme to Tokyo Night (flakes-hncv)
Task · normal
Add edit-in-place script (flakes-oh4v)
Task · normal
Refactor ai.nix into ai/ directory (flakes-ojyn)
Task · normal
Add mdBook documentation site (flakes-px5o)
Feature · normal
Zellij per-project auto-layouts on cd (flakes-u0yw)
Feature · normal
Add mdbook-beans tasks section to docs (flakes-zvh8)
Task · normal
All Tasks
Features
- Project-wise pane configuration in Zellij (flakes-1zlj) — Done
- Deploy OneCLI as credential proxy for yolo container (flakes-2aqm) — Done
- Host Caddy LAN proxy for Incus container services (flakes-68h7) — Done
- Incus container ingress auto-discovery (flakes-7ckd) — Done
- Ingress hub page: service dashboard with iframe embedding (flakes-bti9) — Done
- Change Zellij pane background color based on project path (flakes-dkc4) — Done
- Zellij WASM plugin for dynamic tab naming (flakes-je55) — Draft
- Niri config: declarative host-based mixin (flakes-p2qs) — Archived
- Add mdBook documentation site (flakes-px5o) — Done
- Zellij per-project auto-layouts on cd (flakes-u0yw) — Done
Tasks
- Disable terminal bell sound (flakes-0u0f) — Done
- Create mise-run-or-just fish function (flakes-0ycg) — Done
- Enhance tv-mise-tasks to handle tasks with arguments (flakes-14he) — Done
- Make j abbreviation expand conditionally to mise run or just (flakes-1cc9) — Done
- Tidy up the shell title when running zellij (flakes-2fpy) — Archived
- Remove stateVersion mixin pattern, use defaults instead (flakes-5pbu) — Archived
- Restructure home config host/container registration (flakes-6n5b) — Archived
- Setup code on incus (flakes-6zwu) — In Progress
- Resolve username from host/container dir in activate-home.nix (flakes-7aac) — Archived
- Setup markdown-oxide (flakes-7ih3) — Archived
- Automate OneCLI CA push to containers on boot (flakes-aurl) — Done
- Set up Incus across machines (flakes-bxl7) — Archived
- Auto-trigger tab completion after j abbr expands to mise run _ (flakes-c03b) — Done
- Unify terminal theme to Tokyo Night (flakes-hncv) — Done
- Set up firewalld as NixOS service and migrate existing firewall rules (flakes-jvmx) — Archived
- Setup quartz to preview and publish oxide projects (flakes-k391) — Todo
- Check MCPVault, to see whether it’s a better option for the markdowns (flakes-k3pr) — Todo
- Update CLAUDE.md to reflect current state (flakes-nyvs) — Archived
- Setup colima with incus (flakes-o04s) — Archived
- Add edit-in-place script (flakes-oh4v) — Done
- Add GitHub token via OneCLI + container git config (flakes-oi2x) — Done
- Refactor ai.nix into ai/ directory (flakes-ojyn) — Done
- Configure jjui theme for readability with Pencil Dark terminal theme (flakes-p7hc) — Done
- Integrate Home Manager as a NixOS module (flakes-rg27) — Archived
- Migrate from just to mise for task running (flakes-rg2j) — Archived
- Full jjui theme with terminal-independent hex colors (flakes-sk2r) — Done
- Inhibit idle/sleep during audio/video playback (flakes-va02) — Done
- Dynamic per-host Home Manager configs via flake module (flakes-vaiq) — Archived
- Add rtk-rewrite.sh hook to claude module with home-manager linking (flakes-y887) — Done
- Replace CopyQ with cliphist (Wayland-native clipboard manager) (flakes-yyox) — Archived
- Add mdbook-beans tasks section to docs (flakes-zvh8) — Done
Bugs
- Fix Ctrl+Shift+C in wezterm clobbering zellij clipboard (flakes-5oyz) — Archived
- Hub toolbar URL tracking for iframe navigation (flakes-dya3) — Todo
- Fix mise.toml var args bug (flakes-m3r8) — Archived
- Fix git push auth inside Incus container via OneCLI proxy (flakes-nz9y) — Done
Drafts
Features
- Project-wise pane configuration in Zellij (flakes-1zlj) — Done
- Deploy OneCLI as credential proxy for yolo container (flakes-2aqm) — Done
- Host Caddy LAN proxy for Incus container services (flakes-68h7) — Done
- Incus container ingress auto-discovery (flakes-7ckd) — Done
- Ingress hub page: service dashboard with iframe embedding (flakes-bti9) — Done
- Change Zellij pane background color based on project path (flakes-dkc4) — Done
- Zellij WASM plugin for dynamic tab naming (flakes-je55) — Draft
- Niri config: declarative host-based mixin (flakes-p2qs) — Archived
- Add mdBook documentation site (flakes-px5o) — Done
- Zellij per-project auto-layouts on cd (flakes-u0yw) — Done
Project-wise pane configuration in Zellij (flakes-1zlj)
Configure Zellij pane/tab naming to visually identify projects in multi-project sessions.
Design
State Management
- State file:
/tmp/zellij-tab-{ZELLIJ_SESSION_NAME}— one line per pane:{PANE_ID}={project_name} - Session root: Capture
$PWDat shell init asZELLIJ_SESSION_PROJECT— the “home” project for the session - Project name: Git repo root basename (
git rev-parse --show-toplevel | basename), fallback tobasename $PWD
Pane Name Behavior
| Condition | Pane name |
|---|---|
| PWD is within session’s starting project | {command} (current behavior) |
| PWD is in a different project | <{project}> {command} |
| Idle (no running command) | <{project}> or empty (current behavior) |
Updated in fish_preexec (command starts) and on PWD change.
Tab Name Behavior
On every PWD change in any pane:
- Write/update own entry in the state file
- Read all entries, collect unique project names
- If only one unique project:
rename-tab "{project}"(same as today) - If multiple:
rename-tab "{project1} | {project2} | ..."
Cleanup
fish_exitevent: Remove own pane entry from state file, rebuild tab name- Shell init: Prune entries whose pane IDs are no longer in the state file
Tasks
- Add helper to resolve project name (git root basename, fallback to basename PWD)
- Capture session project on shell init ($ZELLIJ_SESSION_PROJECT)
- Implement state file writes (per-pane project tracking)
- Update pane name logic: <project> command format when outside session project
- Update tab name logic: aggregate unique projects from state file
- Add cleanup on fish_exit
- Add stale entry pruning on shell init (simplified: natural overwrite on pane ID reuse)
Summary of Changes
Implemented in modules/home/programs/fish/init/zellij.fish:
zellij_project_name: resolves git repo root basename, fallback to basename PWDZELLIJ_SESSION_PROJECT: captured at shell init to detect foreign projects- State file (
/tmp/zellij-tab-{SESSION}): per-pane project tracking, scoped by session name - Pane names:
<project> commandwhen in foreign project, justcommandotherwise - Tab names: aggregated unique projects in pane order, joined with
| - Cleanup on
fish_exitremoves pane entry and rebuilds tab name
Deploy OneCLI as credential proxy for yolo container (flakes-2aqm)
Set up OneCLI (MITM proxy) on edger host to inject API keys for agents running in the yolo Incus LXC container. Agents get placeholder keys; OneCLI injects real credentials at the network layer.
Tasks
- Create mixins/nixos/services/onecli.nix (OCI containers + seeder service)
- Create mixins/nixos/services/secrets/onecli-secrets.txt (sops-encrypted)
- Modify configurations/nixos/edger/imports.nix (enable onecli)
- Modify mixins/home/ai/tools/claude-mcp-add-context7.bash (use placeholder key)
- Modify mixins/home/host/linux/scripts/incus/incus-launch-yolo.bash (push CA cert)
context7 Rule Fix
- Update seeder to use per-secret config (hostPattern + headerName + valuePrefix)
- Add CONTEXT7_API_KEY entry targeting context7.com with Authorization: Bearer injection
Proxy Auth & CA Trust Fixes
- Use agent token (aoc_ prefix) instead of user API key (oc_ prefix) for proxy auth
- Set /etc/onecli-proxy-auth to mode 644 so non-root users can read it
- Append OneCLI CA to existing CA bundles (handles Nix store symlinks)
- Add fish shell proxy config via Home Manager shellInit
- Guard onecli-seed-secrets in mise tasks for hosts without OneCLI
- Add verbose diagnostics to onecli-check-proxy script
Summary of Changes
Fixed three root causes preventing OneCLI proxy injection in containers:
- Wrong API key type — gateway expects agent tokens (aoc_), not user keys (oc_)
- CA trust — appended OneCLI CA directly to active CA bundles instead of using security.pki (which fails at Nix build time)
- Fish shell — profile.d scripts aren’t sourced by fish; moved proxy var setup to Home Manager’s programs.fish.shellInit
Host Caddy LAN proxy for Incus container services (flakes-68h7)
Add host-level Caddy reverse proxy to expose Incus container services to ZeroTier LAN via *.hostname.yjpark.org with Let’s Encrypt certs (Cloudflare DNS-01). Domain pattern: yolo-8080.edger.yjpark.org → http://10.100.0.100:8080.
Summary of Changes
Modified mixins/nixos/host/incus-ingress.nix:
- Added
pkgs.caddy.withPluginswith the Cloudflare DNS plugin (hash pinned tosha256-bL1cpMvDogD/...) - Added Caddyfile generation: HTTPS + HTTP fallback blocks, per-container
host_regexpmatchers - Added
sops.secrets."cloudflare-caddy-env"declaration pointing to./secrets/cloudflare-caddy.txt - Added
systemd.services.caddy.serviceConfig.EnvironmentFilefor the SOPS secret path - Added
services.firewalld.zones.public.services = ["http" "https"]
Pending user action: create mixins/nixos/host/secrets/cloudflare-caddy.txt as a SOPS-encrypted dotenv file containing CLOUDFLARE_API_TOKEN=<token>.
Incus container ingress auto-discovery (flakes-7ckd)
Caddy reverse proxy inside container that auto-discovers listening ports and serves them via hostname routing (e.g. https://3000.yolo.incus). Includes ingress and ingress-sync shell commands.
Ingress hub page: service dashboard with iframe embedding (flakes-bti9)
A hub web page served inside each container that auto-discovers running services and presents them as a navigable list with iframe embedding. Built on existing Caddy + ingress-sync infrastructure.
Design
Overview
Single hub page per container at hub.<hostname>.incus — left sidebar lists discovered services, right pane shows selected service in an iframe.
Service Discovery
Extend existing generate-ingress-config script to also produce /var/lib/ingress/services.json:
ss -tlnp→ port + PID + process name (existing logic)readlink /proc/<pid>/cwd→ working directory (new)- Shorten cwd (strip home prefix, show last 1-2 path segments)
- Output format:
[{"port": 3000, "process": "node", "cwd": "my-app", "url": "https://3000.hostname.incus"}] - Hub port excluded from the list (avoid self-reference)
Hub Page (Caddy Templates)
- Static HTML file with embedded CSS/JS, served by Caddy
templatesdirective - JS fetches
services.jsonat load time - Left sidebar: service list showing
<short_path> <process> :<port> - Right pane: iframe loading selected service URL
- Iframe failure detection → fallback “open in new tab” link
Caddy Config Changes
- New route:
hub.<hostname>.incusserving the hub HTML +services.json - Strip
X-Frame-Optionsand rewriteContent-Security-Policyheaders on all proxied routes (enables iframe embedding) - Internal TLS unchanged
Nix Integration
- All changes in
mixins/nixos/container/ingress.nix - Hub HTML as a Nix file/package
ingress-syncregenerates both Caddy config andservices.jsoningresscommand unchanged
Tasks
- Extend
generate-ingress-configto read/proc/<pid>/cwdand writeservices.json - Create hub HTML template (sidebar + iframe layout, JS service loader, iframe error fallback)
- Add Caddy route for
hub.<hostname>.incusserving hub page + services.json - Strip X-Frame-Options/CSP headers on proxied routes
- Test with multiple running services
Not in v1
- Manual label overrides
- Cross-container aggregation (host-level hub)
- Auto-refresh on service change (manual page reload for now)
- Service grouping/categorization
Summary of Changes
Implemented ingress hub page for container service dashboard:
- Hub page served at
hub.<hostname>.incus(internal) and<container>-hub.<host>.yjpark.org(external) - Service discovery via
ss -tlnpwith process name and working directory from/proc/<pid>/cwd - Sidebar + iframe layout with resizable sidebar, compact mode (icon toolbar), and service list sorted by port
- Smart URL routing: uses
https://<container>-<port>.<host>.yjpark.org(real LE certs) when accessed externally,http://<port>.<hostname>.incusinternally - Sync button triggers
generate-ingress-configvia Python API, showsingressstatus output in dismissable modal - Favicon proxy at
/api/icon/<port>fetches favicons from services (tries/favicon.icothen parses HTML<link rel=icon>), falls back to colored letter icons - Header stripping: removes
X-Frame-OptionsandContent-Security-Policyon proxied routes for iframe embedding - Host ingress: added
<container>-hubroute toincus-ingress.nixfor external access
Files changed:
mixins/nixos/container/ingress.nix— hub HTML, sync API, enhanced generate-ingress-configmixins/nixos/services/incus-ingress.nix— hub route for host-level Caddy
Change Zellij pane background color based on project path (flakes-dkc4)
Visually distinguish panes belonging to different projects by assigning each project a unique background color in Zellij. Builds on the project-wise pane naming from flakes-1zlj.
Design
Palette
8 subtle Gruvbox-adjacent background tints (base bg is #282828):
| Index | Hex | Tint |
|---|---|---|
| 0 | #2e1a1a | Red |
| 1 | #1a2e1a | Green |
| 2 | #1a1a2e | Blue |
| 3 | #2e2e1a | Yellow |
| 4 | #2e1a2e | Magenta |
| 5 | #1a2e2e | Cyan |
| 6 | #2e241a | Orange |
| 7 | #1a2e24 | Teal |
Color Assignment
- Hash project name (simple checksum) mod palette size → deterministic color index
- Same project always gets the same color
- Known limitation: hash collisions possible, acceptable for now
Behavior
- Home project (
ZELLIJ_SESSION_PROJECT): no color change, stays at terminal default - Foreign project: apply palette color via
zellij action set-pane-color --bg - On return to home project:
zellij action set-pane-color --reset - Applied on PWD change and shell init
Files Changed
modules/home/programs/fish/init/zellij.fish only.
Tasks
- Add color palette array
- Add hash function for project name → palette index
- Add
zellij_update_pane_colorfunction - Integrate into PWD hook and init block
- Reset color when returning to home project
Summary of Changes
Added to modules/home/programs/fish/init/zellij.fish:
ZELLIJ_PANE_COLORS: 8 subtle Gruvbox-adjacent bg tintszellij_project_color_index: djb2-style hash mod palette size, returns stable integer indexzellij_update_pane_color: applies color for foreign projects, resets for home project- Called from PWD hook and init block alongside existing pane/tab name updates
Zellij WASM plugin for dynamic tab naming (flakes-je55)
Background
We implemented project-aware tab naming in Zellij via fish shell hooks. After extensive iteration, the current approach uses zellij action dump-layout to parse pane names in visual order and compose tab names (e.g., flakes | litmus).
What works well (current fish implementation)
- Project detection via git root basename (
zellij_project_name) - Pane naming with
<project>prefix (zellij_update_panename) - Deterministic pane background colors per project (
zellij_update_pane_color) - Tab name composed from unique project names in visual order (
zellij_visual_projects) dump-layoutparsing gives correct visual order and avoids state file race conditions
Remaining trigger limitations (fish shell can’t solve)
- Pane moves between tabs: no fish event fires when a pane is moved — tab name only updates on next
cd - Pane detach/reattach: splitting a pane into a new tab doesn’t trigger any shell hook
- Non-shell panes: panes running editors, Claude, or other long-running processes never return to fish prompt
- All-tab updates: when a pane moves, both source and destination tabs need updating — fish can only rename the current tab
Why a WASM plugin is the right approach
- Zellij plugins receive native events:
TabUpdate,PaneUpdate,ModeUpdate - A plugin can react to pane moves, tab changes, and layout changes in real-time
- It can update ALL tab names (not just the focused tab) via
rename_tab - No shell hooks needed for tab naming — the plugin handles it entirely
- Fish hooks can focus solely on pane-level concerns (pane name, pane color)
Design considerations
- Research Zellij plugin API for tab/pane events
- Determine how to detect pane project (read pane name set by fish, or query cwd?)
- Prototype plugin that listens to layout changes and renames tabs
- Decide whether to keep fish-based pane naming or move everything to the plugin
- Package the plugin with Nix
Reference
- Zellij plugin docs: https://zellij.dev/documentation/plugins
- Current fish implementation:
modules/home/programs/fish/init/zellij.fish - Tab bar plugin already in use:
~/.config/zellij/plugins/tab-bar.wasm
Niri config: declarative host-based mixin (flakes-p2qs)
Make niri KDL config fully declarative by moving host-specific extras into per-host mixins, eliminating the manual apply-extras step.
Summary of Changes
- Added
me.niri.extraConfigoption tomodules/home/options.nix - Updated
modules/flake/home-configs.nixto support directory-based host mixins (falls back to/{host}.nixif no directory exists) - Created
mixins/home/hosts/a13/andmixins/home/hosts/edger/withdefault.nix+niri.extras.kdl - Rewrote niri config module to concatenate
config.common.kdl+config.me.niri.extraConfig - Deleted
extras.a13.kdl,extras.edger.kdl, andniri.justfilefrom old location
Add mdBook documentation site (flakes-px5o)
Create a docs/ folder using mdBook to maintain documentation about the flakes configuration. Seed initial content from CLAUDE.md, integrate with Nix flake (packages.docs, apps.docs-serve), and add mise tasks (docs-build, docs-serve).
Summary of Changes
- Created with mdBook structure (book.toml + 6 content pages seeded from CLAUDE.md)
- Added with (nix build) and
- Added and mise tasks
- Added to
Zellij per-project auto-layouts on cd (flakes-u0yw)
Spec
When cd’ing to a project directory inside Zellij, automatically open a new tab with a layout matching the project’s configuration.
Preset Layout Files
Location: modules/home/programs/zellij/layouts/ → deployed to ~/.config/zellij/layouts/
claude_terminal.kdl — claude on left (50%), terminal on right (50%)
claude_beans-terminal.kdl — claude on left (50%), right side split: beans-serve --cors-origin "*" on top, stacked terminals below
Presets have no cwd — the fish hook passes --cwd at open time, keeping presets project-agnostic.
Layout Resolution Order
.zellij-layout.kdlfile in project root (inline layout, highest priority)ZELLIJ_LAYOUT_{USERNAME}env var (e.g.,ZELLIJ_LAYOUT_yj=claude_beans-terminal) — only if$USERmatchesZELLIJ_LAYOUTenv var — generic fallback- Neither → do nothing
Env vars are set via .envrc (direnv), e.g.:
export ZELLIJ_LAYOUT=claude_terminal
export ZELLIJ_LAYOUT_yj=claude_beans-terminal
export ZELLIJ_LAYOUT_yjpark=claude_terminal
Fish Hook Logic
Extend existing PWD hook in modules/home/programs/fish/init/zellij.fish:
- Not in Zellij? → skip
- Resolve project root (existing git root detection)
- Determine layout (resolution order above)
- Guard: check
__zellij_opened_layoutsfish global var — if project root already in list → skip zellij action new-tab --layout {layout_file} --cwd {project_root}- Add project root to
__zellij_opened_layouts
State resets when fish session ends. Re-opening after terminal restart is acceptable.
Relationship to existing bean
Related to flakes-kxju (predefined layout files) — this is the broader feature that subsumes it.
Tasks
- Create
claude_terminal.kdlpreset layout - Create
claude_beans-terminal.kdlpreset layout - Extend
zellij.fishPWD hook with layout resolution and auto-open logic - Test: cd to project with
ZELLIJ_LAYOUTset →zzapplies layout to current tab - Test: stacked layout, tab/status bars, clear buffer all working
- Test:
ZELLIJ_LAYOUT_yjoverridesZELLIJ_LAYOUTfor useryj - Test:
.zellij-layout.kdlin project root takes priority (not yet tested)
Tasks
- Disable terminal bell sound (flakes-0u0f) — Done
- Create mise-run-or-just fish function (flakes-0ycg) — Done
- Enhance tv-mise-tasks to handle tasks with arguments (flakes-14he) — Done
- Make j abbreviation expand conditionally to mise run or just (flakes-1cc9) — Done
- Tidy up the shell title when running zellij (flakes-2fpy) — Archived
- Remove stateVersion mixin pattern, use defaults instead (flakes-5pbu) — Archived
- Restructure home config host/container registration (flakes-6n5b) — Archived
- Setup code on incus (flakes-6zwu) — In Progress
- Resolve username from host/container dir in activate-home.nix (flakes-7aac) — Archived
- Setup markdown-oxide (flakes-7ih3) — Archived
- Automate OneCLI CA push to containers on boot (flakes-aurl) — Done
- Set up Incus across machines (flakes-bxl7) — Archived
- Auto-trigger tab completion after j abbr expands to mise run _ (flakes-c03b) — Done
- Unify terminal theme to Tokyo Night (flakes-hncv) — Done
- Set up firewalld as NixOS service and migrate existing firewall rules (flakes-jvmx) — Archived
- Setup quartz to preview and publish oxide projects (flakes-k391) — Todo
- Check MCPVault, to see whether it’s a better option for the markdowns (flakes-k3pr) — Todo
- Update CLAUDE.md to reflect current state (flakes-nyvs) — Archived
- Setup colima with incus (flakes-o04s) — Archived
- Add edit-in-place script (flakes-oh4v) — Done
- Add GitHub token via OneCLI + container git config (flakes-oi2x) — Done
- Refactor ai.nix into ai/ directory (flakes-ojyn) — Done
- Configure jjui theme for readability with Pencil Dark terminal theme (flakes-p7hc) — Done
- Integrate Home Manager as a NixOS module (flakes-rg27) — Archived
- Migrate from just to mise for task running (flakes-rg2j) — Archived
- Full jjui theme with terminal-independent hex colors (flakes-sk2r) — Done
- Inhibit idle/sleep during audio/video playback (flakes-va02) — Done
- Dynamic per-host Home Manager configs via flake module (flakes-vaiq) — Archived
- Add rtk-rewrite.sh hook to claude module with home-manager linking (flakes-y887) — Done
- Replace CopyQ with cliphist (Wayland-native clipboard manager) (flakes-yyox) — Archived
- Add mdbook-beans tasks section to docs (flakes-zvh8) — Done
Disable terminal bell sound (flakes-0u0f)
Disable audible bell in kitty and wezterm to prevent loud alert sounds during terminal work.
Summary of Changes
- kitty: set
enable_audio_bell = no - wezterm: set
audible_bell = 'Disabled'
Create mise-run-or-just fish function (flakes-0ycg)
Create a fish wrapper function that auto-detects whether to use mise run or just based on whether mise is configured in the current directory. Update the j abbr to use it.
Summary of Changes
- Created : wrapper that checks [ { “path”: “/home/yj/agents/flakes/mise.toml”, “tools”: [] } ] output; if non-empty array, uses , otherwise falls back to
- Updated line 21: →
Enhance tv-mise-tasks to handle tasks with arguments (flakes-14he)
Update tv-mise-tasks fish function to detect required args via ‘mise tasks info –json | jq’ and either run directly or pre-fill command line.
Make j abbreviation expand conditionally to mise run or just (flakes-1cc9)
Update fish config to use abbr –function for j so it expands inline to ‘mise run’ or ‘just’ based on current directory
Tidy up the shell title when running zellij (flakes-2fpy)
Objective
Configure zellij so the terminal emulator’s tab title (kitty/wezterm tab bar) shows the zellij session name instead of the verbose <cwd> | <last command with args> format zellij currently sets.
Context
When zellij runs inside a kitty or wezterm tab, it overwrites the terminal’s tab title with its own format: <current directory> | <last command with arguments>. This is too long for a tab bar and makes tabs hard to distinguish.
Fish shell’s fish_title is already configured to show just the current directory or last command name, and works correctly when zellij is not running. The problem is specifically that zellij overrides the terminal title with its own verbose format.
Requirements
- Configure zellij to set the terminal tab title to its session name (e.g. “main”, “dev”)
- Do not modify fish_title or other shell-level title configuration
- Zellij’s internal pane/tab titles (within zellij’s own UI) are out of scope and can remain as-is
Acceptance Criteria
- When zellij is running in a kitty or wezterm tab, the tab bar shows the zellij session name
- When zellij is not running, fish_title continues to work as before (current directory or last command name)
- No regressions in zellij’s internal tab/pane display
Summary of Changes
- : Return empty string when inside zellij — zellij’s
make_terminal_titlealready shows just the session name, so an empty pane title avoids duplication (was: echo $ZELLIJ_SESSION_NAME which produced.skills | .skills) - : Removed
zellij_update_tabname_pwd(PWD event hook) andzellij_update_panename_cmd(preexec event hook) — these were driving zellij’s verbose<cwd> | <command>terminal title format. Keptzellij_update_tabnamecall at startup only, which sets the zellij internal tab name to the initial directory once.
Revision: New Approach (2026-03-17)
Previous approach (empty fish_title inside zellij) broke zellij’s internal tab/pane renaming.
New approach: revert fish/zellij files to original, configure kitty and wezterm to extract just the session name (before ’ | ’) from the terminal title for display.
- Revert fish_title.fish to original
- Revert zellij.fish to original
- Update kitty tab_title_template to use partition(’ | ’)[0]
- Add wezterm format-tab-title event handler
Summary of Revised Changes
- Reverted to original (no ZELLIJ_SESSION_NAME check)
- Reverted to original (restored and hooks)
- Updated kitty to use — strips suffix, showing only the session name
- Added wezterm event handler that extracts text before using Lua pattern matching
Remove stateVersion mixin pattern, use defaults instead (flakes-5pbu)
Set default stateVersion in base configs, delete nearly-empty mixin files, fix fallback key mismatch in activate-home.nix
Summary of Changes
- Added
home.stateVersion = "25.05"toconfigurations/home/yjpark.nix - Added
home.stateVersion = "26.05"toconfigurations/home/yj.nix - Deleted nearly-empty host mixin files: alienware-13.nix, edger.nix, hp-g1.nix, pc.nix, and containers/yolo.nix
- Restored
mixins/home/containers/.gitkeepfor future use - Simplified
mixins/home/hosts/gpd-p2.nix: removed version import, kept only incus/edger.nix import - No change needed to activate-home.nix (the trailing
@on fallback key is required by nixos-unified; home-configs.nix omits it correctly as nixos-unified adds it internally)
Restructure home config host/container registration (flakes-6n5b)
Move host/container registration to mixins/home/hosts/ and mixins/home/containers/, update home-configs.nix and activate-home.nix accordingly
Summary of Changes
- Created files for alienware-13, edger, hp-g1, pc (each imports 25.05 version mixin)
- Updated to add 25.05 version import
- Created importing 26.05 version mixin
- Deleted
- Rewrote : removed
configDirparam,mixinDirnow serves both roles - Rewrote : reads host names from hosts/ and containers/ dirs; fixed fallback key (removed trailing @)
- Removed version imports from
configurations/home/yjpark.nixandconfigurations/home/yj.nix
Verified: nix eval .#homeConfigurations --apply 'x: builtins.attrNames x' returns expected keys.
Setup code on incus (flakes-6zwu)
- https://github.com/mensfeld/code-on-incus
Resolve username from host/container dir in activate-home.nix (flakes-7aac)
Replace flat knownHosts list with separate hosts/containers lists so activate-home.nix determines username (yjpark vs yj) from which dir the hostname is found in, rather than using $(id -un). Also update home-configs.nix fallback key to use ‘username@’ format.
Summary of Changes
- : replaced flat
knownHostswith separatehostsandcontainerslists; shell script now checks hosts first (→yjpark@$_host), then containers (→yj@$_host), falling back to$(id -un)@only when hostname is unknown home-configs.nix: updated fallback key from${username}to"${username}@"to match new shell script format
Setup markdown-oxide (flakes-7ih3)
For neovim, zed, vscode
Automate OneCLI CA push to containers on boot (flakes-aurl)
OneCLI CA cert gets wiped from container CA bundles on reboot/rebuild. Extend onecli-seed-secrets to also push the CA, and add wantedBy=multi-user.target + after=incus.service so it runs on every boot.
Design
Problem: init-ingress-ca on yolo copies system CAs to /var/lib/ingress/ca-bundle.crt on boot, wiping the OneCLI CA that was previously appended by onecli-push-ca. This breaks all HTTPS through the OneCLI MITM proxy (nix downloads, curl, git, etc.) until someone manually runs onecli-push-ca yolo.
Solution: Extend onecli-seed-secrets on edger (host) to:
- Fetch OneCLI CA from
http://10.100.0.1:10254/api/gateway/ca - Push to each running agent container via
incus file push - Append to CA bundles inside the container (handling Nix store symlinks + idempotency)
Also update the systemd service:
- Add
wantedBy = ["multi-user.target"]— runs on every boot - Add
after = [..., "incus.service"]— ensures incus is up
Files to modify:
mixins/nixos/services/onecli.nix— extend seeder script + service config
Known gap: Container-side nixos-rebuild switch resets CA bundles. User must re-run sudo systemctl start onecli-seed-secrets on host. Acceptable since container rebuilds are manual.
Tasks
- Add CA push logic to seeder script in onecli.nix
- Rename
onecli-seed-secretsservice toonecli-init-ca-and-secrets(systemd service + script name) - Add
wantedByandafterincus.service to systemd service - Add
onecli-init-ca-and-secretsshell script on host (starts the systemd service via sudo) for easy manual use - Update
onecli-check-proxy.basherror messages to suggest runningonecli-init-ca-and-secretson the host - Add CA bundle verification to check script: detect broken TLS (e.g. curl to github.com fails but
--noproxyworks) and surface a clear diagnosis - Update
_switch-hostin mise.toml to reference new service name - Verify build with
mise run build-host
Summary of Changes
mixins/nixos/services/onecli.nix— Renamed serviceonecli-seed-secrets→onecli-init-ca-and-secrets. Added CA push logic (fetch from API, push to containers, append to CA bundles with symlink handling + idempotency). AddedwantedBy = ["multi-user.target"]andafter = ["incus.service"]so it runs on every boot.mise.toml— Updated_switch-hostandrestart-oneclitasks to reference new service name.mixins/home/host/linux/scripts/incus/onecli-init-ca-and-secrets.bash— New host script for easy manual triggering.mixins/home/container/scripts/onecli-check-proxy.bash— Updated error messages to point toonecli-init-ca-and-secrets. Added TLS verification check that detects “proxy works but TLS fails” scenario and surfaces clear fix instructions.
Set up Incus across machines (flakes-bxl7)
Set up Incus (container/VM manager) across all machines.
Tasks
- Create
mixins/nixos/services/incus.nix— Incus daemon with KVM, directory storage pool, NAT networking - Create
modules/home/packages/incus.nix— Incus client package + fish completions - Create
mixins/home/settings/incus/edger.nix— sample remote config for edger.yjpark.org - Include mixin in at least one NixOS host to test (edger)
Decisions
- No multi-host clustering
- Both containers and VMs (KVM enabled)
- Directory-based storage pool
- No shell abbreviations for now
- macOS colima setup is a separate future task
Reference
See clarified/2026-03-15_setup-incus.md for full clarification notes.
Summary of Changes
- : Enables incus daemon with NAT bridge network (incusbr0), directory storage pool, KVM kernel modules, and adds yjpark to the incus-admin group
- : Installs incus client and sources fish completions via shellInit
- : Home activation script that registers edger.yjpark.org as an incus remote (idempotent, skips if already present; manual cert trust still needed)
- : Added incus service mixin to edger host
Auto-trigger tab completion after j abbr expands to mise run _ (flakes-c03b)
Replace _j_abbr function with _j_expand fish function that handles expansion and triggers completion automatically when in a mise directory. Bind Space to _j_expand.
Summary of Changes
- Removed abbreviation entry from (was using function)
- Replaced function with in : detects when command line is exactly , expands to + triggers completion in mise dirs, or otherwise; falls through to normal space + abbr expansion for any other input
- Added to bind Space to (both normal and insert/vi mode)
Unify terminal theme to Tokyo Night (flakes-hncv)
Switch wezterm from Gruvbox Dark to Tokyo Night theme, and update Zellij pane background color palette from Gruvbox-adjacent tints to Tokyo Night-adjacent tints. Kitty already uses tokyo_night_night.
Summary of Changes
- wezterm: Changed
color_schemefromGruvbox Dark (Gogh)toTokyo Nightinmixins/home/gui/linux/wezterm/wezterm.lua - Zellij pane colors: Updated palette in
modules/home/programs/fish/init/zellij.fishfrom Gruvbox-adjacent tints (base#282828) to Tokyo Night-adjacent tints (base#1a1b26)
All terminal themes now unified on Tokyo Night: kitty (tokyo_night_night), wezterm (Tokyo Night), Zellij (tokyo-night-dark), and pane background tints.
Set up firewalld as NixOS service and migrate existing firewall rules (flakes-jvmx)
Objective
Replace NixOS’s built-in networking.firewall with firewalld across all hosts, enabling zone-based firewall management that integrates natively with incus.
Context
The repo currently uses NixOS’s networking.firewall — a simple declarative wrapper around iptables/nftables. This works for basic port allowlisting but doesn’t support zone-based policies. With incus now in the stack (mixins/nixos/services/incus.nix), zone-based rules are a better fit:
- Incus has built-in firewalld support — it can automatically manage zones for bridge interfaces (
incusbr0), so containers/VMs get proper network isolation without manual rules. - Already on nftables —
networking.nftables.enable = trueis set in the incus mixin. firewalld uses nftables as its default backend, so there’s no backend conflict. - Current LAN rules use raw iptables — the
extraCommandsinmixins/nixos/lan/*/firewall.nixinject iptables rules directly, which won’t work under firewalld’s managed ruleset. - Zone-based model is cleaner — instead of scattering port ranges across files, interfaces get assigned to zones with appropriate policies.
Requirements
1. Enable firewalld as a NixOS service
- Set
services.firewalld.enable = truein an appropriate NixOS module - This automatically disables
networking.firewall— both cannot be active simultaneously
2. Migrate all existing networking.firewall usages
The following files need migration:
| File | Current rules | Migration approach |
|---|---|---|
mixins/nixos/lan/my/firewall.nix | Open ports 1000-65535 for 10.0.0.0/8 (iptables) | Create a zone (e.g., “lan”) with these rules, assign LAN interface |
mixins/nixos/lan/cn/firewall.nix | Same as above | Same zone approach |
mixins/nixos/dev/ports.nix | TCP ranges 3000-3999, 8000-8999, 29000-29999 | Add port ranges to appropriate zone |
modules/nixos/services/airplay.nix | TCP 7000,7001,7100 + UDP 6000,6001,7011 | firewalld service definition or rich rules |
modules/nixos/services/clash-verge.nix | TCP ports for clash | firewalld service or zone rules |
modules/nixos/services/zerotierone.nix | UDP port for zerotier | firewalld service or zone rules |
mixins/nixos/ext4/k3s.ext4.nix | k3s TCP ports + ranges | firewalld zone for k3s |
mixins/nixos/zfs/k3s.zfs.nix | k3s TCP ports + ranges | firewalld zone for k3s |
configurations/nixos/alienware-13/ports.nix | TCP 2342 (photoprism) | firewalld service or zone rule |
3. Configure incus integration
- Ensure
incusbr0bridge interface is assigned to an appropriate firewalld zone - Verify incus can manage its own firewall rules through firewalld without manual intervention
4. Design the zone layout
Decide on zones — at minimum:
- public (default) — external-facing interface, restrictive
- trusted or custom lan zone — for
10.0.0.0/8traffic with broader access - incus zone — for
incusbr0, managed by incus
Acceptance Criteria
-
services.firewalldis enabled andnetworking.firewallis no longer used - All previously open ports/ranges remain accessible in the correct contexts
- LAN-scoped rules (
10.0.0.0/8broad access) work via firewalld zones instead of raw iptables - Incus containers/VMs have working network access through
incusbr0 - No regressions — services (airplay, clash-verge, zerotier, k3s, photoprism) remain reachable
-
nix fmtpasses on all changed files
Dependencies
- Depends on incus being configured (already done in
mixins/nixos/services/incus.nix) - Hosts not using incus should still work with firewalld (zone config just won’t include incus zone)
Setup quartz to preview and publish oxide projects (flakes-k391)
Check MCPVault, to see whether it’s a better option for the markdowns (flakes-k3pr)
Update CLAUDE.md to reflect current state (flakes-nyvs)
Fix outdated command names, module descriptions, overlays, flake inputs, and add Home Manager activation subsection
Summary of Changes
- Fixed → On branch dev
Changes not staged for commit:
(use “git add
…” to update what will be committed) (use “git restore …” to discard changes in working directory) modified: CLAUDE.md modified: flake.lock
Untracked files:
(use “git add
no changes added to commit (use “git add” and/or “git commit -a”)
- Fixed build-host description: “dry run” → “builds without switching”
- Simplified modules/ description (removed stale subdirectory list)
- Simplified mixins/ description (removed specific version numbers)
- Simplified configurations/ description (removed stale “plus version and platform mixins”)
- Fixed overlays/default.nix description: autowires from
./not./packages/ - Added Desktop/UI flake inputs group: claude-desktop, niri, xremap-flake, antigravity, jjui
- Added Home Manager Activation subsection covering yjpark/yj profiles, host/container mixin dirs, activate-home.nix logic, and home-configs.nix structure
Setup colima with incus (flakes-o04s)
Add edit-in-place script (flakes-oh4v)
Create modules/home/scripts/edit-in-place.bash to back up and replace home-manager managed symlinks with writable copies for local testing
Summary of Changes
Created modules/home/scripts/edit-in-place.bash — autowired automatically. Script backs up the symlink to .bak, removes the symlink, copies .bak back as a regular file, and sets 644 permissions.
Add GitHub token via OneCLI + container git config (flakes-oi2x)
Add GitHub fine-grained token to OneCLI for transparent auth injection into yolo container. Add container-specific git config to rewrite SSH URLs to HTTPS.
Summary of Changes
mixins/nixos/services/onecli.nix— AddedGITHUB_TOKENsecret config with*.github.comhost pattern andAuthorization: token {value}injectionmixins/home/container/git.nix— New file: rewrites SSH GitHub URLs to HTTPS (routes through OneCLI proxy) and setsGIT_TERMINAL_PROMPT=0mixins/nixos/services/secrets/onecli-secrets.txt— User needs to add real token viasops
Refactor ai.nix into ai/ directory (flakes-ojyn)
Convert mixins/home/host/common/ai.nix into a folder (ai/), moving scripts/installs/ under ai/installs/ and extracting inline scripts into bash files.
Summary of Changes
- Converted
mixins/home/host/common/ai.nix→ai/directory ai/default.nix: autowire pattern for extensibilityai/ai.nix: package declarations (bun, nodejs_25)ai/installs/default.nix: gatherScriptPackages_bash- Moved
scripts/installs/install-claude-mcp-context7.bashandinstall-claude-mcp-verena.bash→ai/installs/ - Extracted inline clone-beans and install-ccline scripts into
ai/installs/ - Removed
scripts/installs/directory (now empty)
Configure jjui theme for readability with Pencil Dark terminal theme (flakes-p7hc)
Add settings.ui.colors overrides to programs.jjui in modules/home/programs/jujutsu.nix to fix readability issues with the Pencil Dark (Gogh) WezTerm color scheme. The default dark theme uses ‘bright black’ for dimmed text and selected backgrounds, which is too dark/invisible in Pencil Dark.
Summary of Changes
Added settings.ui.colors overrides to programs.jjui block in modules/home/programs/jujutsu.nix. Key fixes:
dimmed:"bright black"→"#808080"(mid-gray, readable in Pencil Dark)selected/revisions details selectedbg:"bright black"→"#3a3a3a"revset completionbg:"black"→"#1a1a1a"revset completion dimmed: same fix asdimmed
Config verified at ~/.config/jjui/config.toml after just activate-home.
Integrate Home Manager as a NixOS module (flakes-rg27)
Create modules/nixos/settings/home-manager.nix to import home-manager as a NixOS module so nixos-rebuild switch handles both system and home config in one command.
Summary of Changes
Created modules/nixos/settings/home-manager.nix which:
- Imports
home-manager.nixosModules.home-manager - Sets
useGlobalPkgs = trueanduseUserPackages = true - Passes
extraSpecialArgswith theflakeargument so home modules get their expected{flake, ...}argument - Wires
home-manager.users.yjparkto importconfigurations/home/yjpark.nixplus any host-specific mixin frommixins/home/hosts/<hostname>.nix
The module is auto-included in all NixOS hosts via the autowired modules/nixos/settings/ directory. Verified with nix flake check and just build-host — home-manager generation is now built as part of the NixOS system build.
Migrate from just to mise for task running (flakes-rg2j)
Add mise.toml files and shell abbreviations to migrate task running from just to mise. Keep justfiles intact during transition.
Summary of Changes
- Created at repo root with all 12 tasks from the main justfile
- Created with task
- Created with 4 preset tasks (update-presets + 3 individual)
- Added and abbreviations to fish () and nushell ()
- Justified files remain intact for the transition period
- All tasks verified working via
mise tasksandmise run vcs-status
Full jjui theme with terminal-independent hex colors (flakes-sk2r)
Replace the partial Pencil Dark fix with a complete theme using only hex/ANSI256 colors (no ANSI 0-15) so it looks good on any dark terminal.
Summary of Changes
Replaced partial Pencil Dark fix with a complete One Dark-inspired theme in modules/home/programs/jujutsu.nix. Covers all 60+ color keys: core, flash, confirmation, help, revisions list/details, revset/completion, status, menu, picker, oplog, evolog, rebase/squash/duplicate/revert/set_parents, input, choose. All colors use explicit hex values — no ANSI 0-15 — so the theme is terminal-agnostic.
Inhibit idle/sleep during audio/video playback (flakes-va02)
Add sway-audio-idle-inhibit to prevent hypridle from powering off monitors when audio is playing (e.g. YouTube, Netflix in Chrome). Changes: add package to hypridle.nix and spawn at niri startup.
Summary of Changes
- Added
sway-audio-idle-inhibitpackage tomixins/home/gui/linux/services/hypridle.nix - Added
spawn-at-startup "sway-audio-idle-inhibit"to niri config (config.common.kdl)
The daemon monitors PipeWire/PulseAudio for active audio streams and sends a Wayland idle inhibit signal, preventing hypridle from powering off monitors during video/audio playback.
Dynamic per-host Home Manager configs via flake module (flakes-vaiq)
Create home-hosts.nix flake module to auto-generate homeConfigurations for all NixOS hosts, with optional host mixins. Delete per-host config files.
Summary of Changes
- Created
modules/flake/home-hosts.nix: dynamically generates homeConfigurations for all NixOS hosts by readingconfigurations/nixos/dirs, with optional per-host mixins frommixins/home/hosts/<host>.nix - Created
mixins/home/hosts/gpd-p2.nix: imports incus/edger mixin only for gpd-p2 - Updated
modules/flake/activate-home.nix: adds fallback to plainyjparkconfig when hostname isn’t in known NixOS hosts - Deleted all per-host
configurations/home/yjpark@<host>.nixfiles (pc, edger, alienware-13, hp-g1, gpd-p2) - Key fix: used
lib.mkForceon wholelegacyPackagesattrset (not just homeConfigurations sub-key) to override autoWire’s perSystem definition
Add rtk-rewrite.sh hook to claude module with home-manager linking (flakes-y887)
Add the rtk-rewrite.sh hook file to modules/home/programs/claude/hooks/ and set up home.file linking to ~/.claude/hooks/rtk-rewrite.sh
Summary of Changes
- Created
modules/home/programs/claude/hooks/rtk-rewrite.sh(copied from~/.claude/hooks/rtk-rewrite.sh) - Created
modules/home/programs/claude/hooks/default.nixlinking it to~/.claude/hooks/rtk-rewrite.shwith executable=true - Autowire in
claude/default.nixpicks up the new hooks/ subdirectory automatically — no other files changed
Replace CopyQ with cliphist (Wayland-native clipboard manager) (flakes-yyox)
Replace CopyQ (XWayland) with cliphist for Wayland-native clipboard history. Adds fuzzel picker keybinding, television cable channel, and fish function.
Summary of Changes
- Deleted
mixins/home/gui/linux/services/copyq.nix(removed CopyQ with forceXWayland) - Created
mixins/home/gui/linux/services/cliphist.nix(enables cliphist service) - Added
Mod+Vkeybinding in niri config to open fuzzel clipboard history picker - Added
xdg.configFilefor television cable channel inmodules/home/programs/television.nix - Created
modules/home/programs/fish/functions/clip.fishfor tv-based clipboard selection - Added
tcabbreviation inmodules/home/programs/fish/abbrs.nix
Add mdbook-beans tasks section to docs (flakes-zvh8)
Integrate mdbook-beans preprocessor into the flakes documentation, mirroring the litmus project setup. Adds kanban and all-tasks pages.
Summary of Changes
- Added
mdbook-beansflake input toflake.nix - Updated
docs/book.tomlwith beans preprocessor config and sidebar CSS - Created
docs/src/beans/tasks.md(all tasks) anddocs/src/beans/kanban.md(active tasks) - Copied
docs/src/beans-sidebar.cssfrom litmus for bean styling - Updated
docs/src/SUMMARY.mdwith Kanban and All Tasks links - Updated
modules/flake/docs.nixto include mdbook-beans and .beans data in build - Updated
mise.tomldocs tasks to use nix (ensuring mdbook-beans is available)
Bugs
- Fix Ctrl+Shift+C in wezterm clobbering zellij clipboard (flakes-5oyz) — Archived
- Hub toolbar URL tracking for iframe navigation (flakes-dya3) — Todo
- Fix mise.toml var args bug (flakes-m3r8) — Archived
- Fix git push auth inside Incus container via OneCLI proxy (flakes-nz9y) — Done
Fix Ctrl+Shift+C in wezterm clobbering zellij clipboard (flakes-5oyz)
Copy (to system clipboard) doesn’t work when using wezterm → incus shell → zellij. Root cause: Ctrl+Shift+C runs CopyTo(‘Clipboard’) which copies empty terminal selection, overwriting what zellij put in clipboard via OSC 52. Fix: only copy when there’s an actual selection.
Summary of Changes
Changed binding in from a direct action to a callback that first checks if there’s an actual terminal selection. Only copies when selection is non-empty, matching kitty’s behavior and preventing the clipboard from being clobbered when zellij has already set it via OSC 52.
Hub toolbar URL tracking for iframe navigation (flakes-dya3)
The ingress hub page toolbar should display the current URL of the active iframe as the user navigates within it. Current implementation uses same-origin proxy (/s/
Context
The hub page (mixins/nixos/container/ingress.nix) embeds services in iframes. The toolbar at the top should show the current URL of the iframe content.
Current approach (not working)
- Caddy proxy routes:
handle_path /s/<port>/*in hub config proxies tolocalhost:<port>, making iframe same-origin - Iframe src uses
/s/<port>/instead of cross-originhttp://<port>.yolo.incus setIntervalpollsframe.contentWindow.locationevery 500ms- Translates proxy path back to real service URL for display
Known issues
- Apps with absolute paths (e.g.
<a href="/dashboard">) break under path-based proxy — the iframe navigates to/dashboardon the hub domain instead of/s/<port>/dashboard, which 404s - The proxy routes are generated dynamically by
generate-ingress-config(needsingress-syncafter rebuild) - Needs investigation: may need to verify Caddy config is actually being regenerated with the HUB_ROUTES
Alternative approaches to consider
- Inject postMessage script via response body rewriting (complex, no native Caddy support)
- Use a sub-path rewriting middleware or Caddy plugin
- Accept cross-origin limitation and only show base URL (simplest)
- Proxy all traffic through the Python sync API server (single-threaded bottleneck)
Files
mixins/nixos/container/ingress.nix— hub HTML, Caddy config generation, Python API server
Fix mise.toml var args bug (flakes-m3r8)
Remove unused args and fix the var=true arg to be optional (default=‘’) in mise.toml
Summary of Changes
- : removed unused — task never needed extra args
- : changed to so it’s optional
- : removed unused trailing
- : removed unused trailing
Fix git push auth inside Incus container via OneCLI proxy (flakes-nz9y)
git push inside the container failed with ‘could not read Username for https://github.com: terminal prompts disabled’. Two root causes were identified and fixed.
Summary of Changes
Problem
git push inside the Incus container failed with:
fatal: could not read Username for 'https://github.com': terminal prompts disabled
The OneCLI MITM proxy was correctly configured and the proxy was being used, but two issues prevented authentication from working.
Root Cause 1: gh credential helper override
Home Manager’s programs.gh module auto-registers gh auth git-credential as a git credential helper for github.com. The lib.mkForce "" in git.nix cleared the helper list, but gh re-added its helper after the clear. Git would call gh auth git-credential, which tried the placeholder GH_TOKEN=onecli-managed, failed, then fell back to terminal prompt.
Fix: programs.gh.gitCredentialHelper.enable = false in mixins/home/container/git.nix
Root Cause 2: Wrong auth format for GitHub’s git HTTP endpoint
OneCLI was injecting Authorization: token <PAT> which works for GitHub’s REST API (api.github.com) but NOT for GitHub’s git smart HTTP endpoint (github.com). The git endpoint requires Basic auth: Authorization: Basic base64(x-access-token:<PAT>).
Fix: Added encoding = "basic-auth" field to GITHUB_TOKEN secret config in mixins/nixos/services/onecli.nix. The seeder script base64-encodes x-access-token:<value> when this encoding is set, and the valueFormat was changed to Basic {value}.
Key diagnostic commands
GIT_CURL_VERBOSE=1 git push— revealed git wasn’t sendingProxy-Authorization(fixed byhttp.proxyAuthMethod = basic)- OneCLI gateway logs (
podman logs onecli) — confirmed MITM + injection was happening but GitHub returned 401 - Certificate issuer in TLS handshake — confirmed OneCLI CA vs real cert (MITM vs tunnel)
Files modified
mixins/home/container/git.nix— disabled gh credential helper, removed extraHeader workaround, addedproxyAuthMethod = basicmixins/nixos/services/onecli.nix— changed GITHUB_TOKEN to Basic auth format with base64 encoding at seed time
Drafts
Zellij WASM plugin for dynamic tab naming (flakes-je55)
Background
We implemented project-aware tab naming in Zellij via fish shell hooks. After extensive iteration, the current approach uses zellij action dump-layout to parse pane names in visual order and compose tab names (e.g., flakes | litmus).
What works well (current fish implementation)
- Project detection via git root basename (
zellij_project_name) - Pane naming with
<project>prefix (zellij_update_panename) - Deterministic pane background colors per project (
zellij_update_pane_color) - Tab name composed from unique project names in visual order (
zellij_visual_projects) dump-layoutparsing gives correct visual order and avoids state file race conditions
Remaining trigger limitations (fish shell can’t solve)
- Pane moves between tabs: no fish event fires when a pane is moved — tab name only updates on next
cd - Pane detach/reattach: splitting a pane into a new tab doesn’t trigger any shell hook
- Non-shell panes: panes running editors, Claude, or other long-running processes never return to fish prompt
- All-tab updates: when a pane moves, both source and destination tabs need updating — fish can only rename the current tab
Why a WASM plugin is the right approach
- Zellij plugins receive native events:
TabUpdate,PaneUpdate,ModeUpdate - A plugin can react to pane moves, tab changes, and layout changes in real-time
- It can update ALL tab names (not just the focused tab) via
rename_tab - No shell hooks needed for tab naming — the plugin handles it entirely
- Fish hooks can focus solely on pane-level concerns (pane name, pane color)
Design considerations
- Research Zellij plugin API for tab/pane events
- Determine how to detect pane project (read pane name set by fish, or query cwd?)
- Prototype plugin that listens to layout changes and renames tabs
- Decide whether to keep fish-based pane naming or move everything to the plugin
- Package the plugin with Nix
Reference
- Zellij plugin docs: https://zellij.dev/documentation/plugins
- Current fish implementation:
modules/home/programs/fish/init/zellij.fish - Tab bar plugin already in use:
~/.config/zellij/plugins/tab-bar.wasm