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 flake-parts with autowire (from jig) for automatic module discovery and composition.
Repository Structure
flake.nix # Entry point, declares all inputs
→ flake/*.nix # Flake-level glue (configs, formatter, activation)
→ packs/{home,nixos}/ # Autowired packs (common, host, container, gui)
→ mixins/{home,nixos}/ # Opt-in configs (manually imported by configurations)
→ home/*.nix # Per-user base Home Manager configurations
→ nixos/{hosts,containers} # Per-host/container NixOS configurations
packs/— Autowired collections — import a pack, get everything in itmixins/— Opt-in configuration pieces (services, hardware, versions)home/— Base Home Manager configs (yjpark.nix,yj.nix)nixos/— Per-host and per-container NixOS configssecrets/— SOPS-encrypted secrets (age keys, per-host)private/— Host-specific private configs (gitignored, present on deployed machines)
Architecture
Autowiring Pattern
Pack roots use wireImportsRecursively to auto-discover all .nix files recursively:
{flake, ...}: flake.inputs.autowire.wireImportsRecursively ./.
Subdirectories with a custom default.nix are treated as opaque — their default.nix is imported instead of recursing further. Adding a new .nix file to a pack directory automatically includes it.
Layered Composition
Configuration is built up in layers:
flake.nix— Declares all inputs; outputs generated viaflake-partsflake/*.nix— Flake-level glue (configs, formatter, activation). Each.nixfile is aflake-partsmodule imported byflake.nixpacks/{home,nixos}/— Autowired packs (common, host, container, gui). Import a pack, get everything in itmixins/{home,nixos}/— Opt-in configuration pieces (services, hardware, versions), manually imported by host configshome/*.nix— Per-user base Home Manager configurationsnixos/{hosts,containers}/— Per-host and per-container NixOS configurations
Custom Options
packs/home/common/options.nix defines the me option set used throughout Home Manager modules:
me.usernameme.fullnameme.email
These are set in each user’s base config (e.g., home/yjpark.nix).
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 | home/yjpark.nix | 25.05 |
yj | Containers | 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 — no manual imports needed.
Activation Logic
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 bare
<username>for unknown hosts
Configuration Generation
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 |
jig | github:edger-dev/jig — provides autowire lib for auto-discovering and composing .nix files |
Software
| Input | Purpose |
|---|---|
sops-nix | Declarative secret management with SOPS + age |
nixvim | Neovim configured via Nix |
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
No tasks
Todo
Container-first infrastructure (flakes-qbvb)
Epic · normal · (7/12 done)
- ○ lima->incus host-routable VM IP + self-healing route (macOS) (flakes-1huh)
- ○ Configure & run ZeroClaw (config, secrets, service) (flakes-8itl)
- ✓ Migrate container IPs to new scheme (flakes-cjao)
- ✓ Drop nixos-unified, use raw flake-parts (flakes-dght)
- ○ Declarative incus fleet management (flakes-ihxf)
- ✓ Fix CA cert injection to be declarative (flakes-krtw)
- ○ MCP OAuth bridge: Mac-side forwarder + Phase 2 + e2e (external) (flakes-mqe9)
- ○ Package beans-serve (web UI) from source via nix (flakes-qe0o)
- ✓ Containerize OneCLI (flakes-t9u2)
- ✓ Setup SearXNG search for agents (flakes-woco)
- ✓ Cross-container MCP OAuth callback bridge (flakes-wq6s)
- ✓ Container-to-host network isolation (flakes-x821)
Done (9)
Install latest beans via nix flake (flakes-6ne0)
Task · normal
Install Zeroclaw (flakes-kj17)
Task · normal
Unify incus container image build tasks (flakes-p8hf)
Task · normal
All Tasks
Epics
- Container-first infrastructure (flakes-qbvb) — Todo
- lima->incus host-routable VM IP + self-healing route (macOS) (flakes-1huh) — Draft
- Configure & run ZeroClaw (config, secrets, service) (flakes-8itl) — Draft
- Migrate container IPs to new scheme (flakes-cjao) — Done
- Drop nixos-unified, use raw flake-parts (flakes-dght) — Done
- Declarative incus fleet management (flakes-ihxf) — Draft
- Fix CA cert injection to be declarative (flakes-krtw) — Done
- MCP OAuth bridge: Mac-side forwarder + Phase 2 + e2e (external) (flakes-mqe9) — Draft
- Package beans-serve (web UI) from source via nix (flakes-qe0o) — Draft
- Containerize OneCLI (flakes-t9u2) — Done
- Setup SearXNG search for agents (flakes-woco) — Done
- Cross-container MCP OAuth callback bridge (flakes-wq6s) — Done
- Container-to-host network isolation (flakes-x821) — Done
Features
- Project-wise pane configuration in Zellij (flakes-1zlj) — Done ·
archived - Deploy OneCLI as credential proxy for yolo container (flakes-2aqm) — Done ·
archived - Host Caddy LAN proxy for Incus container services (flakes-68h7) — Done ·
archived - Incus container ingress auto-discovery (flakes-7ckd) — Done ·
archived - Ingress hub page: service dashboard with iframe embedding (flakes-bti9) — Done ·
archived - Extract ingress hub scripts for Ubuntu compatibility (flakes-d4np) — Done ·
archived - Change Zellij pane background color based on project path (flakes-dkc4) — Done ·
archived - Declarative incus fleet management (flakes-ihxf) — Draft
- Zellij WASM plugin for dynamic tab naming (flakes-je55) — Draft
- Niri config: declarative host-based mixin (flakes-p2qs) — Done ·
archived - Add mdBook documentation site (flakes-px5o) — Done ·
archived - Fish functions for jj workspace management (flakes-rhqy) — Done ·
archived - Zellij per-project auto-layouts on cd (flakes-u0yw) — Done ·
archived - Setup SearXNG search for agents (flakes-woco) — Done
- Cross-container MCP OAuth callback bridge (flakes-wq6s) — Done
- Add git worktree support to workspace commands (flakes-y87y) — Done ·
archived
Tasks
- Disable terminal bell sound (flakes-0u0f) — Done ·
archived - Create mise-run-or-just fish function (flakes-0ycg) — Done ·
archived - Enhance tv-mise-tasks to handle tasks with arguments (flakes-14he) — Done ·
archived - Make j abbreviation expand conditionally to mise run or just (flakes-1cc9) — Done ·
archived - lima->incus host-routable VM IP + self-healing route (macOS) (flakes-1huh) — Draft
- Convert onecli-reseed to a manually-invoked script (flakes-2ey6) — Done
- Tidy up the shell title when running zellij (flakes-2fpy) — Done ·
archived - Remove stateVersion mixin pattern, use defaults instead (flakes-5pbu) — Done ·
archived - Restructure home config host/container registration (flakes-6n5b) — Done ·
archived - Install latest beans via nix flake (flakes-6ne0) — Done
- Resolve username from host/container dir in activate-home.nix (flakes-7aac) — Done ·
archived - Setup markdown-oxide (flakes-7ih3) — Done ·
archived - Configure & run ZeroClaw (config, secrets, service) (flakes-8itl) — Draft
- Automate OneCLI CA push to containers on boot (flakes-aurl) — Done ·
archived - Set up Incus across machines (flakes-bxl7) — Done ·
archived - Auto-trigger tab completion after j abbr expands to mise run _ (flakes-c03b) — Done ·
archived - Migrate container IPs to new scheme (flakes-cjao) — Done
- Drop nixos-unified, use raw flake-parts (flakes-dght) — Done
- Unify terminal theme to Tokyo Night (flakes-hncv) — Done ·
archived - Replace autowire.nix with jig lib (flakes-iymi) — Done ·
archived - wezterm: Claude Code editing ergonomics (shipped) (flakes-jkwu) — Done
- Set up firewalld as NixOS service and migrate existing firewall rules (flakes-jvmx) — Done ·
archived - Setup quartz to preview and publish oxide projects (flakes-k391) — Draft
- Check MCPVault, to see whether it’s a better option for the markdowns (flakes-k3pr) — Done
- Install Zeroclaw (flakes-kj17) — Done
- Fix CA cert injection to be declarative (flakes-krtw) — Done
- MCP OAuth bridge: Mac-side forwarder + Phase 2 + e2e (external) (flakes-mqe9) — Draft
- Update CLAUDE.md to reflect current state (flakes-nyvs) — Done ·
archived - Setup colima with incus (flakes-o04s) — Done ·
archived - Add edit-in-place script (flakes-oh4v) — Done ·
archived - Add GitHub token via OneCLI + container git config (flakes-oi2x) — Done ·
archived - Refactor ai.nix into ai/ directory (flakes-ojyn) — Done ·
archived - MITM egress-proxy CLI: stable CA + ease-of-use (reference) (flakes-p2dp) — Draft
- Configure jjui theme for readability with Pencil Dark terminal theme (flakes-p7hc) — Done ·
archived - Unify incus container image build tasks (flakes-p8hf) — Done
- Setup OpenCode and Claude for spacebot container (flakes-pr3i) — Done
- Package beans-serve (web UI) from source via nix (flakes-qe0o) — Draft
- Integrate Home Manager as a NixOS module (flakes-rg27) — Done ·
archived - Migrate from just to mise for task running (flakes-rg2j) — Done ·
archived - Trial MCPVault as an MCP server for the notes vault (flakes-rv2o) — Draft
- Full jjui theme with terminal-independent hex colors (flakes-sk2r) — Done ·
archived - Containerize OneCLI (flakes-t9u2) — Done
- Inhibit idle/sleep during audio/video playback (flakes-va02) — Done ·
archived - Dynamic per-host Home Manager configs via flake module (flakes-vaiq) — Done ·
archived - Container-to-host network isolation (flakes-x821) — Done
- Add rtk-rewrite.sh hook to claude module with home-manager linking (flakes-y887) — Done ·
archived - Replace CopyQ with cliphist (Wayland-native clipboard manager) (flakes-yyox) — Done ·
archived - Add mdbook-beans tasks section to docs (flakes-zvh8) — Done ·
archived
Bugs
- Fix Ctrl+Shift+C in wezterm clobbering zellij clipboard (flakes-5oyz) — Done ·
archived - Auto-restart firewalld when zone config changes (flakes-ja9k) — Done
- Fix mise.toml var args bug (flakes-m3r8) — Done ·
archived - Fix git push auth inside Incus container via OneCLI proxy (flakes-nz9y) — Done ·
archived - Pass currentSystem to NixOS-integrated home-manager extraSpecialArgs (flakes-sytx) — Done
Drafts
- lima->incus host-routable VM IP + self-healing route (macOS) (flakes-1huh) — Draft
- Configure & run ZeroClaw (config, secrets, service) (flakes-8itl) — Draft
- Declarative incus fleet management (flakes-ihxf) — Draft
- Zellij WASM plugin for dynamic tab naming (flakes-je55) — Draft
- Setup quartz to preview and publish oxide projects (flakes-k391) — Draft
- MCP OAuth bridge: Mac-side forwarder + Phase 2 + e2e (external) (flakes-mqe9) — Draft
- MITM egress-proxy CLI: stable CA + ease-of-use (reference) (flakes-p2dp) — Draft
- Package beans-serve (web UI) from source via nix (flakes-qe0o) — Draft
- Trial MCPVault as an MCP server for the notes vault (flakes-rv2o) — Draft
Epics
Container-first infrastructure (flakes-qbvb)
Migrate services to containers, isolate host, restructure Nix config.
IP Scheme
10.100.0.1 host (bridge)
10.100.0.2+ infra containers
.002 onecli
.003 searxng
10.100.0.100+ agent containers
.100 yolo
.101 spacebot
.102 hermes
10.100.0.200+ scratch
.200 ubuntu
Architecture
- Host becomes thin bridge: Caddy ingress, dnsmasq, SOPS secrets, onecli-seeder
- OneCLI moves to its own container
- SearXNG in its own container
- Container→host traffic blocked (except DHCP/DNS) via nftables
- Container↔container unrestricted on bridge
- Secret flow: Host SOPS → seeder calls OneCLI API at .002 → pushes proxy-auth/CA to agent containers
- CA cert injection made declarative via security.pki.certificateFiles
- Drop nixos-unified for raw flake-parts (separate host/container config namespaces)
Features
- Project-wise pane configuration in Zellij (flakes-1zlj) — Done ·
archived - Deploy OneCLI as credential proxy for yolo container (flakes-2aqm) — Done ·
archived - Host Caddy LAN proxy for Incus container services (flakes-68h7) — Done ·
archived - Incus container ingress auto-discovery (flakes-7ckd) — Done ·
archived - Ingress hub page: service dashboard with iframe embedding (flakes-bti9) — Done ·
archived - Extract ingress hub scripts for Ubuntu compatibility (flakes-d4np) — Done ·
archived - Change Zellij pane background color based on project path (flakes-dkc4) — Done ·
archived - Declarative incus fleet management (flakes-ihxf) — Draft
- Zellij WASM plugin for dynamic tab naming (flakes-je55) — Draft
- Niri config: declarative host-based mixin (flakes-p2qs) — Done ·
archived - Add mdBook documentation site (flakes-px5o) — Done ·
archived - Fish functions for jj workspace management (flakes-rhqy) — Done ·
archived - Zellij per-project auto-layouts on cd (flakes-u0yw) — Done ·
archived - Setup SearXNG search for agents (flakes-woco) — Done
- Cross-container MCP OAuth callback bridge (flakes-wq6s) — Done
- Add git worktree support to workspace commands (flakes-y87y) — Done ·
archived
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
Extract ingress hub scripts for Ubuntu compatibility (flakes-d4np)
Extract scripts from mixins/nixos/container/ingress.nix into standalone files under mixins/home/container/ingress/, refactor NixOS module to reference them, and add ubuntu-setup-ingress.bash for Ubuntu container support.
Tasks
- Extract hub-index.html from ingress.nix
- Extract generate-ingress-config.bash
- Extract ingress-sync-api.py
- Extract ingress.bash (status script)
- Extract ingress-sync.bash
- Create mixins/home/container/ingress/default.nix (Nix packages)
- Refactor mixins/nixos/container/ingress.nix to use shared scripts
- Create ubuntu-setup-ingress.bash
Summary of Changes
Extracted inline scripts from the NixOS module into standalone files:
- Scripts live in
mixins/home/container/ingress/(source of truth) - Copies in
mixins/nixos/container/ingress-scripts/(needed due to Nix flake autowire boundaries) - NixOS module refactored to use
builtins.readFileon local copies - Home Manager module adds scripts to PATH via
home.packages ubuntu-setup-ingress.bashhandles apt packages, systemd units, dnsmasq, resolved, sudo rules- All three configs (yolo, yolo-lima, home/yj) evaluate successfully
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
Declarative incus fleet management (flakes-ihxf)
NixOS module to declare containers (image, IP, devices, ACLs) and have them rebuild/restart on _switch-host. Long-term goal.
Design notes (night-shift planning pass, 2026-06-30)
Not implemented this shift: building a declarative incus orchestration layer would mutate the live fleet (create/configure/restart containers) — unverifiable in CI and risky to run unattended, and it hinges on design decisions that are architect calls. Captured the design surface so it’s actionable later. Set to draft (needs design refinement before implementation), per the repo’s planning≠implementation rule.
Current state (what this would supersede / build on)
- Imperative launch/restart scripts:
packs/home/host/linux/scripts/incus/incus-launch-*.bash,incus-restart-*.bash,incus-setup-storage-ext4.bash— per-container shell, run by hand. - Host incus enablement:
packs/home/host/linux/incus.nix,packs/nixos/host/incus.nix,mixins/home/settings/incus/. - Static fleet list already exists in
mixins/nixos/services/incus-ingress.nixasingressContainers = [ { name; ip; } ... ](onecli .2, searxng .3, yolo .100, spacebot .101, hermes .102). A declarative fleet module should become the single source of truth feeding both container creation and this ingress list (DRY). - Containers are a mix: NixOS containers (
nixos/containers/*) and Ubuntu (packs/home/container/scripts/ubuntu-setup-*.bash).
Key design decisions (need a call before implementation)
- Option schema: e.g.
services.incus.fleet.<name> = { image; ipv4; devices; profiles; acls; storageVolumes; autostart; ... }. - Reconciliation model: activation script / systemd oneshot on
_switch-hostthat diffs declared vsincus list --format jsonand creates/configures/restarts. Must be idempotent and non-destructive (never delete/recreate a container with data; the egress-proxy work showed CA/postgres data lives on custom storage volumes that must persist across recreate). - Image source: build NixOS container images from this flake (reproducible) vs. pull Ubuntu images vs. both. Affects how “image” is declared.
- Scope of management: devices (proxy/disk/gpu),
ipv4.addressoverrides (currently set by hand viaincus config device override), network ACLs, profiles, storage volumes. - Restart policy: what changes trigger a restart vs. a live
incus configapply; avoid disrupting running services unnecessarily. - Migration: supersede the imperative
incus-launch-*/incus-restart-*scripts, or coexist during transition.
Proposed phased decomposition (create as child beans once decisions above are made)
fleetoption schema only (pure types, no side effects) + deriveingressContainersfrom it.- Read-only reconciler: report drift between declared fleet and
incus list(no mutation) — safe to land + verify first. - Create/ensure containers from declarations (idempotent, non-destructive; storage volumes persist).
- Device / ACL / profile / IP-override management.
- Wire restart-on-change into
_switch-host; migrate the imperative scripts.
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
Fish functions for jj workspace management (flakes-rhqy)
Convention-based jj workspace management via fish shell functions. All workspaces at ~/workspaces/
Functions
| Abbr | Full name | Behavior |
|---|---|---|
| wl | workspace-list | List all workspaces across all projects with bookmark + dirty/conflict status |
| w | workspace-cd | cd to a workspace + show brief status. Tab-completes |
| wc | workspace-create | From inside a repo, create ~/workspaces/ |
| workspace-delete | (no abbr) | Forget + remove a workspace |
Design Details
Convention
- All workspaces at ~/workspaces/
/ = basename of the main repo root - Default workspace = the main repo itself
Registry (~/.config/workspaces.conf)
- Maps project name to main repo path (e.g. llm-triage=/home/yj/code/llm-triage)
- Auto-populated by wc on first use
- Needed to discover default workspace / run jj commands from the right root
workspace-list (wl)
- Iterates all registered projects
- For each: runs jj workspace list from main repo, then for each workspace shows:
- Workspace name (default or feature name)
- Path (main repo or ~/workspaces/
/ ) - Bookmark name (or ‘no bookmark’)
- Change-id (short)
- Status: clean / dirty / conflicts
- Grouped by project
workspace-cd (w)
- w
/ → cd to workspace path + print one-line status (bookmark, change-id, dirty/conflicts) - w
/default → cd to main repo root - Tab completion for all
/ combos - w with no args → do nothing
workspace-create (wc)
- Must be run from inside a jj repo
- wc
→ jj workspace add ~/workspaces/ / - Auto-registers project in ~/.config/workspaces.conf if not present
- cd to the new workspace after creation
workspace-delete
- workspace-delete
/ → jj workspace forget (from main repo) + rm -rf the directory - No abbreviation (destructive operation)
- Tab completion same as w
Tasks
- Create workspace-list.fish function
- Create workspace-cd.fish function with completions
- Create workspace-create.fish function
- Create workspace-delete.fish function
- Add abbrs (wl, w, wc) to abbrs.nix
- Wire functions into fish module via nix (autowired automatically)
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)
Setup SearXNG search for agents (flakes-woco)
Deploy SearXNG as a self-hosted meta-search engine to provide web search capabilities for agents (spacebot, hermes, etc.).
- https://github.com/searxng/searxng
Goals
- Run SearXNG as a service accessible to agent containers over the LAN
- Provide a search API endpoint agents can call (JSON format) without external API keys
- Keep configuration declarative via Nix
Open Questions
- Run as NixOS system service in a dedicated incus container at 10.100.0.3
- Which search engines to enable by default?
- Authentication/rate-limiting for the API endpoint?
- Integration method: MCP server, direct HTTP API, or tool wrapper?
Summary of Changes
- Created
nixos/containers/searxng/NixOS container config:- Static IP 10.100.0.3, services.searxng with JSON format enabled
- Engines: google, bing, duckduckgo, wikipedia, github
- Listens on 0.0.0.0:8080; no auth needed (LAN-only)
- Uses full container pack (ingress.nix auto-discovers port 8080)
- Registered “searxng” in
flake/nixos-configs.nix - Added searxng to
incus-ingress.nixingressContainers (10.100.0.3) - Added
incus-launch-searxng.bash - JSON API: http://10.100.0.3:8080/search?q=<query>&format=json
- Caddy: searxng-8080.<host>.yjpark.org → http://10.100.0.3:8080
Cross-container MCP OAuth callback bridge (flakes-wq6s)
Claude Code running inside a nested container does MCP OAuth, but the callback listener is loopback-only and the redirect host is hardcoded to http://localhost:<port>/callback. The browser opens on the Mac, so localhost resolves to the Mac, not the container — today this needs a manual URL-copy + in-container curl to finish the flow.
Ground truth (from the CC binary)
- Callback port default is a random ephemeral port.
MCP_OAUTH_CALLBACK_PORTenv wins unconditionally (no availability check) — pinning is mandatory.- Listener binds
127.0.0.1only; redirect host/path is hardcoded (only the port is configurable, not the host).
Fix — pinned-port loopback bridge
Mac browser -> http://localhost:3118/callback
-> [Mac launchd] socat 127.0.0.1:3118 -> <container-ip>:3118
-> [container] Caddy site http://:3118 { bind <container-ip>; reverse_proxy 127.0.0.1:3118 }
-> Claude Code listener @ 127.0.0.1:3118 (done)
- Pin
MCP_OAUTH_CALLBACK_PORT=3118in the container env. - Container-side bridge relays
<container-ip>:3118 -> 127.0.0.1:3118. A socket bound tolowon’t serve a connection arriving oneth0, so a bridge is required. Reuse the already-running reverse proxy (Caddy) instead of a new socat daemon.- Gotcha: a Caddy site-address host controls Host matching, not the listen interface.
http://<ip>:3118listens on0.0.0.0and steals CC’s127.0.0.1:3118(env port has no availability check). Usehttp://:3118 { bind <ip> }so it binds the container IP only, leaving loopback free for CC.
- Gotcha: a Caddy site-address host controls Host matching, not the listen interface.
- Mac forwarder = launchd socat over the existing host→container route.
Phase 2 — auto-open the authorize URL (true zero-touch)
Mac launchd listener that runs open <url>; container sets BROWSER to a script that ships the URL to it (+ an xdg-open shim). Restrict by shared secret, NOT subnet — the bridge NATs container traffic so the Mac sees the VM’s source IP, not the container’s.
Scope in this repo
- In-repo (NixOS container configs under
nixos/containers/): theMCP_OAUTH_CALLBACK_PORT=3118env var and the Caddyhttp://:3118 { bind <ip> }site. Container IPs are already statically assigned (seemixins/nixos/services/incus-ingress.nix). - External (not managed by this flake): the Mac-side launchd socat forwarder and the Phase-2
open <url>listener live on the macOS host (lima VM), outside this repo. - Verification caveat: the end-to-end OAuth flow can only be confirmed against the live container + Mac; a build here proves the config evaluates, not that the bridge works.
Todo
- Pinned
MCP_OAUTH_CALLBACK_PORT=3118(NixOS container env viapacks/nixos/container/mcp-oauth-bridge.nix; also fed to the ingress-sync service) - Caddy bridge site emitted by the ingress generator (
generate-ingress-config.bash, both nixos + home copies): detects container IP viaip route getand writes/var/lib/caddy/mcp-oauth.confwithhttp://:3118 { bind <ip>; reverse_proxy 127.0.0.1:3118 }. Verified withcaddy validate - Mac-side launchd socat forwarder documented as external (see follow-up bean)
- (external follow-up) Phase 2:
BROWSERshim + xdg-open script + shared-secret auth - (external follow-up) Verify end-to-end against a live container + Mac forwarder
Summary of Changes
Implemented the in-repo (container-side) half of the bridge and verified it; the macOS-host half is tracked separately in flakes-mqe9.
Files
packs/nixos/container/mcp-oauth-bridge.nix(new): pinsMCP_OAUTH_CALLBACK_PORT=3118(login-shell env for Claude Code) and passes the same port to theingress-syncsystemd service.packs/nixos/container/ingress-scripts/generate-ingress-config.bashandpacks/home/container/ingress/generate-ingress-config.bash(identical edits): the ingress generator now detects the container IP (ip -4 route get 1) and writes/var/lib/caddy/mcp-oauth.conf=http://:3118 { bind <ip>; reverse_proxy 127.0.0.1:3118 }, before the existing atomiccaddy reload. The IP-detect assignment uses|| trueso a missing default route at early boot can’t abort the whole generator underset -euo pipefail.
Why a dedicated IP-bound site: CC’s callback listener binds 127.0.0.1:3118 only and its env-pinned port has no availability check, so the bridge must bind the container IP (eth0) — not 0.0.0.0 — leaving loopback free for CC.
Verified: nix eval confirms the env var + ingress-sync Environment; the full yolo system toplevel evaluates; caddy validate returns Valid configuration for the generated bridge site (real IP 10.100.0.100) in a Caddyfile mirroring the real structure; bash -n passes; both generator copies remain identical. A review subagent verified module merge, no port collision with the auto-generated 3118.<host>.incus site, env delivery, and found the pipefail bug (fixed).
Not done (external → flakes-mqe9): the Mac launchd socat forwarder (localhost:3118 → <container-ip>:3118), Phase 2 zero-touch auto-open, and live end-to-end verification — these live on the macOS host/lima VM, outside this repo.
Add git worktree support to workspace commands (flakes-y87y)
Extend workspace-create, workspace-list, workspace-cd, workspace-delete, and completions to support git worktree in addition to jj workspaces. Runtime VCS detection (jj priority over git). Git worktrees create new branches by default.
Summary of Changes
Added git worktree support to all workspace commands:
- workspace-create: detects jj vs git, uses git worktree add -b for git repos
- workspace-list: shows (jj)/(git) indicator per project, git worktrees show branch/hash/status
- workspace-cd: shows git branch/hash/status when navigating to git worktrees
- workspace-delete: uses git worktree remove for git repos
- completions: lists git worktrees alongside jj workspaces
- New helper __workspace_list_git_entry.fish for git worktree display formatting
Tasks
- Disable terminal bell sound (flakes-0u0f) — Done ·
archived - Create mise-run-or-just fish function (flakes-0ycg) — Done ·
archived - Enhance tv-mise-tasks to handle tasks with arguments (flakes-14he) — Done ·
archived - Make j abbreviation expand conditionally to mise run or just (flakes-1cc9) — Done ·
archived - lima->incus host-routable VM IP + self-healing route (macOS) (flakes-1huh) — Draft
- Convert onecli-reseed to a manually-invoked script (flakes-2ey6) — Done
- Tidy up the shell title when running zellij (flakes-2fpy) — Done ·
archived - Remove stateVersion mixin pattern, use defaults instead (flakes-5pbu) — Done ·
archived - Restructure home config host/container registration (flakes-6n5b) — Done ·
archived - Install latest beans via nix flake (flakes-6ne0) — Done
- Resolve username from host/container dir in activate-home.nix (flakes-7aac) — Done ·
archived - Setup markdown-oxide (flakes-7ih3) — Done ·
archived - Configure & run ZeroClaw (config, secrets, service) (flakes-8itl) — Draft
- Automate OneCLI CA push to containers on boot (flakes-aurl) — Done ·
archived - Set up Incus across machines (flakes-bxl7) — Done ·
archived - Auto-trigger tab completion after j abbr expands to mise run _ (flakes-c03b) — Done ·
archived - Migrate container IPs to new scheme (flakes-cjao) — Done
- Drop nixos-unified, use raw flake-parts (flakes-dght) — Done
- Unify terminal theme to Tokyo Night (flakes-hncv) — Done ·
archived - Replace autowire.nix with jig lib (flakes-iymi) — Done ·
archived - wezterm: Claude Code editing ergonomics (shipped) (flakes-jkwu) — Done
- Set up firewalld as NixOS service and migrate existing firewall rules (flakes-jvmx) — Done ·
archived - Setup quartz to preview and publish oxide projects (flakes-k391) — Draft
- Check MCPVault, to see whether it’s a better option for the markdowns (flakes-k3pr) — Done
- Install Zeroclaw (flakes-kj17) — Done
- Fix CA cert injection to be declarative (flakes-krtw) — Done
- MCP OAuth bridge: Mac-side forwarder + Phase 2 + e2e (external) (flakes-mqe9) — Draft
- Update CLAUDE.md to reflect current state (flakes-nyvs) — Done ·
archived - Setup colima with incus (flakes-o04s) — Done ·
archived - Add edit-in-place script (flakes-oh4v) — Done ·
archived - Add GitHub token via OneCLI + container git config (flakes-oi2x) — Done ·
archived - Refactor ai.nix into ai/ directory (flakes-ojyn) — Done ·
archived - MITM egress-proxy CLI: stable CA + ease-of-use (reference) (flakes-p2dp) — Draft
- Configure jjui theme for readability with Pencil Dark terminal theme (flakes-p7hc) — Done ·
archived - Unify incus container image build tasks (flakes-p8hf) — Done
- Setup OpenCode and Claude for spacebot container (flakes-pr3i) — Done
- Package beans-serve (web UI) from source via nix (flakes-qe0o) — Draft
- Integrate Home Manager as a NixOS module (flakes-rg27) — Done ·
archived - Migrate from just to mise for task running (flakes-rg2j) — Done ·
archived - Trial MCPVault as an MCP server for the notes vault (flakes-rv2o) — Draft
- Full jjui theme with terminal-independent hex colors (flakes-sk2r) — Done ·
archived - Containerize OneCLI (flakes-t9u2) — Done
- Inhibit idle/sleep during audio/video playback (flakes-va02) — Done ·
archived - Dynamic per-host Home Manager configs via flake module (flakes-vaiq) — Done ·
archived - Container-to-host network isolation (flakes-x821) — Done
- Add rtk-rewrite.sh hook to claude module with home-manager linking (flakes-y887) — Done ·
archived - Replace CopyQ with cliphist (Wayland-native clipboard manager) (flakes-yyox) — Done ·
archived - Add mdbook-beans tasks section to docs (flakes-zvh8) — Done ·
archived
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
lima->incus host-routable VM IP + self-healing route (macOS) (flakes-1huh)
Reaching incus containers (e.g. 10.100.0.0/24) directly from macOS, surviving reboots and VM IP changes.
Note on scope: the moving parts here are macOS-side — a lima VM template and launchd agents on the Mac host. This flakes repo manages NixOS/home-manager, not the macOS host or the lima template, so this is largely external. Captured as a reference/draft bean; the in-repo touchpoint is boot.autostart and container networking inside incus.
Design
- vzNAT network in the lima template gives the VM a host-routable IP (Mac
192.168.64.1, VM192.168.64.5); the container reaches the Mac via VM NAT. This is the substrate the MCP OAuth bridge rides on. - Self-healing route: a launchd agent runs an idempotent
ensure-route— it only touches the routing table when the route is missing or stale (no-op + no sudo when it already points at the VM’s current IP). A one-timeroute-allow-sudodrops a narrow sudoers line so the agent can maintain the route unattended. - VM auto-start via a launchd agent so containers survive Mac reboots (
boot.autostartinside, agent restores the route once the VM is up).
Todo
- (External) lima template with vzNAT network
- (External) launchd
ensure-routeagent + narrowroute-allow-sudosudoers line - (External) launchd VM auto-start agent
- (In-repo, if applicable) confirm
boot.autostartis set on incus containers that should survive reboots
Convert onecli-reseed to a manually-invoked script (flakes-2ey6)
Remove the systemd service for onecli-init-ca-and-secrets and replace with a plain onecli-reseed command. Launch is manual so systemd wantedBy/autostart causes more trouble than it’s worth.
Summary of Changes
- Renamed
initScript→reseedScriptwith binary nameonecli-reseed - Added
reseedScripttoenvironment.systemPackagesdirectly - Removed
systemd.services.onecli-init-ca-and-secretsblock entirely - Deleted
onecli-init-ca-and-secrets.bash(was justsudo systemctl start ...) - Removed auto-start block from
mise run _switch-host - Updated
mise run restart-oneclito callsudo onecli-reseeddirectly
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.
Install latest beans via nix flake (flakes-6ne0)
Add beans (edger-dev/beans) as a flake input and install it as a home-manager package, replacing or supplementing the current beans installation.
Plan
Investigation (changes the approach)
- The bean assumed a flake input
edger-dev/beans. That repo 404s (private or nonexistent; no GitHub auth in this container). The real upstream isgithub.com/hmans/beans(from the binary’s Go module path). hmans/beansis public but has no flake.nix, so it can’t be a flake input directly.- Building from source is a two-stage pnpm + Go build:
internal/web/embed.gohas//go:embed dist/*which requires a pre-built SvelteKit frontend (pnpm build→ copy intointernal/web/dist/) beforego build. That needs two content hashes (pnpmDeps + goModules vendorHash) and is high-risk to converge unattended. - hmans/beans publishes prebuilt goreleaser binaries. Latest stable = v0.4.2, per-platform tarballs (Linux x86_64/arm64, Darwin x86_64/arm64). The Linux binary is static (
not a dynamic executable) and runs (beans 0.4.2). License: Apache-2.0. - The public release ships only
beans(CLI +beans tuisubcommand).beans-serve(web UI, embeds the frontend) is not published and is not a subcommand ofbeansv0.4.2 (beans serve→ unknown command). The current imperative install (from a private dev build) has all three:beans/beans-serve/beans-tui.
Decision
Package the prebuilt v0.4.2 beans release binary as a home-manager package via fetchurl (per-platform, hashes from the release checksums.txt). This is robust and verifiable, and honors the bean’s intent (manage beans declaratively via nix) better than the impossible flake-input or the fragile source build.
Scope note: this supplements — it adds nix-managed beans (CLI + TUI). It does not remove the imperative ~/.local/bin/{beans,beans-serve,beans-tui} and does not provide beans-serve (web UI), which the public release doesn’t publish. Packaging beans-serve would need the source build → separate follow-up.
Steps
- Added
packs/home/common/packages/beans.nix(prebuilt v0.4.2, per-platform fetchurl) +home.packagesentry - Built via nix-build; binary runs (
beans 0.4.2,tuisubcommand,list);homeConfigurations.yjnow contains beans-0.4.2 - Reviewed (subagent verified all 4 hashes vs upstream checksums, static-binary + darwin-signing concerns cleared) + committed
Summary of Changes
Added packs/home/common/packages/beans.nix: a home-manager module that installs the prebuilt v0.4.2 beans release binary from github.com/hmans/beans via per-platform fetchurl (x86_64/aarch64 × linux/darwin), hashes pinned from the release checksums.txt, added to home.packages.
Deviation from the original spec (documented in Plan): the bean named a flake input edger-dev/beans — that repo is inaccessible (404/private, no auth here) and the real upstream hmans/beans has no flake. Building from source is a two-stage pnpm+Go build (embedded SvelteKit frontend). The prebuilt release binary is the robust, verifiable path and matches the intent (declarative nix-managed beans).
Verified: nix-build succeeds; the binary runs (beans 0.4.2, beans tui, beans list); homeConfigurations.yj.config.home.packages contains beans-0.4.2; activation package evaluates. Review subagent confirmed all four asset hashes against upstream checksums and cleared static-binary/darwin-signing concerns.
Scope / follow-up: supplements (does not remove) the imperative ~/.local/bin/{beans,beans-serve,beans-tui}. beans-serve (web UI) is not in the public release; packaging it from source is tracked in follow-up bean flakes-qe0o. PATH precedence: if ~/.local/bin shadows the nix beans, remove the imperative copy to use v0.4.2.
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
Configure & run ZeroClaw (config, secrets, service) (flakes-8itl)
Follow-up from flakes-kj17, which installed the ZeroClaw binaries (v0.8.2) via nix (packs/home/common/packages/zeroclaw.nix). This bean covers actually running/configuring ZeroClaw, which needs user decisions and secrets and so was deferred from the autonomous install.
ZeroClaw is a self-hosted autonomous AI assistant runtime: LLM providers (Anthropic/OpenAI/Ollama/…), channels (Discord/Telegram/Matrix/email/CLI), tools (shell/browser/http), configured via ~/.zeroclaw/config.toml.
Decisions needed
- Which LLM provider(s) + API keys — manage secrets via sops-nix (this repo’s pattern), not plaintext config
- Which channels/integrations to enable, and the autonomy level (this thing can run shell/tools — scope carefully)
- Run mode: on-demand CLI (
zeroclaw/zerocode) vs. an always-on service. Upstream ships a NixOS module (system service), not a home-manager service — decide host(s) and whether to use the upstream module or a home-manager systemd user service - Which machines should run it (currently the binary is in the common home profile → all profiles incl. containers; consider narrowing to a host mixin)
Tasks (once decided)
- Declarative
config.toml(home-managerhome.fileor xdg) with secrets wired via sops-nix - If service: import upstream NixOS module on the chosen host, or define a user service
- Verify a minimal end-to-end run (one provider, CLI channel)
Notes
- The release tarball also ships a
web/dist/dashboard copy; the static binary likely embeds its UI. If the web dashboard fails to find assets at runtime, packageweb/dist/alongside and point ZeroClaw at it. - To track latest: bump
version+sha256inzeroclaw.nixfrom the releaseSHA256SUMS.
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)
Migrate container IPs to new scheme (flakes-cjao)
Update static IPs: spacebot .102→.101, hermes .103→.102, ubuntu .101→.200. Update incus-ingress.nix, launch scripts, and dnsmasq config.
Summary of Changes
- Updated : spacebot .102→.101, hermes .103→.102
- Updated @onecli static block to point to 10.100.0.2 (anticipating OneCLI containerization)
- Note: ubuntu container IP (.101→.200) is managed imperatively via
incus config device override ubuntu eth0 ipv4.address=10.100.0.200
Drop nixos-unified, use raw flake-parts (flakes-dght)
Replace nixos-unified.lib.mkFlake with flake-parts.lib.mkFlake. Separate host and container config namespaces. Rewrite home-configs.nix and activate-home.nix to own the logic directly. Keep autowire (from jig) for module discovery.
Plan
- Move host configs: configurations/nixos/{edger,a13,g1,p2,pc} → configurations/nixos/hosts/
- Move container configs: configurations/nixos/{yolo,spacebot,hermes} → configurations/nixos/containers/
- Rewrite flake.nix: use flake-parts.lib.mkFlake with explicit imports
- Rewrite modules/flake/toplevel.nix: remove nixos-unified imports
- Create modules/flake/nixos-configs.nix: explicit nixosConfigurations + nixosModules.default + homeModules.default + overlays
- Rewrite modules/flake/home-configs.nix: use home-manager.lib.homeManagerConfiguration directly
- Rewrite modules/flake/activate-home.nix: use home-manager switch –flake directly
- Remove nixos-unified from flake inputs
- Update mise.toml build tasks — no changes needed, output names unchanged
- Test: nix flake check (passes — niri hash mismatch is pre-existing, unrelated)
- Test: mise run build-host (on edger) — blocked by pre-existing niri hash issue
Summary of Changes
Completed in two commits:
-
fee9eea - Drop nixos-unified, use raw flake-parts
- Replaced nixos-unified.lib.mkFlake with flake-parts.lib.mkFlake
- Separated host/container configs into configurations/nixos/hosts/ and containers/
- Rewrote home-configs.nix, activate-home.nix to own the logic directly
- Removed nixos-unified flake input
-
f69f1fd - Restructure into packs/ with wireImportsRecursively
- Introduced packs/ directory with autowired module collections
- Added wireImportsRecursively to jig (1f1b27c)
- Removed 28 boilerplate default.nix files
- Merged modules/ and relevant mixins/ into packs/
- Retained mixins/ for opt-in pieces (services, versions, storage, lan)
- Deduplicated shared config (ai/, removed pass-through files)
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.
Replace autowire.nix with jig lib (flakes-iymi)
Migrate from standalone autowire.nix flake input to jig’s lib.autowire. Add shim in flake.nix, rename default→wireImports, fix generic gatherFiles calls, update docs.
Summary of Changes
- Replaced
autowire.url = "github:yjpark/autowire.nix"withjig.url = "github:edger-dev/jig"in flake.nix - Added shim:
inputs = inputs // { autowire = inputs.jig.lib.autowire; }so all existingflake.inputs.autowire.*references keep working - Renamed
autowire.default→autowire.wireImportsin 48 default.nix files - Updated 3 files using generic
gatherFiles→gatherFiles_(pre-applied variant) - Updated CLAUDE.md and docs/src/architecture.md
- Verified with
nix flake show— evaluates cleanly
wezterm: Claude Code editing ergonomics (shipped) (flakes-jkwu)
Two wezterm ergonomics tweaks for Claude Code, already shipped in packs/home/gui/linux/wezterm/wezterm.lua. Captured for the record.
1. Ctrl+\ → soft newline in Claude Code
Send Space \ <50ms delay> Enter so CC inserts a continuation line instead of submitting. The delay matters — without it a stray \ is left on the last line.
{ key = '\\', mods = 'CTRL', action = wezterm.action_callback(function(window, pane)
window:perform_action(act.SendKey { key = 'Space' }, pane)
window:perform_action(act.SendKey { key = 'Backslash' }, pane)
wezterm.sleep_ms(50)
window:perform_action(act.SendKey { key = 'Enter' }, pane)
end)},
2. Don’t let Ctrl+Shift+C clobber the zellij/OSC-52 clipboard
Through wezterm → remote shell → zellij, Ctrl+Shift+C ran CopyTo('Clipboard') which copied the (empty) terminal selection, overwriting what zellij had already put on the system clipboard via OSC 52. Fix: make the binding a callback that only copies when there is a non-empty terminal selection (mirrors kitty’s behavior). Upstreamable.
Status
Both bindings are present in wezterm.lua (lines ~60–74). This bean documents them; no further work unless upstreaming the Ctrl+Shift+C fix.
Todo
- Ctrl+\ soft-newline binding
- Ctrl+Shift+C clipboard-safe binding
- (optional) Upstream the Ctrl+Shift+C selection-guard behavior to wezterm
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)
Blocked — needs spec (triaged 2026-06-29, night shift)
The bean has no body and the approach can’t be determined without guessing on several independent axes. Context found: ‘oxide projects’ most likely refers to markdown-oxide notes (the Obsidian-style markdown PKM enabled in [[flakes-7ih3]] via the markdown_oxide LSP — but no vault path is configured anywhere in the repo).
Quartz (https://quartz.jzhao.xyz) is a static-site generator that publishes a folder of Obsidian/markdown notes. It’s structured as a per-content project scaffold (clone quartz, put markdown in content/, npx quartz build/preview), not a home-manager package — so this is a project/workflow setup, not a dotfiles config change.
Open questions blocking implementation
- Content source: which markdown directory/vault are the ‘oxide projects’? Where does it live (path / separate git repo / the private/ dir)?
- Repo location: should the Quartz project live in this flakes repo, a sibling repo, or inside the notes vault?
- Publish target: GitHub Pages? Cloudflare Pages? The existing host Caddy ingress (
*.${host}.yjpark.org, seemixins/nixos/services/incus-ingress.nix)? Cloudflared tunnel? This drives the whole ‘publish’ half. - Preview: local
npx quartz build --serveis generic; fine once content path is known.
Proposed approach (once unblocked)
- Scaffold Quartz pointing
content/at the vault (or symlink); add amise/just task forpreview; pick a publish target and add a build+deploy task (or a NixOS/cloudflared service if self-hosting). Optionally add thequartz/node toolchain to a dev shell.
Moved to draft so it leaves the ready queue until the content source + publish target are decided.
Check MCPVault, to see whether it’s a better option for the markdowns (flakes-k3pr)
Evaluation (research, 2026-06-29)
MCPVault = bitbonsai/mcpvault (site: mcpvault.org) — a TypeScript/Node (≥18) MCP server that gives MCP-compatible AI agents safe read/write access to a folder of markdown files (no Obsidian needed). Exposes ~14 tools: read/write/patch/move notes, BM25 search_notes, frontmatter get/update (AST-aware YAML preservation), tags, vault stats; with path-traversal protection. Run via npx @bitbonsai/mcpvault@latest /path/to/vault.
Is it a ‘better option for the markdowns’?
No — it’s a different layer, not a replacement. It does not compete with the current setup:
markdown-oxide(LSP) serves you in the editor (completion, backlinks, go-to-def). [[flakes-7ih3]]beansCLI manages the.beans/issue markdowns for you + agents.- MCPVault serves AI agents structured access to a notes vault. Complementary to markdown-oxide; largely redundant with
beansfor the.beans/dir (beans already has a CLI/GraphQL).
NixOS feasibility
Not in nixpkgs; it’s an npm package. Cleanest integration = register as an MCP server pointing npx at the notes vault; no system packaging needed.
Verdict
The bean’s ‘better option’ framing is a false either/or. Recommendation: keep markdown-oxide; optionally TRIAL MCPVault as an MCP server against the notes vault to give agents note access. Not urgent, not a replacement.
Sources: github.com/bitbonsai/mcpvault, mcpvault.org/install
Summary of Changes
Research/evaluation task (no code changes). Identified MCPVault as bitbonsai/mcpvault (Node MCP server for markdown vaults). Conclusion: not a ‘better option’ — it’s a complementary AI-agent access layer, not a replacement for markdown-oxide or beans. Recorded full assessment + sources above. Optional trial captured as a follow-up draft bean.
Install Zeroclaw (flakes-kj17)
Install Zeroclaw (https://github.com/zeroclaw-labs/zeroclaw) — evaluate and set up in the environment.
Plan
Evaluation (research + verified, 2026-06-29)
ZeroClaw (github.com/zeroclaw-labs/zeroclaw, MIT/Apache-2.0) is a single-binary Rust self-hosted autonomous AI assistant runtime (LLM providers + channels + tools; TOML config at ~/.zeroclaw/config.toml). Latest release v0.8.2.
Availability (verified): in nixpkgs as zeroclaw but stale (0.5.1); upstream flake has packages.<sys>.zeroclaw (but compiles a large Rust workspace, and packages.default is the Rust toolchain, no apps); prebuilt release binaries + SHA256SUMS for all platforms.
Decision
Package the prebuilt v0.8.2 release binary via fetchurl (same pattern as beans.nix / flakes-6ne0). This gives the latest version with no heavy Rust build and no new flake input — strictly better than the stale nixpkgs build and lighter than building the upstream flake from source. Use the musl (static) linux builds (no patchelf/loader concerns on NixOS).
Platforms: x86_64-linux, aarch64-linux (musl), aarch64-darwin. No x86_64-darwin asset is published → unsupported (throw).
Scope: install the zeroclaw (+ zerocode) binaries only. Runtime ‘setup’ (config.toml, API-key secrets, optional systemd service) needs user decisions → deferred to a follow-up. The web dashboard ships a web/dist/ copy in the tarball; the static binary most likely embeds its UI, so only the binaries are installed (caveat noted).
Steps
- Added
packs/home/common/packages/zeroclaw.nix(prebuilt v0.8.2 musl/darwin fetchurl) + home.packages - nix-build OK;
zeroclaw 0.8.2runs (zeroclaw + zerocode installed); present in homeConfigurations.yj - Self-reviewed (same verified pattern as beans.nix flakes-6ne0); committed; follow-up bean created for runtime config/secrets/service
Summary of Changes
Added packs/home/common/packages/zeroclaw.nix: home-manager package installing the prebuilt ZeroClaw v0.8.2 release binaries (zeroclaw + zerocode) via per-platform fetchurl (x86_64-linux/aarch64-linux musl-static, aarch64-darwin), hashes pinned from the release SHA256SUMS.
Why prebuilt: nixpkgs zeroclaw is stale (0.5.1) and the upstream flake compiles a large Rust workspace; the prebuilt static binary gives the latest with no heavy build and no new flake input (same pattern as beans, flakes-6ne0).
Verified: nix-build succeeds; zeroclaw 0.8.2 runs; both binaries installed; present in homeConfigurations.yj.
Scope: binaries only. Runtime setup (config.toml, API-key secrets via sops-nix, optional service, host targeting) is deferred to follow-up flakes-8itl — it needs user decisions and secrets, inappropriate for autonomous setup.
Fix CA cert injection to be declarative (flakes-krtw)
Replace manual /etc/ssl/certs mutation with security.pki.certificateFiles. Seeder pushes CA to a known path, NixOS handles cert bundle integration.
Summary of Changes
packs/nixos/container/onecli-proxy.nix: added/var/lib/onecli/tmpfiles dir,onecli-ca-bundle.service(oneshot, rebuilds bundle idempotently from system certs + CA), andonecli-ca-bundle.path(watches/var/lib/onecli/ca.crt, re-triggers on every push/rotation). UpdatedNODE_EXTRA_CA_CERTSto point to the new stable path.mixins/nixos/services/onecli.nix: seeder now writes CA to/var/lib/onecli/ca.crtonly — no more in-container symlink replacement or bundle appending.packs/home/host/linux/scripts/incus/onecli-push-ca.bash: same simplification.
MCP OAuth bridge: Mac-side forwarder + Phase 2 + e2e (external) (flakes-mqe9)
External (macOS host) half of the cross-container MCP OAuth bridge. The in-repo container half is done in flakes-wq6s (env pin + Caddy bridge site <container-ip>:3118 -> 127.0.0.1:3118). This bean covers the parts that live on the Mac host / lima VM, outside this NixOS flakes repo, plus end-to-end verification.
Tasks
- Mac launchd socat forwarder: relay
localhost:3118(where the Mac browser opens the OAuth callback) →<container-ip>:3118over the existing host→VM→container route (rides the vzNAT VM IP from the lima/incus routing setup, flakes-1huh). - Phase 2 — auto-open authorize URL (zero-touch): Mac launchd listener that runs
open <url>; container setsBROWSERto a script that ships the URL to it (+ anxdg-openshim). Restrict by shared secret, NOT subnet — the bridge NATs container traffic so the Mac sees the VM’s source IP, not the container’s. - End-to-end verification: run an MCP OAuth flow from Claude Code inside the container; confirm the callback completes without manual URL-copy/curl.
Notes / gotchas (verified against the CC binary)
- CC callback port default is a random ephemeral port;
MCP_OAUTH_CALLBACK_PORTwins unconditionally (no availability check) → pinning to 3118 is mandatory (done container-side in flakes-wq6s). - CC listener binds
127.0.0.1only; redirect host/path hardcoded tohttp://localhost:<port>/callback(only the port is configurable). - The container Caddy bridge binds the container IP only (not 0.0.0.0) so CC’s own
127.0.0.1:3118stays free. - The bridge
mcp-oauth.confis (re)written wheneveringress-syncruns; ensure ingress-sync has run after boot before relying on the bridge. - Ubuntu containers: the in-repo CC-side env pin (
environment.variables) is NixOS-only. On Ubuntu containers the Caddy bridge still works (script falls back to port 3118), but CC’sMCP_OAUTH_CALLBACK_PORTmust be pinned via the Ubuntu user env separately if those containers run the OAuth flow.
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)
MITM egress-proxy CLI: stable CA + ease-of-use (reference) (flakes-p2dp)
Lessons from making a corporate egress-proxy CLI stable + easy to use. The tool MITMs all outbound HTTPS through a local proxy with its own CA. Patterns generalize to “any MITM-proxy-with-rotating-CA” setup.
Note on scope: this is a work-specific corporate tool that does not live in this flakes repo. Captured here as a reference bean for the lessons learned; the analogous in-repo pattern is the ingress CA bundle handling in mixins/nixos/services/incus-ingress.nix. Not directly implementable in this repo — treat as reference / draft.
Lessons
- CA must come from the real signing cert, not the management API. The proxy’s
/api/gateway/caendpoint can report a CA that does not match what it actually signs MITM certs with (regenerated-after-restart vs the persisted volume CA). Read the CA from the on-disk signing cert (.../gateway/ca.pem) instead. That one is stable (10-yr, survives restart/reboot; changes only on container recreate). - Persist the CA across recreate. Back the proxy’s data dir with a dedicated incus custom storage volume (bind-mounted), so CA + encryption key survive
delete+recreate. Verified: CA hash unchanged across a full recreate. - The ingress CA bundle gets clobbered — re-append on every rebuild. The dynamic ingress-config generator rebuilds the world-readable
ca-bundle.crtascat system + caddyon every port change, silently dropping the proxy CA that a separate service had appended. Net effect: TLS works right after boot, then breaks mid-session when a port change re-triggers ingress-sync. Fix: append the proxy CA inside the generator itself. (git/libcurl readsSSL_CERT_FILE/NIX_SSL_CERT_FILE-> the bundle, so once the CA is in the bundle, no per-commandhttp.sslCAInfoflag is needed.) - git push over the proxy: the proxy CA must be in the world-readable bundle; the root-only
ca.crt(mode 000) isn’t readable by the non-root user. - Ease-of-use wins: rewrite
git@host:...SSH URLs to HTTPS in container git config (SSH can’t traverse the proxy); add a:80reverse-proxy to the dashboard for*.incusaccess; single personal write-PAT scoped by path whitelist (the proxy matches secrets by host/path only — no env/header/per-agent gating is possible, so path-scoping a single PAT is the workable model; most-specific pathPattern wins). - Caveat: postgres isn’t persisted, so a recreate still resets seeded secrets + agent token (re-run the reseed). Persisting postgres too would make recreates seamless.
Todo
- (Reference only) Evaluate whether the “re-append CA inside the ingress generator” lesson applies to
incus-ingress.nixhere — currentlyinit-ingress-caresets the bundle on boot andincus-pull-care-appends manually
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.
Unify incus container image build tasks (flakes-p8hf)
Replace per-container build-image/build-metadata/build-and-import tasks with a single parameterized task that builds image+metadata and imports directly without staging in images/
Summary of Changes
Replaced 9 per-container tasks (build-yolo-image, build-yolo-metadata, build-and-import-yolo, and equivalents for spacebot/hermes) with a single mise run build-container <name> task.
- Uses
usagefield with KDLchoicesfor shell auto-complete (covers all 5 containers) - Builds metadata + image sequentially, resolves nix store paths directly from
resultsymlink - Pipes both tarballs straight to
incus image import— noimages/staging directory needed - Removed
images/directory entirely
Setup OpenCode and Claude for spacebot container (flakes-pr3i)
Configure OpenCode with the claude-code-plugin (github:unixfox/opencode-claude-code-plugin) in the spacebot incus container, enabling Claude as a coding assistant within spacebot’s environment.
Summary of Changes
- Added
opencode-claude-code-pluginflake input (github:unixfox/opencode-claude-code-plugin, flake=false) - Updated
mixins/home/containers/spacebot.nix:- Added
llm-agents.opencodeandllm-agents.claude-codeto packages - Added
xdg.configFile."opencode/plugins/claude-code"pointing to plugin source
- Added
- Plugin placed at
~/.config/opencode/plugins/claude-codefor OpenCode to load - Also fixed minor bug in spacebot-restart (was calling spacebot-restart instead of spacebot-status)
Package beans-serve (web UI) from source via nix (flakes-qe0o)
The nix package added in flakes-6ne0 installs the prebuilt beans release binary (CLI + beans tui). The public release does not publish beans-serve (the web UI / GraphQL server, ~59MB because it embeds the SvelteKit frontend), so the bw = beans-serve fish abbr still relies on the imperative ~/.local/bin/beans-serve.
To package beans-serve declaratively requires building from source (github.com/hmans/beans), which is a two-stage build:
- Frontend:
frontend/is SvelteKit + pnpm.pnpm install+pnpm build, output copied intointernal/web/dist/(consumed by//go:embed dist/*ininternal/web/embed.go). Needs apnpmDepsfixed-output hash. - Backend:
go build ./cmd/beans-serve(and optionally./cmd/beans,./cmd/beans-tui) viabuildGoModulewith avendorHash. Noteinternal/graph/generated.goappears committed, butmise.toml’s build runsgo generate ./...(gqlgen) first — confirm whether codegen is needed or the committed output suffices.
Open questions
- Is the web UI (
beans-serve) actually still used, or has the workflow moved to TUI / external beans server? If unused, scrap this. - Prefer building all three from source (replacing the prebuilt-binary package in flakes-6ne0 for consistency) vs. keeping prebuilt
beans+ source-builtbeans-serve?
Tasks (if pursued)
- Frontend derivation (pnpm) -> dist
- buildGoModule with vendorHash, embedding the built frontend
- Wire into home.packages; update
bw/btabbrs as needed; drop imperative~/.local/bin/beans*
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
Trial MCPVault as an MCP server for the notes vault (flakes-rv2o)
Optional follow-up from flakes-k3pr evaluation. MCPVault (bitbonsai/mcpvault) is an npm MCP server exposing a markdown vault to AI agents (read/write/patch/search/frontmatter/tags). It complements markdown-oxide (editor LSP) rather than replacing it. If wanted, register it as an MCP server: npx @bitbonsai/mcpvault@latest <vault-path>, pointed at the notes vault. Decide the vault path and which agent client(s) to wire it into. Skip if agent note-access isn’t needed.
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.
Containerize OneCLI (flakes-t9u2)
Move OneCLI + postgres from podman-on-host to a dedicated incus container at 10.100.0.2. Update seeder to target container IP. Convert to NixOS service with local postgres (no more podman-in-podman).
Summary of Changes
- Created
nixos/containers/onecli/with NixOS config:- Static IP 10.100.0.2, native postgresql, podman onecli image with –network=host
- Selective imports (skips ingress.nix and onecli-proxy.nix)
- Registered “onecli” in
flake/nixos-configs.nix - Updated
mixins/nixos/services/onecli.nix: removed podman setup, now just the seeder pointing to 10.100.0.2 - Updated
packs/nixos/container/onecli-proxy.nix: reference 10.100.0.2 - Updated all launch/utility scripts: spacebot .102→.101, hermes .103→.102, ubuntu .101→.200, onecli scripts 10.100.0.1→10.100.0.2
- Rewrote
onecli-reset-db.bashto use incus exec instead of podman - Added
incus-launch-onecli.bash
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
Container-to-host network isolation (flakes-x821)
Add nftables INPUT rules on incusbr0 to block container→host traffic except DHCP (67/68) and DNS (53/5354). Container↔container traffic via FORWARD chain stays unrestricted.
Summary of Changes
- Updated
packs/nixos/host/incus.nix: removedtarget = "ACCEPT"from incus firewalld zone - Added explicit services: “dhcp” (UDP 67) and “dns” (TCP/UDP 53) for container→host DHCP/DNS
- Added ports 5354 TCP/UDP for custom dnsmasq DNS
- Container↔container traffic via FORWARD chain is unaffected (bridge forwarding)
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) — Done ·
archived - Auto-restart firewalld when zone config changes (flakes-ja9k) — Done
- Fix mise.toml var args bug (flakes-m3r8) — Done ·
archived - Fix git push auth inside Incus container via OneCLI proxy (flakes-nz9y) — Done ·
archived - Pass currentSystem to NixOS-integrated home-manager extraSpecialArgs (flakes-sytx) — 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.
Auto-restart firewalld when zone config changes (flakes-ja9k)
NixOS services.firewalld module doesn’t restart/reload the firewalld service when zone config changes, so switch-host applies new XML but the running daemon keeps old rules until manually restarted.
Context
Discovered while debugging ICMP/ping access (commits 84638b0, 235381f). After running mise run _switch-host with new services.firewalld.zones.lan settings (added protocols = ["icmp"] and extra source range), the active runtime config from firewall-cmd --zone=lan --list-all still showed the old rules. Manual sudo systemctl restart firewalld was required.
Investigation
- Confirm root cause: nixpkgs
services.firewalldsets norestartTriggers/reloadTriggers; zone/service XML lands in/etc/firewalld/{zones,services}/*.xmlviaenvironment.etc, disconnected from the unit - Decided reload: the unit already has
ExecReload = kill -HUP $MAINPID; firewalld reloads permanent config on SIGHUP, non-disruptive - (deferred) Check if upstream nixpkgs already has an open issue/PR — no web research done this shift
Implementation
- Added
systemd.services.firewalld.reloadTriggersinpacks/nixos/common/settings/firewalld.nixreferencing everyfirewalld/*etc source (zones, services, policies, firewalld.conf) - Verified at eval/build:
reloadTriggersresolves to the exact content-addressed zone/service XML store paths (incl.zone-lan.xmlon a13); reviewer confirmed realizedX-Reload-Triggerscontent. NOTE: live_switch-host+firewall-cmdtest still pending on a physical host (cannot run from the yolo container) - (follow-up) Consider upstreaming the
reloadTriggersfix to nixpkgs
Plan
Root cause confirmed by reading the nixpkgs services.firewalld module (services/networking/firewalld/{default,zone,service}.nix):
- Zone XML →
/etc/firewalld/zones/<name>.xml, service XML →/etc/firewalld/services/<name>.xml(viaenvironment.etc). - The systemd unit sets
ExecReload = kill -HUP \$MAINPID(firewalld reloads permanent config on SIGHUP) but has noreloadTriggers/restartTriggers. So when only the etc XML changes, the unit definition is unchanged andswitch-hostnever reloads the daemon → stale runtime rules.
Fix (reload, not restart — non-disruptive)
In packs/nixos/common/settings/firewalld.nix, set systemd.services.firewalld.reloadTriggers to the .source of every generated firewalld/* etc entry. Any zone/service XML change then flips the trigger and switch-to-configuration issues systemctl reload firewalld → SIGHUP → reload.
No upstream change attempted in this pass (note as possible follow-up).
Summary of Changes
File: packs/nixos/common/settings/firewalld.nix
Added systemd.services.firewalld.reloadTriggers, computed from every config.environment.etc entry whose key starts with firewalld/ (mapped to its .source). This covers firewalld.conf, zones/*.xml, services/*.xml, and policies/*.xml.
Why it works: the nixpkgs module writes firewalld permanent config to /etc/firewalld/... but never linked those files to the systemd unit, so switch-host left the daemon running stale rules. Wiring the etc sources into reloadTriggers makes switch-to-configuration issue systemctl reload firewalld whenever any firewalld config file content changes; the unit ExecReload sends SIGHUP and firewalld reloads its permanent config into runtime (non-disruptive).
Decision: chose reload (SIGHUP) over restart — matches the bean preference, no connection flush. sysconfig/firewalld (FIREWALLD_ARGS, needs a restart) is intentionally excluded; extraArgs is empty and rarely changes.
Verification: nix eval/nix build confirm reloadTriggers resolves to the exact content-addressed XML store paths; a review subagent independently built the yolo config and confirmed the realized X-Reload-Triggers content. Live _switch-host + firewall-cmd --list-all test is deferred to a physical host (not runnable from a container).
Follow-ups: check for / open an upstream nixpkgs issue or PR.
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
Pass currentSystem to NixOS-integrated home-manager extraSpecialArgs (flakes-sytx)
nixos-rebuild on g1 fails with infinite recursion in packs/home/host because the NixOS-integrated home-manager module (packs/nixos/common/settings/home-manager.nix) doesn’t pass currentSystem via extraSpecialArgs. Standalone home configurations get it via flake/home-configs.nix but NixOS hosts that use home-manager.users.* don’t.
Summary of Changes
Updated packs/nixos/common/settings/home-manager.nix to:
- Take
pkgsas module argument - Pass
currentSystem = pkgs.stdenv.hostPlatform.systemviaextraSpecialArgs
This mirrors what flake/home-configs.nix already does for standalone home configurations. The integrated home-manager NixOS module evaluates packs/home/host (via home/yjpark.nix imported through packs/nixos/host/yjpark.nix), and that module reads currentSystem in its imports to conditionally include the linux subdirectory.
Verified: g1 evaluation now progresses past the recursion point (fails only on an unrelated network fetch of Smithay/smithay.git).
Drafts
- lima->incus host-routable VM IP + self-healing route (macOS) (flakes-1huh) — Draft
- Configure & run ZeroClaw (config, secrets, service) (flakes-8itl) — Draft
- Declarative incus fleet management (flakes-ihxf) — Draft
- Zellij WASM plugin for dynamic tab naming (flakes-je55) — Draft
- Setup quartz to preview and publish oxide projects (flakes-k391) — Draft
- MCP OAuth bridge: Mac-side forwarder + Phase 2 + e2e (external) (flakes-mqe9) — Draft
- MITM egress-proxy CLI: stable CA + ease-of-use (reference) (flakes-p2dp) — Draft
- Package beans-serve (web UI) from source via nix (flakes-qe0o) — Draft
- Trial MCPVault as an MCP server for the notes vault (flakes-rv2o) — Draft
lima->incus host-routable VM IP + self-healing route (macOS) (flakes-1huh)
Reaching incus containers (e.g. 10.100.0.0/24) directly from macOS, surviving reboots and VM IP changes.
Note on scope: the moving parts here are macOS-side — a lima VM template and launchd agents on the Mac host. This flakes repo manages NixOS/home-manager, not the macOS host or the lima template, so this is largely external. Captured as a reference/draft bean; the in-repo touchpoint is boot.autostart and container networking inside incus.
Design
- vzNAT network in the lima template gives the VM a host-routable IP (Mac
192.168.64.1, VM192.168.64.5); the container reaches the Mac via VM NAT. This is the substrate the MCP OAuth bridge rides on. - Self-healing route: a launchd agent runs an idempotent
ensure-route— it only touches the routing table when the route is missing or stale (no-op + no sudo when it already points at the VM’s current IP). A one-timeroute-allow-sudodrops a narrow sudoers line so the agent can maintain the route unattended. - VM auto-start via a launchd agent so containers survive Mac reboots (
boot.autostartinside, agent restores the route once the VM is up).
Todo
- (External) lima template with vzNAT network
- (External) launchd
ensure-routeagent + narrowroute-allow-sudosudoers line - (External) launchd VM auto-start agent
- (In-repo, if applicable) confirm
boot.autostartis set on incus containers that should survive reboots
Configure & run ZeroClaw (config, secrets, service) (flakes-8itl)
Follow-up from flakes-kj17, which installed the ZeroClaw binaries (v0.8.2) via nix (packs/home/common/packages/zeroclaw.nix). This bean covers actually running/configuring ZeroClaw, which needs user decisions and secrets and so was deferred from the autonomous install.
ZeroClaw is a self-hosted autonomous AI assistant runtime: LLM providers (Anthropic/OpenAI/Ollama/…), channels (Discord/Telegram/Matrix/email/CLI), tools (shell/browser/http), configured via ~/.zeroclaw/config.toml.
Decisions needed
- Which LLM provider(s) + API keys — manage secrets via sops-nix (this repo’s pattern), not plaintext config
- Which channels/integrations to enable, and the autonomy level (this thing can run shell/tools — scope carefully)
- Run mode: on-demand CLI (
zeroclaw/zerocode) vs. an always-on service. Upstream ships a NixOS module (system service), not a home-manager service — decide host(s) and whether to use the upstream module or a home-manager systemd user service - Which machines should run it (currently the binary is in the common home profile → all profiles incl. containers; consider narrowing to a host mixin)
Tasks (once decided)
- Declarative
config.toml(home-managerhome.fileor xdg) with secrets wired via sops-nix - If service: import upstream NixOS module on the chosen host, or define a user service
- Verify a minimal end-to-end run (one provider, CLI channel)
Notes
- The release tarball also ships a
web/dist/dashboard copy; the static binary likely embeds its UI. If the web dashboard fails to find assets at runtime, packageweb/dist/alongside and point ZeroClaw at it. - To track latest: bump
version+sha256inzeroclaw.nixfrom the releaseSHA256SUMS.
Declarative incus fleet management (flakes-ihxf)
NixOS module to declare containers (image, IP, devices, ACLs) and have them rebuild/restart on _switch-host. Long-term goal.
Design notes (night-shift planning pass, 2026-06-30)
Not implemented this shift: building a declarative incus orchestration layer would mutate the live fleet (create/configure/restart containers) — unverifiable in CI and risky to run unattended, and it hinges on design decisions that are architect calls. Captured the design surface so it’s actionable later. Set to draft (needs design refinement before implementation), per the repo’s planning≠implementation rule.
Current state (what this would supersede / build on)
- Imperative launch/restart scripts:
packs/home/host/linux/scripts/incus/incus-launch-*.bash,incus-restart-*.bash,incus-setup-storage-ext4.bash— per-container shell, run by hand. - Host incus enablement:
packs/home/host/linux/incus.nix,packs/nixos/host/incus.nix,mixins/home/settings/incus/. - Static fleet list already exists in
mixins/nixos/services/incus-ingress.nixasingressContainers = [ { name; ip; } ... ](onecli .2, searxng .3, yolo .100, spacebot .101, hermes .102). A declarative fleet module should become the single source of truth feeding both container creation and this ingress list (DRY). - Containers are a mix: NixOS containers (
nixos/containers/*) and Ubuntu (packs/home/container/scripts/ubuntu-setup-*.bash).
Key design decisions (need a call before implementation)
- Option schema: e.g.
services.incus.fleet.<name> = { image; ipv4; devices; profiles; acls; storageVolumes; autostart; ... }. - Reconciliation model: activation script / systemd oneshot on
_switch-hostthat diffs declared vsincus list --format jsonand creates/configures/restarts. Must be idempotent and non-destructive (never delete/recreate a container with data; the egress-proxy work showed CA/postgres data lives on custom storage volumes that must persist across recreate). - Image source: build NixOS container images from this flake (reproducible) vs. pull Ubuntu images vs. both. Affects how “image” is declared.
- Scope of management: devices (proxy/disk/gpu),
ipv4.addressoverrides (currently set by hand viaincus config device override), network ACLs, profiles, storage volumes. - Restart policy: what changes trigger a restart vs. a live
incus configapply; avoid disrupting running services unnecessarily. - Migration: supersede the imperative
incus-launch-*/incus-restart-*scripts, or coexist during transition.
Proposed phased decomposition (create as child beans once decisions above are made)
fleetoption schema only (pure types, no side effects) + deriveingressContainersfrom it.- Read-only reconciler: report drift between declared fleet and
incus list(no mutation) — safe to land + verify first. - Create/ensure containers from declarations (idempotent, non-destructive; storage volumes persist).
- Device / ACL / profile / IP-override management.
- Wire restart-on-change into
_switch-host; migrate the imperative scripts.
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
Setup quartz to preview and publish oxide projects (flakes-k391)
Blocked — needs spec (triaged 2026-06-29, night shift)
The bean has no body and the approach can’t be determined without guessing on several independent axes. Context found: ‘oxide projects’ most likely refers to markdown-oxide notes (the Obsidian-style markdown PKM enabled in [[flakes-7ih3]] via the markdown_oxide LSP — but no vault path is configured anywhere in the repo).
Quartz (https://quartz.jzhao.xyz) is a static-site generator that publishes a folder of Obsidian/markdown notes. It’s structured as a per-content project scaffold (clone quartz, put markdown in content/, npx quartz build/preview), not a home-manager package — so this is a project/workflow setup, not a dotfiles config change.
Open questions blocking implementation
- Content source: which markdown directory/vault are the ‘oxide projects’? Where does it live (path / separate git repo / the private/ dir)?
- Repo location: should the Quartz project live in this flakes repo, a sibling repo, or inside the notes vault?
- Publish target: GitHub Pages? Cloudflare Pages? The existing host Caddy ingress (
*.${host}.yjpark.org, seemixins/nixos/services/incus-ingress.nix)? Cloudflared tunnel? This drives the whole ‘publish’ half. - Preview: local
npx quartz build --serveis generic; fine once content path is known.
Proposed approach (once unblocked)
- Scaffold Quartz pointing
content/at the vault (or symlink); add amise/just task forpreview; pick a publish target and add a build+deploy task (or a NixOS/cloudflared service if self-hosting). Optionally add thequartz/node toolchain to a dev shell.
Moved to draft so it leaves the ready queue until the content source + publish target are decided.
MCP OAuth bridge: Mac-side forwarder + Phase 2 + e2e (external) (flakes-mqe9)
External (macOS host) half of the cross-container MCP OAuth bridge. The in-repo container half is done in flakes-wq6s (env pin + Caddy bridge site <container-ip>:3118 -> 127.0.0.1:3118). This bean covers the parts that live on the Mac host / lima VM, outside this NixOS flakes repo, plus end-to-end verification.
Tasks
- Mac launchd socat forwarder: relay
localhost:3118(where the Mac browser opens the OAuth callback) →<container-ip>:3118over the existing host→VM→container route (rides the vzNAT VM IP from the lima/incus routing setup, flakes-1huh). - Phase 2 — auto-open authorize URL (zero-touch): Mac launchd listener that runs
open <url>; container setsBROWSERto a script that ships the URL to it (+ anxdg-openshim). Restrict by shared secret, NOT subnet — the bridge NATs container traffic so the Mac sees the VM’s source IP, not the container’s. - End-to-end verification: run an MCP OAuth flow from Claude Code inside the container; confirm the callback completes without manual URL-copy/curl.
Notes / gotchas (verified against the CC binary)
- CC callback port default is a random ephemeral port;
MCP_OAUTH_CALLBACK_PORTwins unconditionally (no availability check) → pinning to 3118 is mandatory (done container-side in flakes-wq6s). - CC listener binds
127.0.0.1only; redirect host/path hardcoded tohttp://localhost:<port>/callback(only the port is configurable). - The container Caddy bridge binds the container IP only (not 0.0.0.0) so CC’s own
127.0.0.1:3118stays free. - The bridge
mcp-oauth.confis (re)written wheneveringress-syncruns; ensure ingress-sync has run after boot before relying on the bridge. - Ubuntu containers: the in-repo CC-side env pin (
environment.variables) is NixOS-only. On Ubuntu containers the Caddy bridge still works (script falls back to port 3118), but CC’sMCP_OAUTH_CALLBACK_PORTmust be pinned via the Ubuntu user env separately if those containers run the OAuth flow.
MITM egress-proxy CLI: stable CA + ease-of-use (reference) (flakes-p2dp)
Lessons from making a corporate egress-proxy CLI stable + easy to use. The tool MITMs all outbound HTTPS through a local proxy with its own CA. Patterns generalize to “any MITM-proxy-with-rotating-CA” setup.
Note on scope: this is a work-specific corporate tool that does not live in this flakes repo. Captured here as a reference bean for the lessons learned; the analogous in-repo pattern is the ingress CA bundle handling in mixins/nixos/services/incus-ingress.nix. Not directly implementable in this repo — treat as reference / draft.
Lessons
- CA must come from the real signing cert, not the management API. The proxy’s
/api/gateway/caendpoint can report a CA that does not match what it actually signs MITM certs with (regenerated-after-restart vs the persisted volume CA). Read the CA from the on-disk signing cert (.../gateway/ca.pem) instead. That one is stable (10-yr, survives restart/reboot; changes only on container recreate). - Persist the CA across recreate. Back the proxy’s data dir with a dedicated incus custom storage volume (bind-mounted), so CA + encryption key survive
delete+recreate. Verified: CA hash unchanged across a full recreate. - The ingress CA bundle gets clobbered — re-append on every rebuild. The dynamic ingress-config generator rebuilds the world-readable
ca-bundle.crtascat system + caddyon every port change, silently dropping the proxy CA that a separate service had appended. Net effect: TLS works right after boot, then breaks mid-session when a port change re-triggers ingress-sync. Fix: append the proxy CA inside the generator itself. (git/libcurl readsSSL_CERT_FILE/NIX_SSL_CERT_FILE-> the bundle, so once the CA is in the bundle, no per-commandhttp.sslCAInfoflag is needed.) - git push over the proxy: the proxy CA must be in the world-readable bundle; the root-only
ca.crt(mode 000) isn’t readable by the non-root user. - Ease-of-use wins: rewrite
git@host:...SSH URLs to HTTPS in container git config (SSH can’t traverse the proxy); add a:80reverse-proxy to the dashboard for*.incusaccess; single personal write-PAT scoped by path whitelist (the proxy matches secrets by host/path only — no env/header/per-agent gating is possible, so path-scoping a single PAT is the workable model; most-specific pathPattern wins). - Caveat: postgres isn’t persisted, so a recreate still resets seeded secrets + agent token (re-run the reseed). Persisting postgres too would make recreates seamless.
Todo
- (Reference only) Evaluate whether the “re-append CA inside the ingress generator” lesson applies to
incus-ingress.nixhere — currentlyinit-ingress-caresets the bundle on boot andincus-pull-care-appends manually
Package beans-serve (web UI) from source via nix (flakes-qe0o)
The nix package added in flakes-6ne0 installs the prebuilt beans release binary (CLI + beans tui). The public release does not publish beans-serve (the web UI / GraphQL server, ~59MB because it embeds the SvelteKit frontend), so the bw = beans-serve fish abbr still relies on the imperative ~/.local/bin/beans-serve.
To package beans-serve declaratively requires building from source (github.com/hmans/beans), which is a two-stage build:
- Frontend:
frontend/is SvelteKit + pnpm.pnpm install+pnpm build, output copied intointernal/web/dist/(consumed by//go:embed dist/*ininternal/web/embed.go). Needs apnpmDepsfixed-output hash. - Backend:
go build ./cmd/beans-serve(and optionally./cmd/beans,./cmd/beans-tui) viabuildGoModulewith avendorHash. Noteinternal/graph/generated.goappears committed, butmise.toml’s build runsgo generate ./...(gqlgen) first — confirm whether codegen is needed or the committed output suffices.
Open questions
- Is the web UI (
beans-serve) actually still used, or has the workflow moved to TUI / external beans server? If unused, scrap this. - Prefer building all three from source (replacing the prebuilt-binary package in flakes-6ne0 for consistency) vs. keeping prebuilt
beans+ source-builtbeans-serve?
Tasks (if pursued)
- Frontend derivation (pnpm) -> dist
- buildGoModule with vendorHash, embedding the built frontend
- Wire into home.packages; update
bw/btabbrs as needed; drop imperative~/.local/bin/beans*
Trial MCPVault as an MCP server for the notes vault (flakes-rv2o)
Optional follow-up from flakes-k3pr evaluation. MCPVault (bitbonsai/mcpvault) is an npm MCP server exposing a markdown vault to AI agents (read/write/patch/search/frontmatter/tags). It complements markdown-oxide (editor LSP) rather than replacing it. If wanted, register it as an MCP server: npx @bitbonsai/mcpvault@latest <vault-path>, pointed at the notes vault. Decide the vault path and which agent client(s) to wire it into. Skip if agent note-access isn’t needed.