229 Commits

Author SHA1 Message Date
m3ta-chiron 0519de4f1d chore: m3-are to new ui 2026-06-06 07:54:30 +02:00
m3ta-chiron 9316aab5ca +nier wallpaper 2026-06-02 19:26:39 +02:00
m3ta-chiron a87234bd7f +dms 2026-06-02 18:20:43 +02:00
m3ta-chiron a9ffe3ed28 feat: agent-lib exlude agents 2026-05-31 14:10:15 +02:00
m3ta-chiron 7c5b92c377 Align nixpkgs with Home Manager 26.11 2026-05-31 13:14:24 +02:00
m3ta-chiron 6c4e16de3d feat(m3-kratos): enable agent-lib shared skills 2026-05-30 10:01:47 +02:00
m3ta-chiron f20dd18b5f +babysitter 2026-05-29 18:35:12 +02:00
m3ta-chiron 23b4e825b1 m3ta-home update 2026-05-29 17:38:20 +02:00
m3ta-chiron 2a37ea8fbc fix(kratos): launch Hyprland via wrapper from greetd 2026-05-25 09:51:06 +02:00
m3ta-chiron b1fb63c814 fix(kratos): simplify tuigreet login command
Root cause: The complex concatStringsSep command with power commands
(--power-shutdown, --power-reboot) and multiple --remember flags was
causing tuigreet to display its usage/flags instead of the login UI.
The quoting in the systemd binary paths may have been problematic.

Changes:
- Use lib.getExe instead of manual bin path for tuigreet
- Use simple 'Hyprland' command (found via PATH) instead of
  the full start-hyprland path which may have issues
- Remove unverified options: --remember-session, --remember-user-session,
  --user-menu, --user-menu-min-uid, --power-shutdown, --power-reboot
- Keep only verified options: --time, --remember, --asterisks, --cmd
- Update tmpfiles comment to reflect actual requirement

This provides a minimal, stable login that works reliably.
User can reboot to test.
2026-05-25 09:34:01 +02:00
m3ta-chiron 32677cfb40 fix(kratos): replace gdm with greetd/tuigreet login manager 2026-05-25 09:19:08 +02:00
m3tam3re 9f1e7fd568 Merge pull request 'fix: enable Hermes voice STT dependencies' (#21) from fix/hermes-voice-stt-deps into master
Reviewed-on: #21
2026-05-23 11:30:18 +02:00
m3ta-chiron 95aaddd8c0 fix: enable Hermes voice STT dependencies 2026-05-23 10:32:11 +02:00
m3tam3re 6588586a26 Merge pull request 'Set Hermes default model to GPT 5.5' (#20) from chore/hermes-gpt55-default-fallbacks into master
Reviewed-on: #20
2026-05-23 09:22:18 +02:00
m3ta-chiron 2c8d4da6ff Set Hermes default model to GPT 5.5 2026-05-23 09:19:38 +02:00
m3tam3re 06cc749b69 Merge pull request 'Optimize Hermes Nix service configuration' (#19) from fix/hermes-matrix-mautrix-deps into master
Reviewed-on: #19
2026-05-23 08:58:00 +02:00
m3ta-chiron b49d5c4f72 Optimize Hermes Nix service configuration 2026-05-23 08:55:05 +02:00
m3tam3re d90a20123e Merge pull request 'Fix Hermes Matrix deps for v2026.5.16' (#18) from fix/hermes-matrix-mautrix-deps into master
Reviewed-on: #18
2026-05-23 08:16:35 +02:00
m3ta-chiron 1bd78b5de8 Fix Hermes Matrix deps for v2026.5.16 2026-05-23 08:06:01 +02:00
m3ta-chiron 59ada8585f feat(atlas): deploy self-hosted honcho 2026-05-20 20:52:15 +02:00
m3tam3re 42acdbc98f flake update 2026-05-18 20:17:24 +02:00
m3tam3re af08084692 chore: fix git identity for m3-hermes 2026-05-11 19:27:11 +02:00
m3tam3re 4f9944101f chore: optimize hermes 2026-05-11 19:01:17 +02:00
m3tam3re 20d2548791 Merge pull request 'fix(m3-atlas): remove netbird-only middleware from dashboard router' (#16) from fix/remove-netbird-middleware into master
Reviewed-on: #16
2026-05-11 17:16:42 +02:00
m3ta-chiron a957fd1372 fix(m3-atlas): remove netbird-only middleware from dashboard router
Access control is handled at DNS level — dash.m3ta.dev resolves to
Netbird IP (100.81.142.56) which is unreachable from the public internet.
No need for IP whitelist middleware.
2026-05-11 17:15:16 +02:00
m3tam3re 354791f252 Merge pull request 'feat: Hermes Dashboard via m3-atlas Traefik (TLS + Netbird-only)' (#15) from feat/hermes-dashboard-traefik into master
Reviewed-on: #15
2026-05-11 16:09:53 +02:00
m3ta-chiron fc39e05beb feat: Hermes Dashboard via m3-atlas Traefik with TLS + Netbird-only access
m3-hermes:
- Add --insecure flag (required for 0.0.0.0 bind, safe behind Netbird firewall)
- Update comments to document the Traefik proxy flow

m3-atlas Traefik:
- New service: hermes-dashboard → http://100.81.231.152:9119 (Netbird)
- New router: dash.m3ta.dev with GoDaddy TLS cert
- New middleware: netbird-only (IP whitelist 100.64.0.0/16)

Flow: Browser → dash.m3ta.dev (TLS) → Traefik → Netbird → m3-hermes:9119
2026-05-11 15:53:04 +02:00
m3tam3re e6f184f24a chore: hermes dashboard over netbird 2026-05-11 11:36:49 +02:00
m3tam3re 72ef896297 Merge pull request 'feat(m3-hermes): Hermes Dashboard as systemd service with Netbird-only firewall' (#13) from feat/hermes-dashboard-service into master
Reviewed-on: #13
2026-05-11 11:26:51 +02:00
m3ta-chiron 20bd28d567 feat(m3-hermes): add Hermes Dashboard as systemd service with Netbird-only firewall
- New hermes-dashboard.service: runs 'hermes dashboard' on 0.0.0.0:9119
- Firewall restricts port 9119 to Netbird mesh VPN range (100.64.0.0/16)
- Runs as hermes user with NoNewPrivileges + ProtectSystem hardening
- Depends on hermes-agent.service (starts after gateway)
- Added placeholder hermes-api-server-key.age (needs real encryption on host)
2026-05-11 11:19:21 +02:00
m3tam3re e743808d2b Merge pull request 'feat(m3-hermes): Netbird mesh VPN + API server for Desktop App' (#12) from feat/hermes-netbird-api-server into master
Reviewed-on: #12
2026-05-11 09:15:48 +02:00
m3ta-chiron c6df5d3836 feat(m3-hermes): add Netbird mesh VPN + enable API server for Hermes Desktop 2026-05-10 11:46:21 +02:00
m3tam3re 1544764f37 chore: m3-atlas -coding 2026-05-09 10:46:47 +02:00
m3tam3re c4fefdd172 Merge pull request 'feat(m3-hermes): enable Kanban board + update for v0.13.0' (#11) from feat/hermes-v0.13-kanban into master
Reviewed-on: #11
2026-05-09 10:43:53 +02:00
m3ta-chiron ee94ebf660 feat(m3-hermes): enable kanban board + update for v0.13.0
- Add kanban config block with gateway-embedded dispatcher
  (dispatch_in_gateway=true, 60s interval)
- Update venvSitePackages path from python3.11 to python3.12
  (v0.13.0 upgraded Python runtime)
- Update checkpoints section comment for v2
2026-05-09 10:29:22 +02:00
m3tam3re 6128d0ae61 chore: udate m3-atlas 2026-05-09 10:17:14 +02:00
m3tam3re 22f15abd34 chore: flake update 2026-05-09 09:58:33 +02:00
m3tam3re 90e417525b Merge pull request 'feat: integrate m3ta-home for centralized user profiles' (#9) from feat/m3ta-home-integration into master
Reviewed-on: #9
2026-05-02 11:02:54 +02:00
m3ta-chiron a455789bee refactor: remove old home/ directory (77 files migrated to m3ta-home)
All home-manager configuration is now centralized in the m3ta-home repo:
- profiles/base/     ← shell, CLI tools, secrets
- profiles/contexts/ ← desktop, server
- profiles/sets/     ← coding, gaming, media
- users/             ← identities, preferences

Per-host overrides (monitors, XDG/MIME) remain in hosts/<name>/home.nix.
Central user integration via hosts/common/users/m3tam3re.nix.
2026-05-02 11:01:12 +02:00
m3ta-chiron 2078d6bccd docs: update AGENTS.md for m3ta-home integration, work identity, new structure 2026-05-02 10:54:42 +02:00
m3ta-chiron 5cbb975c78 feat: complete host home.nix files + add m3-daedalus, clean up m3tam3re.nix
- hosts/m3-kratos/home.nix: XDG/MIME defaults + dual DP Hyprland monitors
- hosts/m3-ares/home.nix: XDG/MIME defaults + eDP+HDMI Hyprland monitors
- hosts/m3-daedalus/home.nix: XDG/MIME defaults (no Hyprland)
- hosts/common/users/m3tam3re.nix: refactored hostFlags into let binding,
  added m3-daedalus profile (desktop/coding+media, no gaming/Hyprland)
2026-05-02 10:41:12 +02:00
m3ta-chiron f2ecd13780 fix: set home-manager.useGlobalPkgs=true for m3ta-nixpkgs overlays 2026-05-02 10:08:50 +02:00
m3ta-chiron ab1bdc9848 feat: integrate m3ta-home for centralized user profiles 2026-05-02 09:53:27 +02:00
m3tam3re 1692a34f6e Merge pull request 'feat: enable orchestrator + switch TTS to Edge (Katja voice)' (#8) from feature/orchestrator-edge-tts into master
Reviewed-on: #8
2026-05-01 16:15:13 +02:00
m3ta-chiron 2403e54039 feat: enable orchestrator + switch TTS to Edge (Seraphina voice)
- Enable delegation.orchestrator_enabled with max_spawn_depth=2
- Switch TTS from ElevenLabs (paid) to Edge TTS (free)
- Voice: de-DE-SeraphinaMultilingualNeural — friendly, multilingual German female
- No API key required
2026-05-01 16:06:49 +02:00
m3tm3re 3e8c95944c chore: hermes update 2026-05-01 12:06:23 +02:00
m3ta-chiron fbc555feeb feat: pi guardrails 2026-04-29 20:14:07 +02:00
m3ta-chiron 6a5d8f0011 feat(agents): add strict security hardening for Pi and OpenCode
Pi Guardrails:
- Enables @aliou/pi-guardrails with strict default config
- Sets onboarding.completed = true to skip onboarding prompt
- Enables pathAccess in ask mode for /nix/store and /tmp
- Adds noAccess policies for: SSH keys, GPG keys, AWS config,
  Kubernetes config, cloud CLI configs (gh/gcloud/1password/sops),
  agenix secrets, Pi auth/sessions, env files, private keys/certs
- Adds auto-deny patterns for env leakage commands:
  env, printenv, /proc/*/environ, GPG secret exports,
  ssh-add -D, password manager reads

OpenCode permissions:
- Adds permission section with global security rules
- external_directory: ask by default, allow /nix/store and /tmp
- read/edit: allow by default, deny SSH/GPG/AWS/Kube/cloud configs,
  agenix secrets, Pi auth/sessions, env files, private keys/certs
- glob: restrict sensitive path patterns
- grep: deny SSH/GPG/agenix, ask for PASSWORD/SECRET/API_KEY/PRIVATE_KEY
- bash: ask by default, allow safe git/nix commands,
  deny env/printenv/proc/GPG secret/sudo/ssh-add deletion/curl|sh
- webfetch: ask by default, allow github/nixos search
- doom_loop: ask
2026-04-29 19:48:29 +02:00
m3tam3re 9c3d10836f Merge pull request 'fix: add uv to hermes-agent service PATH' (#7) from fix/hermes-agent-uv-path into master
Reviewed-on: #7
2026-04-29 16:24:17 +02:00
m3ta-chiron a615ab61e8 fix: add uv to hermes-agent service PATH
Add pkgs.uv to systemd.services.hermes-agent.path so that CronJobs
and terminal sessions can execute PEP 723 scripts via 'uv run'
(e.g. garmin-daily.py for Garmin Connect health data).

Also adds uv to environment.systemPackages for general availability.
2026-04-29 16:18:41 +02:00
m3ta-chiron 193b8c0115 fix(git-identity): use existing gitea SSH key for agent commits
The m3ta-chiron SSH key was not accepted by Gitea.
Using the existing gitea key instead for push authentication.
2026-04-27 19:52:11 +02:00
m3tm3re f76c4dd5d4 chore: smlink pip to uv pip 2026-04-27 19:36:52 +02:00
m3tm3re 05dc6bf608 chore: smlink pip to uv pip 2026-04-27 19:07:26 +02:00
m3tam3re d524864fc3 Merge pull request 'feature/agent-git-identity' (#6) from feature/agent-git-identity into master
Reviewed-on: #6
2026-04-27 17:55:06 +02:00
m3tm3re 09e2ba8538 chore: AGENTS + nixpkgs input urls 2026-04-27 17:53:08 +02:00
m3tm3re a427f319d4 feat(agents): add gitIdentity config and git-identity rule
- coding.agents.gitIdentity enabled with m3ta-chiron identity
- coding.agents.pi.codingRules.concerns includes 'git-identity'
- Uses feature/agent-git-identity branches for m3ta-nixpkgs and agents
2026-04-27 13:24:34 +02:00
m3tm3re 936eb13794 feat: add global skills to hermes environment 2026-04-26 15:14:54 +02:00
m3tm3re 5b0e6cbd5d feat(hermes-agent): add copy-hermes-skills systemd service 2026-04-26 14:37:43 +02:00
m3tm3re 2302810d11 chore: update beads issue state and gitignore docs/plans 2026-04-26 14:35:38 +02:00
m3tm3re 25ac47a422 feat(hermes-agent): add mkOpencodeSkills integration for skills provisioning
- Add inputs parameter to module signature for flake input access
- Define hermesSkills via inputs.agents.lib.mkOpencodeSkills
- Includes customSkills from agents flake and external skills:
  - skills-basecamp (basecamp/basecamp-cli)
  - skills-anthropic (anthropics/skills)
  - skills-kestra (kestra-io/agent-skills)
- Verified with nixos-rebuild dry-run --flake .#m3-hermes (no errors)
2026-04-26 14:35:06 +02:00
m3tm3re e6cfcc346b docs(agents): expand Beads workflow documentation
- Add 6-step core workflow with examples
- Document slash commands for agent integration
- Add 'Why Beads?' section emphasizing persistence
- Note to avoid bd edit in agent contexts
- Include dependency linking examples
2026-04-26 14:12:30 +02:00
m3tm3re 09bc9da6d9 chore: complete AGENTS.md documentation
- Add comprehensive project documentation to AGENTS.md
- Remove stale docs from docs/ directory
- Update agent configs (agents.nix, pi.nix)
- Update python.nix language config
- Update .gitignore
2026-04-26 14:10:54 +02:00
m3tm3re eb06533174 Merge feature/home-profile-restructuring: home-manager profile refactoring
Refactor home-manager configuration structure:
- Reorganize from features/ to base/coding/desktop/server/profiles/
- Add language runtime modules (go, js, python, rust, typescript)
- Add LSP server configuration
- Add gaming and media profiles
- Add shell modules (fish, nushell, starship)
- Consolidate editor and git configuration
2026-04-26 13:53:00 +02:00
m3tm3re 0d81b0e5e9 chore: add beads issue tracker configuration 2026-04-26 13:49:23 +02:00
m3tm3re 0ea8b8d2eb feat(home): extract CLI tools into modular home/base structure
- Add individual modules for: bat, carapace, direnv, eza, fzf, lf, nitch,
  television, zellij, zellij-ps, zoxide
- Centralize in home/base/cli-tools/ with default.nix aggregator
- Simplify home/base/packages by removing extracted tools
2026-04-26 13:49:17 +02:00
m3tm3re 30a9a23de2 refactor: add language runtimes module and cleanup agent config
- Add home/coding/languages/ with Python, JavaScript, Rust, Go, TypeScript
- Move bun/nodejs from agents.nix to languages/javascript.nix
- Move python3 with packages to languages/python.nix
- Move npm config to javascript.nix (broader context)
- Add language options to m3-ares and m3-kratos host configs
- Move pyrefly from agents.nix to lsp/servers.nix
- Remove duplicate python3 reference (build conflict fix)
- Remove unused base/secrets/cli-tools/ duplicates
2026-04-26 13:20:22 +02:00
m3tm3re 6d0149ee6e feat: add AMD GPU tools, media packages, and productivity module
Task 3 - Gaming profile:
- Add gpu.nix with ROCm runtime/smi/info and vulkan-tools
- Import gpu.nix in gaming profile aggregator

Task 4 - Media profile:
- Add unimatrix to yt-dlp.nix packages
- (plexamp, webcord, mpv config were already present)

Task 5 - Desktop apps:
- Add productivity.nix with pomodoro-timer
- Import productivity.nix in desktop apps aggregator
2026-04-26 12:32:47 +02:00
m3tm3re d19b87f8cd feat: add coding packages module (bruno, insomnia) 2026-04-26 12:29:14 +02:00
m3tm3re 8f5d076d7b fix: make base modules enabled by default; document lazylib→lazygit
- All base/* modules now use (mkEnableOption "...") // { default = true; }
  so they activate automatically when imported — no explicit .enable = true
  required in host configs
- packages.nix: add comment documenting that lazylib does not exist in
  nixpkgs; lazygit is the correct and intended package
- zellij-ps.nix: clarify that cli.zellij-ps namespace is intentional —
  it is the home-manager module convention from m3ta-nixpkgs
- nix flake check passes (warnings are pre-existing)
2026-04-26 12:16:44 +02:00
m3tm3re 3c9a107608 feat: add missing packages and programs to base cli-tools
- packages.nix: essential packages (jq, ripgrep, fd, htop, coreutils,
  lazygit, httpie, just, devenv, gcc, go, sqlite, sqlite-vec, nix-index,
  nix-update, progress, comma, fabric-ai, llm, basecamp, hyprpaper-random,
  libnotify, trash-cli, unzip, zip, yazi)
- bat.nix: bat with nix-colors derived syntax theme
- carapace.nix: multi-shell completion (fish, nushell, bash)
- direnv.nix: automatic env loading with nix-direnv
- eza.nix: modern ls with icons, git status, long format
- lf.nix: terminal file manager with bat preview
- zoxide.nix: smarter cd with fish and nushell integration
- zellij-ps.nix: project session manager wrapping cli.zellij-ps
2026-04-26 12:06:36 +02:00
m3tm3re cc01c1d0aa fix(agents): make videoDrivers optional with safe default
For standalone Home Manager evaluation where videoDrivers may be absent
2026-04-26 11:37:17 +02:00
m3tm3re d59a6b82b6 chore: remove features.old archive and format all files
- Delete home/features.old/ (archived old flat feature modules)
- All content migrated to new profile-based structure
- Run alejandra formatter over 13 changed files
- nix flake check passes cleanly
2026-04-26 11:29:49 +02:00
m3tm3re d44bdad73a refactor: archive old features directory to features.old
The new profile-based structure (home/base, home/desktop, home/server,
home/profiles/, home/coding) is fully operational and imported via
home/lib/mkHomeConfig. The legacy home/features directory is no longer
referenced anywhere in the configuration.

Archived rather than deleted to preserve history for reference.
2026-04-26 11:22:17 +02:00
m3tm3re 797ffb2b8a fix: assert unknown profiles in mkHomeConfig; move agent modules to coding/agents
- home/lib/default.nix: add assertion for unknown profile names instead of
  silently filtering them out; remove unused 'inherit (lib) optional'
- home/coding/agents/{opencode,pi}.nix: moved from home/features/coding/
  to co-locate with agents.nix (eliminating cross-directory back-references)
- home/coding/agents/agents.nix: update imports to ./opencode.nix and ./pi.nix
- home/features/coding/: remove now-dead default.nix (nothing imported it)
2026-04-26 11:17:03 +02:00
m3tm3re 73bd2b1f2e fix: spec review - add missing fish module to base/shell
- Create home/base/shell/fish.nix
- Add to base/shell/default.nix imports
- Migrate remaining hosts from features.cli.fish to base.shell.fish
2026-04-26 11:09:50 +02:00
m3tm3re f3749c5679 feat: implement profile system with mkHomeConfig and context constraints
- Add home/lib/default.nix with mkHomeConfig utility
  - Loads base + common modules always
  - Maps profiles (coding, gaming, media) to module imports
  - Enforces desktop/server mutual exclusion via assertion
  - Context must be 'desktop', 'server', or null

- Migrate all per-host home configs to new profile system
  - m3-ares: context=desktop, profiles=[coding, gaming, media]
  - m3-kratos: context=desktop, profiles=[coding, gaming, media]
  - m3-atlas: context=server, profiles=[coding]
  - m3-helios: context=server, profiles=[]
  - m3-hermes: context=server, profiles=[]
  - m3-aether: context=server, profiles=[]
  - m3-daedalus: context=desktop, profiles=[coding, media]

- Replace features.* options with new namespaces:
  - features.cli.* -> base.shell.* / base.cliTools.* / base.secrets
  - features.desktop.* -> desktop.wm.* / desktop.apps.* / desktop.theme.*
  - gaming/media moved to profiles.gaming.* / profiles.media.*

- Fix home/coding/editor/neovim.nix: remove duplicate option declaration
  (coding.editors.neovim.enable already declared by m3ta-nixpkgs)

- Fix home/coding/lsp/servers.nix: replace removed nodePackages.typescript-language-server
  with typescript-language-server

- Fix home/desktop/theme/wallpapers.nix: correct relative path
  (was ../../.. which resolved to project root, should be ../..)
2026-04-26 11:03:43 +02:00
m3tm3re 9908b9e335 fix: code review fixes
- Fix hardcoded user path in webapps.nix (use homeDirectory)
- Normalize wallpapers option to use .enable suffix
- Remove duplicate FZF keybind declaration
- Update comments to match actual implementation
2026-04-26 10:49:01 +02:00
m3tm3re 06b430e067 fix: code review fixes
- Fix hardcoded user path in webapps.nix (use homeDirectory)
- Normalize wallpapers option to use .enable suffix
- Remove duplicate FZF keybind declaration
- Update comments to match actual implementation
2026-04-26 10:48:52 +02:00
m3tm3re 1b5bcae686 feat: create new home/ directory structure for profile-based config
New structure:
- home/base/        - Always loaded (shell, cli-tools, secrets)
- home/coding/      - Profile-independent dev tooling (editor, lsp, git, agents)
- home/profiles/    - Freely combinable profiles (gaming, media)
- home/desktop/     - Desktop-only (wm, apps, theme)
- home/server/      - Minimal server stub

Migration sources:
- home/features/cli/ → home/base/{shell,cli-tools,secrets}
- home/features/desktop/hyprland,wayland,rofi → home/desktop/wm/
- home/features/desktop/obsidian,office,webapps,crypto → home/desktop/apps/
- home/features/desktop/fonts,theme,wallpapers → home/desktop/theme/
- gaming.nix split → home/profiles/gaming/{steam,gamescope}
- media.nix split  → home/profiles/media/{obs,ffmpeg,yt-dlp,kdenlive,handbrake}

Option namespaces updated:
- features.cli.*  → base.shell.* / base.cliTools.* / base.secrets
- features.desktop.* → desktop.wm.* / desktop.apps.* / desktop.theme.*
- features.desktop.gaming → profiles.gaming.*
- features.desktop.media  → profiles.media.*

Verified: nix flake check passes (warnings only)
2026-04-26 10:37:03 +02:00
m3tm3re b1eb50a350 chore: add .worktrees to gitignore
For isolated feature development
2026-04-26 10:27:13 +02:00
m3tm3re 383f4ef56f feat: tuwunel matrix server + hermes update 2026-04-24 21:38:57 +02:00
m3tm3re d47680aef7 chore: n8n update 2026-04-22 19:26:10 +02:00
m3tm3re 047b60a6a8 refactor: update Pi agent configuration and devShell
- Switch model provider from zai/glm-5.1 to minimax/MiniMax-M2.7
- Add coding rules for Nix language and standard concerns
- Add linting tools (statix, deadnix) to devShell
- Simplify devShell configuration
- Update AGENTS.md project rules
2026-04-22 17:59:23 +02:00
m3tm3re 382b4c8c98 chore: hermes update 2026-04-21 19:00:49 +02:00
m3tam3re 90fbdfe346 Merge pull request 'feat: pi-agent wrapper' (#5) from feature/pi-agent-wrapper into master
Reviewed-on: #5
2026-04-14 18:51:44 +02:00
m3tm3re 7e0d60c95b feat: pi-agent wrapper 2026-04-14 18:36:55 +02:00
m3tam3re e289698960 Merge pull request 'feat: containerized pi agent' (#4) from feature/pi-agent-containerized into master
Reviewed-on: #4
2026-04-13 21:31:03 +02:00
m3tm3re 24e39d19e3 feat: containerized pi agent 2026-04-13 21:28:13 +02:00
m3tam3re 197704ee95 Merge pull request 'fix/evaluation-warnings-opencode-neovim' (#3) from fix/evaluation-warnings-opencode-neovim into master
Reviewed-on: #3
2026-04-13 19:40:38 +02:00
m3tm3re 53a30c7af3 chore: ignore .pi-lens artifacts and untrack cached files 2026-04-13 19:35:58 +02:00
m3tm3re 4684c5dc3e chore: include local changes and bump home-manager state to 26.05 2026-04-13 19:23:49 +02:00
m3tm3re 55aac2c910 fix: silence nix evaluation warnings for neovim/opencode/system 2026-04-13 19:18:25 +02:00
m3tam3re fa608ae399 Merge pull request 'fix(hermes): inject matrix-nio via PYTHONPATH in container' (#2) from fix/matrix-nio-pythonpath into master
Reviewed-on: #2
2026-04-13 17:00:10 +02:00
m3tam3re 717e7b0291 Merge pull request 'feat: config with agents rework' (#1) from feature/agents-rework into master
Reviewed-on: #1
2026-04-13 16:56:56 +02:00
m3tm3re 40507bb930 feat: config with agents rework 2026-04-13 16:53:33 +02:00
m3tm3re 7bf686481c feat: config with agents rework 2026-04-13 16:44:18 +02:00
Chiron Agent 3868f69958 fix(hermes): inject matrix-nio via PYTHONPATH in container
matrix-nio is installed in the container's writable venv layer
(~/.venv) but the hermes process uses the read-only Nix store Python.
This adds PYTHONPATH and LD_LIBRARY_PATH as container-level env vars
so matrix-nio + libolm (e2e encryption) are importable.
2026-04-11 05:17:51 +00:00
m3tm3re ab3332e45b feat: hermes agent for m3-ares 2026-04-09 19:56:19 +02:00
m3tm3re c92868308b chore: initialize taskplane tasks 2026-04-08 20:44:51 +02:00
m3tm3re 6a58b3656a flake update + hermes workaround for upstream bug 2026-04-08 17:48:27 +02:00
sascha.koenig 6853bb3063 +m3-hermes 2026-04-07 06:19:05 +02:00
m3tm3re 38c27eff1c +basic hermes config 2026-04-06 18:44:07 +02:00
m3tm3re d9d4b6cd85 feat: add openwork to desktop packages, update flake inputs and misc config 2026-04-03 12:45:55 +02:00
m3tm3re 44c0234739 chore: update mcp config 2026-03-31 13:30:47 +02:00
m3tm3re 4f0a92e43b fix: nushell tv warning m3-atlas 2026-03-28 10:29:05 +01:00
m3tm3re 87939ce630 feat: implement agent skills 2026-03-28 10:17:29 +01:00
m3tm3re 1bc1616a4f m3-ares update 2026-03-22 17:21:25 +01:00
m3tm3re 43523cf2d8 +television, flake-update 2026-03-21 09:39:19 +01:00
m3tm3re 08baa5ee83 updated agents input 2026-03-14 10:07:21 +01:00
m3tm3re 114644ce63 updated agents input 2026-03-14 10:06:47 +01:00
m3tm3re 411c67d2c6 add mcps to home-config; +ghostty 2026-03-14 09:26:13 +01:00
m3tm3re e9f20d7dda chore: rekey secrets 2026-03-10 04:38:25 +01:00
m3tm3re 4ae2bb0f48 chore: flake update 2026-03-07 11:44:04 +01:00
m3tm3re b08be9132b feat: litellm provider for opncode 2026-03-03 20:07:25 +01:00
m3tm3re e4195230a5 chore: fix netbird ssh 2026-03-02 19:24:28 +01:00
m3tm3re 674ce6957c feat: authentik 2026-02-28 10:06:42 +01:00
m3tm3re a9022a4f55 refactor(netbird): use port registry and named IP variables 2026-02-27 16:03:12 +01:00
m3tm3re fa9747f3e9 refactor(ports): add netbird port definitions 2026-02-27 16:03:08 +01:00
m3tm3re 4920029c65 +openspec 2026-02-18 17:57:21 +01:00
m3tm3re a12958b68f chore: flake update 2026-02-18 16:28:31 +01:00
m3tm3re 2e550b91f5 feat(opencode): integrate rules into default devShell
- Switch agents input to local path for development
- Add default devShell with Opencode rules integration
- Update .gitignore to exclude generated Opencode files
- Upgrade opencode to v1.2.6
2026-02-17 20:11:14 +01:00
m3tm3re 1d3564f360 feat(opencode): deploy rules/ to ~/.config/opencode/rules/ via home-manager
- Add xdg.configFile entry for opencode/rules
- Rules deployed alongside skills, commands, context, prompts

Refs: T4 of rules-system plan
2026-02-17 18:59:43 +01:00
m3tm3re 8ead26a791 update opencode config, secrets, tailscale, and agents input
- opencode: remove deprecated google_auth, add git_master config,
  experimental truncation, use glm-4.6v for multimodal-looker,
  upgrade categories to glm-5, remove opencode-memory plugin
- m3-atlas: add ref/exa/outline/basecamp secret declarations
- m3-kratos: enable tailscale with ssh and reset flags
- agents: switch back to remote git input
2026-02-17 08:54:13 +01:00
m3tm3re 87baf2377f +opencode-memory 2026-02-16 19:57:16 +01:00
m3tm3re 7b9caedaa4 headscale ssh acl 2026-02-16 18:59:34 +01:00
m3tm3re 105e573c53 Switch to local m3ta-nixpkgs and enable services
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-02-15 18:16:11 +01:00
m3tm3re eda9f2a1f9 Enable mem0 and qdrant services on m3-ares
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-02-15 18:15:37 +01:00
m3tm3re f3536919f0 Update OpenCode configuration and add sqlite tools
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-02-15 18:15:00 +01:00
m3tm3re 5c0fe5abda apply updates from m3ta-nixpkgs 2026-02-11 19:59:59 +01:00
m3tm3re f80e907277 +td +sidecar 2026-02-10 20:08:32 +01:00
m3tm3re ffed020289 + Obsidian 2026-02-05 09:01:35 +01:00
m3tm3re a7ac2232ee flake update 2026-02-05 08:05:20 +01:00
m3tm3re c28c1953d0 hyprland opaque keybinding fix 2026-02-04 16:51:00 +01:00
m3tm3re f7a1591451 merge flake inputs 2026-01-26 20:53:49 +01:00
m3tm3re b6dab69d42 chore folder renaming for opencode: skill -> skills, command -> sommands 2026-01-26 20:50:39 +01:00
m3tm3re b6fa6c1bcf oh-my-opencode model structure for v3.x 2026-01-24 18:09:42 +01:00
m3tm3re 2449532b30 chore: update dependencies, add basecamp secrets, improve desktop environment
- Update opencode to v1.1.34
- Add new opencode agents (Prometheus, Metis, Momus, Atlas) and categories
- Switch m3ta-nixpkgs back to local path
- Add basecamp client credentials for m3-ares and m3-kratos
- Set NIXOS_OZONE_WL=1 for better Wayland app support in Hyprland
- Remove redundant wrl.enable from flatpak portal config
2026-01-23 18:14:08 +01:00
m3tm3re 39d2b5a609 chore: optimize gitea runner 2026-01-21 17:59:58 +01:00
m3tm3re 3b2d19f8a6 fix: jq + curl to gitea action runner 2026-01-20 21:17:08 +01:00
m3tm3re 4439e6d173 model changes opencode 2026-01-19 19:26:37 +01:00
m3tm3re 048244f122 fix(m3-ares): use stable tuxedo-drivers module to work around nixpkgs#480391
The unstable tuxedo-drivers module has a type error where cfg.settings
(a set) is passed directly to lib.any which expects a list. This was
introduced in commit 15d9ec6 and fixed in a77e30e, but the fix hasn't
propagated to our nixpkgs input yet.

Workaround: disable the unstable module and import from nixpkgs-stable.
2026-01-19 19:24:34 +01:00
m3tm3re 09e93ef6f0 flake update 2026-01-17 10:17:48 +01:00
m3tm3re dd59d2a56d fix: add nodejs to gitea runner hostPackages
- Enables actions/checkout@v4 to work on self-hosted nixos runner
- Fixes: nixpkgs-5yn (Gitea Actions runner missing Node.js)
2026-01-15 19:06:42 +01:00
m3tm3re f9415c05f9 Add Gitea Actions Runner and update OpenCode config
- Add Gitea Actions Runner service on m3-atlas with nixos:host label
- Configure agenix secret for runner token
- Add Antigravity provider models to OpenCode config
- Switch m3ta-nixpkgs to local path for development
2026-01-14 20:55:23 +01:00
m3tm3re e1de4805ce bd sync: 2026-01-13 20:51:49 2026-01-13 20:51:49 +01:00
m3tm3re f482759c2e bd sync: 2026-01-13 20:51:30 2026-01-13 20:51:30 +01:00
m3tm3re ad35d27135 opencode -> 1.1.14 2026-01-12 20:25:22 +01:00
m3tm3re 18ef6174bb opencode config changes 2026-01-11 13:13:07 +01:00
m3tm3re 9dfaccb77e feat: add wallpaper feature with 5 new wallpapers
- Add wallpapers.nix module for wallpaper management
- Update hyprland and rofi configs
- Update m3-ares and m3-kratos user configs
- Update flake inputs
2026-01-10 18:09:58 +01:00
m3tm3re 5e4e007b67 opencode update 2026-01-10 06:14:02 +01:00
m3tm3re 1316d3268f chore: update opencode, hyprland config, and disable qdrant
- Remove opencode-anthropic-auth plugin from opencode config
- Add XDG environment variables for Hyprland session awareness
- Disable qdrant service on m3-ares host
2026-01-09 18:18:45 +01:00
m3tm3re b76db4800a Agents input update 2026-01-09 14:38:02 +01:00
m3tm3re 5ff19bccbe Claude Max login fix for opencode 2026-01-09 14:36:17 +01:00
m3tm3re 5713202dc9 chore: update flake inputs and adjust configs
- Update flake inputs (home-manager, HyprPanel, nixpkgs variants, NUR, AGENTS)
- Switch OpenCode agents to opencode/ model provider
- Remove webcord package from media config
- Fix thunar plugins namespace (xfce.* → pkgs.*)
- Upgrade m3-kratos kernel to 6.18
- Disable ADB and qdrant service on m3-kratos
- Enable qdrant overlay using stable nixpkgs
2026-01-09 12:56:07 +01:00
m3tm3re 4b386040db fix(hyprland): update window rules to new syntax
- Replace old windowrule format with new match: prefix syntax
- Fix property names: initialTitle → initial_title
- Fix effect names: idleinhibit → idle_inhibit, noanim → no_anim, etc.
- Replace noborder with decorate off (correct effect)
- Fix pomodoro move expression to use monitor_h variable
- Apply fixes across all host configs (m3-ares, m3-kratos, m3-daedalus)
2026-01-09 12:30:20 +01:00
m3tm3re e7a02dc45e Opencode model setup to Opencode Zen 2026-01-09 10:28:17 +01:00
m3tm3re 9566e6cd77 + beads opencode plugin 2026-01-08 04:47:31 +01:00
m3tm3re c1f274d63a adjusted auto-enabled MCPs for opencode 2026-01-07 19:31:00 +01:00
m3tm3re 1092fc98a5 Basecamp MCP 2026-01-07 19:08:33 +01:00
m3tm3re b2fff7b104 anytype-key m3-ares 2026-01-07 04:49:43 +01:00
m3tm3re c8752086a2 Opencode permissions + agent configs 2026-01-06 08:54:17 +01:00
m3tm3re 175f971809 project-launcher changes 2026-01-06 05:54:19 +01:00
m3tm3re bb85c4b40d m3ta-nixpkgs update 2026-01-04 04:02:36 +01:00
m3tm3re 21957d895c m3ta-nixpkgs update 2026-01-04 04:01:56 +01:00
m3tm3re ffd26b3139 merge 2026-01-04 03:52:38 +01:00
m3tm3re b2cdc7db2a opencode hm config 2026-01-04 03:49:13 +01:00
m3tm3re eab4c26ca4 nodejs to bun aliases 2026-01-03 11:12:01 +01:00
m3tm3re f615f3bd6a nvidia fix m3-ares 2026-01-03 09:31:59 +01:00
m3tm3re 841d7abbe7 stt, mem0, rofi-project-opener 2026-01-02 15:12:26 +01:00
m3tm3re 6ac20b65f4 AGENTS.md: add hierarchical documentation for hosts, home, features, services 2025-12-29 18:55:52 +01:00
m3tm3re 460fc927ec +AGENTS.md for AI coding agents 2025-12-28 11:28:19 +01:00
m3tm3re 949e017b22 add bun to PATH and packages, remove opencode 2025-12-28 10:25:23 +01:00
m3tm3re 27d162f3ae flake update 2025-12-27 09:52:19 +01:00
Sascha Koenig 90a5e53510 m3-atlas to 26.05 2025-12-23 12:58:52 +01:00
Sascha Koenig 5d707efe7b re-enabled ollama 2025-12-23 09:53:46 +01:00
Sascha Koenig 2f4273c86f +fabric +opencode 2025-12-23 09:31:59 +01:00
Sascha Koenig 436928b187 flake update 2025-12-23 09:25:17 +01:00
Sascha Koenig f529c59400 flake url fix m3ta-nixpkgs 2025-12-11 07:07:18 +01:00
Sascha Koenig 0e6ea7e6a3 flake update 2025-12-11 07:04:52 +01:00
Sascha Koenig aaf82b29da flake update 2025-11-29 12:48:52 +01:00
m3tm3re d24c6b28f8 flake update 2025-10-28 16:44:52 +01:00
m3tam3re 8f4b39d277 flake update 2025-10-05 14:38:50 +02:00
m3tam3re dea4ca9377 added ports module from m3ta-nixpkgs / prep for rewrite 2025-10-05 12:24:27 +02:00
m3tam3re 6cd1fdd651 beginning of bigger restructure, m3ta-nixpkgs input 2025-10-03 19:14:37 +02:00
m3tam3re b66553888b beginning of bigger restructure, m3ta-nixpkgs input 2025-10-03 14:55:24 +02:00
m3tam3re 35becfdea4 +crush 2025-09-29 18:58:19 +02:00
m3tam3re b2ad6005bc +hyprland cfg 2025-09-15 18:11:00 +02:00
m3tam3re 150dd20d32 +gamemoderun 2025-09-14 16:58:40 +02:00
m3tam3re bab10c3273 +tuxedo backlight 2025-09-08 04:40:19 +02:00
m3tam3re d5e13e2099 gnome m3-ares 2025-09-06 21:19:11 +02:00
m3tam3re 20706ce6a3 rofi dmenu exec 2025-09-04 20:23:31 +02:00
m3tam3re e9baa42695 keybindings + rofi fix 2025-09-04 18:21:56 +02:00
m3tam3re 2e6bf051f0 keybindings + rofi fix 2025-09-04 18:15:37 +02:00
Sascha Koenig a5f544e8d3 flake update 2025-09-04 15:37:14 +02:00
m3tam3re a81aee81b1 flake update 2025-09-04 15:36:53 +02:00
m3tam3re d2c127c396 AZ ssh config 2025-09-04 15:04:11 +02:00
m3tam3re 846a88475a more nix-colors; nushell fzf fix 2025-09-02 20:18:42 +02:00
m3tam3re bbb79160ae nix-colors 2025-08-30 14:32:25 +02:00
m3tam3re cc0f4c66ce Msty Studio version number 2025-08-30 09:09:30 +02:00
m3tam3re 7c355127c7 +Msty Studio 2025-08-30 09:05:58 +02:00
m3tam3re 6ce51f5cf2 cleanup 2025-08-28 20:33:23 +02:00
m3tam3re d96e054ef3 cleanup 2025-08-28 20:31:25 +02:00
m3tam3re 7a3f368aca + webapp launcher, hyprland config changes 2025-08-28 20:28:10 +02:00
Sascha Koenig 9e44101016 hyprpaper randomizer 2025-08-24 11:57:40 +02:00
Sascha Koenig d5b06c496d minor changes 2025-08-14 11:10:59 +02:00
Sascha Koenig 669ed55dc0 minor changes 2025-08-14 10:51:24 +02:00
Sascha Koenig 578bfd527f some refactoring 2025-07-20 19:30:57 +02:00
Sascha Koenig 2715ddc617 flake update 2025-07-20 14:26:14 +02:00
Sascha Koenig eedf92819c baserow upgrade 2025-07-15 17:37:38 +02:00
Sascha Koenig 0ff5c04d23 outline config + paperless overlay 2025-07-10 19:31:32 +02:00
Sascha Koenig 55e8c6cae5 +outine, flake update 2025-07-10 06:55:08 +02:00
Sascha Koenig b54213cfbb flake update 2025-07-03 19:06:08 +02:00
Sascha Koenig a11ee7ee02 +m3-daedalus 2025-06-24 20:13:48 +02:00
Sascha Koenig 8c9860674b hyprland stream-boxes 2025-05-21 14:24:30 +02:00
Sascha Koenig e1719204e9 tailscale key rotation 2025-05-20 09:35:04 +02:00
Sascha Koenig 72ceffc61d nushell fzf 2025-05-19 21:03:09 +02:00
Sascha Koenig 22cbc7c5a2 flake update 2025-05-19 17:27:39 +02:00
Sascha Koenig 68ecbd3bf1 +builder for proxmox nixos-template 2025-05-19 12:53:05 +02:00
Sascha Koenig f85332f321 gitea fix 2025-05-18 13:03:39 +02:00
Sascha Koenig 040d03423d mesa 25.0.6 overlay for doom the dark ages 2025-05-17 15:21:21 +02:00
Sascha Koenig cf731e7309 rm wl-clipboard pin 2025-05-15 09:50:08 +02:00
Sascha Koenig e0aa2783bb postgres upgrade@m3-atlas 2025-05-13 05:32:59 +02:00
Sascha Koenig 7854d75742 flake update 2025-05-12 11:22:29 +02:00
Sascha Koenig 80cff4278d port changes for gitea 2025-05-12 10:33:27 +02:00
Sascha Koenig f33bfec02e vw conf @m3-atlas 2025-05-08 09:45:25 +02:00
Sascha Koenig 20bdd1c7b6 Paperless Service @m3-atlas 2025-05-08 08:20:16 +02:00
Sascha Koenig 7fec29f602 +vautwarden, +slash@nemoti 2025-05-08 00:15:04 +02:00
m3tam3re a3a85c3596 +Tailscale @m3-ares 2025-05-06 13:23:55 +02:00
m3tam3re 279d00ae82 Headscale and Tailscale config modules 2025-05-06 13:22:39 +02:00
192 changed files with 12685 additions and 3113 deletions
+3
View File
@@ -0,0 +1,3 @@
node_modules/
runs/
*.log
+5
View File
@@ -0,0 +1,5 @@
{
"projectRoot": "/home/m3tam3re/p/NIX/nixos-config",
"isNewProject": false,
"additionalContext": "Install and configure babysitter for this existing NixOS flake configuration repository. Respect AGENTS.md instructions, Beads workflow, Nix conventions, and avoid interactive/destructive operations unless explicitly approved."
}
+4570
View File
File diff suppressed because it is too large Load Diff
+9
View File
@@ -0,0 +1,9 @@
{
"name": "nixos-config-a5c",
"version": "1.0.0",
"private": true,
"type": "module",
"dependencies": {
"@a5c-ai/babysitter-sdk": "latest"
}
}
+596
View File
@@ -0,0 +1,596 @@
{
"projectName": "nixos-config",
"description": "A reliable, elegant, multi-system NixOS flake configuration for personal desktop, server, cloud, Home Manager, package, overlay, and secret management.",
"goals": [
{
"id": "goal-reliability-1",
"description": "Keep all managed NixOS systems reproducible, reliable, and easy to validate before deployment.",
"category": "reliability",
"priority": "high",
"status": "active"
},
{
"id": "goal-architecture-1",
"description": "Maintain an elegant multi-system architecture with clear host boundaries and reusable common modules.",
"category": "architecture",
"priority": "high",
"status": "active"
},
{
"id": "goal-modularization-1",
"description": "Continue breaking up the former monorepo by keeping Home Manager profiles in m3ta-home and custom packages/modules in m3ta-nixpkgs where appropriate.",
"category": "modularization",
"priority": "high",
"status": "active"
},
{
"id": "goal-cicd-1",
"description": "CI/CD is not currently configured; add useful Gitea Actions validation later for formatting, linting, flake evaluation, and safe host checks.",
"category": "automation",
"priority": "medium",
"status": "deferred"
}
],
"techStack": {
"languages": [
{
"name": "Nix",
"role": "primary system, module, overlay, and package configuration language"
},
{
"name": "Markdown",
"role": "project, agent, and workflow documentation"
},
{
"name": "JSON/YAML",
"role": "tool configuration and metadata"
}
],
"frameworks": [
{
"name": "Nix flakes",
"category": "reproducible dependency and output model"
},
{
"name": "NixOS modules",
"category": "host and service configuration"
},
{
"name": "Home Manager",
"category": "user environment management"
},
{
"name": "Agenix",
"category": "encrypted secret management"
},
{
"name": "Disko",
"category": "server disk provisioning"
},
{
"name": "NUR",
"category": "community package access"
},
{
"name": "llm-agents.nix",
"category": "LLM agent packages overlay"
},
{
"name": "m3ta-home",
"category": "external reusable Home Manager profiles"
},
{
"name": "m3ta-nixpkgs",
"category": "external custom packages/modules/overlays"
}
],
"databases": [],
"infrastructure": [
{
"name": "m3-ares",
"category": "desktop NixOS host"
},
{
"name": "m3-kratos",
"category": "desktop NixOS host"
},
{
"name": "m3-daedalus",
"category": "portable laptop/Home Manager configuration"
},
{
"name": "m3-atlas",
"category": "primary server NixOS host"
},
{
"name": "m3-helios",
"category": "minimal server/AdGuard host"
},
{
"name": "m3-hermes",
"category": "secondary server/Hermes host"
},
{
"name": "m3-aether",
"category": "cloud VM/minimal server host"
}
],
"buildTools": [
"nix",
"nixos-rebuild",
"nix build",
"nix flake show",
"alejandra",
"statix",
"deadnix"
],
"packageManagers": [
"nix flakes"
]
},
"architecture": {
"pattern": "Pure Nix flake-based NixOS configuration repository with host-specific modules, common shared modules, overlays, custom packages, agenix secrets, and externalized Home Manager/package inputs.",
"modules": [
{
"name": "flake.nix",
"path": "flake.nix",
"description": "Top-level entry point defining inputs, packages, overlays, Home Manager modules, NixOS configurations, and dev shells."
},
{
"name": "hosts/common",
"path": "hosts/common",
"description": "Shared NixOS configuration, nix settings, overlays, Home Manager setup, ports, extra services, and users."
},
{
"name": "hosts",
"path": "hosts",
"description": "Per-host NixOS/Home Manager configurations for desktops, servers, and cloud VM."
},
{
"name": "modules/nixos",
"path": "modules/nixos",
"description": "Reusable NixOS modules."
},
{
"name": "modules/home-manager",
"path": "modules/home-manager",
"description": "Reusable Home Manager module exports."
},
{
"name": "overlays",
"path": "overlays",
"description": "Nixpkgs overlays for stable, locked, pinned, master, temporary, and agent packages."
},
{
"name": "pkgs",
"path": "pkgs",
"description": "Custom package export set."
},
{
"name": "secrets",
"path": "secrets",
"description": "Encrypted agenix secret files and registry."
}
],
"entryPoints": [
"flake.nix",
"hosts/<host>/default.nix",
"hosts/<host>/configuration.nix",
"hosts/common/default.nix",
"hosts/common/users/m3tam3re.nix",
"overlays/default.nix",
"pkgs/default.nix",
"secrets.nix"
],
"dataFlow": "flake.nix wires inputs, overlays, packages, NixOS modules, and Home Manager. Host modules import common configuration and host-specific hardware/programs/services/secrets. Host profile flags in hosts/common/users/m3tam3re.nix feed the external m3ta-home mkHome integration. Secrets flow through agenix registry and host secret modules."
},
"team": [
{
"name": "m3tam3re",
"role": "solo developer and operator",
"responsibilities": [
"architecture",
"implementation",
"host maintenance",
"deployments",
"review"
]
},
{
"name": "m3ta-chiron",
"role": "agent contributor",
"responsibilities": [
"semi-autonomous implementation",
"validation",
"documentation updates",
"conventional commits"
]
}
],
"workflows": [
{
"name": "development",
"description": "Default feature-branch workflow for solo development with conventional commits and validation before push.",
"steps": [
"review Beads issues with bd ready --json",
"claim work with bd update <id> --claim when applicable",
"edit Nix modules or project files",
"run alejandra .",
"run statix check .",
"run targeted nix flake or host dry-run checks",
"commit with conventional commit format",
"pull --rebase and push"
],
"triggers": [
"new feature",
"bug fix",
"refactor",
"agent task"
]
},
{
"name": "nix validation",
"description": "Quality gate for Nix configuration changes.",
"steps": [
"alejandra .",
"statix check .",
"deadnix check or deadnix -w when appropriate",
"nix flake show",
"sudo nixos-rebuild dry-run --flake .#<host> for affected hosts"
],
"triggers": [
"Nix code changes",
"before deployment",
"before commit"
]
},
{
"name": "host deployment",
"description": "Manual deployment after successful dry-run validation.",
"steps": [
"sudo nixos-rebuild dry-run --flake .#<host>",
"sudo nixos-rebuild switch --flake .#<host>"
],
"triggers": [
"manual host update"
]
},
{
"name": "dependency/input update",
"description": "Controlled flake input updates without manually editing flake.lock.",
"steps": [
"use nix flake update or nixos-rebuild --update-input <input>",
"validate affected outputs",
"commit flake.nix/flake.lock changes"
],
"triggers": [
"planned dependency update",
"security update"
]
},
{
"name": "beads issue tracking",
"description": "Persistent issue tracking and session handoff workflow.",
"steps": [
"bd ready --json",
"bd show <id>",
"bd update <id> --claim",
"bd close <id> --reason <summary>",
"bd dolt push"
],
"triggers": [
"start of tracked work",
"completion of tracked work"
]
}
],
"processes": [
{
"id": "cradle/project-install",
"name": "Babysitter project install",
"status": "installing",
"purpose": "Create and save a Babysitter project profile and setup recommendations."
}
],
"tools": {
"formatting": [
{
"name": "alejandra",
"purpose": "Nix formatting",
"configPaths": [
"flake.nix devShells.default"
]
}
],
"linting": [
{
"name": "statix",
"purpose": "Nix anti-pattern linting",
"configPaths": [
"flake.nix devShells.default"
]
},
{
"name": "deadnix",
"purpose": "Detect unused Nix code",
"configPaths": [
"flake.nix devShells.default"
]
}
],
"testing": [
{
"name": "nix flake show",
"purpose": "Evaluate flake outputs",
"configPaths": [
"flake.nix"
]
},
{
"name": "nixos-rebuild dry-run",
"purpose": "Validate host configurations without applying changes",
"configPaths": [
"flake.nix",
"hosts/*"
]
},
{
"name": "nix build",
"purpose": "Build selected outputs such as host toplevels or ISOs",
"configPaths": [
"flake.nix"
]
}
],
"issueTracking": [
{
"name": "Beads",
"command": "bd",
"purpose": "Persistent task tracking"
}
]
},
"services": [
{
"name": "code.m3ta.dev",
"type": "git hosting",
"url": "git+ssh://gitea@code.m3ta.dev"
},
{
"name": "GitHub",
"type": "flake input hosting",
"url": "github:* flake inputs"
},
{
"name": "Agenix",
"type": "secret encryption",
"url": "github:ryantm/agenix"
},
{
"name": "Hermes Agent",
"type": "NixOS module/agent service",
"url": "github:NousResearch/hermes-agent"
},
{
"name": "RustFS",
"type": "NixOS server service flake",
"url": "github:rustfs/rustfs-flake"
}
],
"externalIntegrations": [
{
"service": "Beads",
"category": "issue tracking",
"enabled": true
},
{
"service": "Dolt",
"category": "Beads storage/sync",
"enabled": true
},
{
"service": "Agenix",
"category": "secrets",
"enabled": true
},
{
"service": "Home Manager",
"category": "user environment",
"enabled": true
},
{
"service": "m3ta-home",
"category": "external home profiles",
"enabled": true
},
{
"service": "m3ta-nixpkgs",
"category": "external Nix modules/packages",
"enabled": true
},
{
"service": "NUR",
"category": "Nix packages",
"enabled": true
},
{
"service": "Disko",
"category": "disk provisioning",
"enabled": true
},
{
"service": "Hermes Agent",
"category": "LLM/agent service",
"enabled": true
}
],
"cicd": {
"provider": null,
"enabled": false,
"configPaths": [],
"pipelines": [],
"notes": "CI/CD is intentionally disabled for now. If re-enabled later, prefer Gitea Actions because this repository is hosted on code.m3ta.dev.",
"babysitterIntegration": {
"enabled": false,
"triggerOn": [],
"processIds": []
}
},
"painPoints": [
{
"id": "pp-architecture-1",
"description": "The repository is transitioning away from a monorepo; boundaries with m3ta-home and m3ta-nixpkgs must remain clear.",
"severity": "high",
"category": "architecture",
"discoveredVia": "user interview",
"suggestedRemediation": "Keep host-specific decisions local while moving reusable Home Manager profiles and package/module abstractions to their dedicated inputs."
},
{
"id": "pp-validation-1",
"description": "A single shared Nix change can require validating several hosts to be confident.",
"severity": "medium",
"category": "validation",
"discoveredVia": "repo structure and AGENTS workflow",
"suggestedRemediation": "Use targeted affected-host validation locally for now; add a Gitea Actions validation matrix later if CI/CD is re-enabled."
},
{
"id": "pp-dependency-1",
"description": "Multiple pinned, locked, stable, master, and external SSH flake inputs increase update complexity.",
"severity": "medium",
"category": "dependency management",
"discoveredVia": "flake and history analysis",
"suggestedRemediation": "Update inputs intentionally, group related updates, and validate affected host outputs."
},
{
"id": "pp-operations-1",
"description": "Service additions often need synchronized module, secret, and network/TLS changes.",
"severity": "medium",
"category": "operations",
"discoveredVia": "git history and tree structure",
"suggestedRemediation": "Use checklist-style issue templates or Babysitter processes for service changes."
}
],
"bottlenecks": [
{
"id": "bn-flake-1",
"description": "flake.nix and flake.lock are high-churn files whose changes can affect many hosts at once.",
"impact": "High; evaluation failures can block all hosts.",
"location": "flake.nix, flake.lock",
"frequency": "very frequent"
},
{
"id": "bn-secrets-1",
"description": "Secret registry and host secret modules must stay aligned with encrypted .age files.",
"impact": "Medium to high; missing or mismatched secrets break host deployment.",
"location": "secrets.nix, hosts/*/secrets.nix, secrets/*.age",
"frequency": "recurring"
},
{
"id": "bn-services-1",
"description": "Server service changes can span service modules, secrets, Traefik/networking, and flake inputs.",
"impact": "High for m3-atlas and m3-hermes changes; requires host-specific dry-runs.",
"location": "hosts/m3-atlas/services, hosts/m3-hermes/services, hosts/common",
"frequency": "frequent"
},
{
"id": "bn-home-1",
"description": "Home Manager behavior depends on both the external m3ta-home input and local host flags.",
"impact": "Medium; may require coordinated updates across repositories.",
"location": "flake.nix, hosts/common/users/m3tam3re.nix, m3ta-home input",
"frequency": "frequent after migration"
}
],
"conventions": {
"naming": {
"files": "hyphen-case for Nix/docs where practical; host directories use m3-* names",
"hosts": "m3-<greek-name>",
"modules": "one module per file/directory where possible",
"nixVariables": "camelCase"
},
"git": {
"branchStrategy": "default feature branches for non-trivial work; master as integration branch",
"commits": "conventional commits for agent work",
"reviews": "optional for solo development",
"releaseCadence": "continuous/manual as needed",
"remote": "code.m3ta.dev over SSH for private inputs and repo access"
},
"codeStyle": {
"formatter": "alejandra",
"indentation": "2 spaces",
"nixStyle": "explicit pkgs references preferred; avoid with pkgs, builtins.fetchTarball, import <nixpkgs>, builtins.getAttr/hasAttr"
},
"importOrder": [
"module function arguments",
"imports",
"let bindings",
"options/config"
],
"errorHandling": "Nix configuration should fail explicitly during evaluation/build; avoid hiding errors or impure paths.",
"testingConventions": "Run alejandra, statix, deadnix as appropriate, nix flake show, and host-specific nixos-rebuild dry-run before switching.",
"additionalRules": [
"Use Beads for persistent task tracking.",
"Use non-interactive flags for shell file operations.",
"Do not modify flake.lock directly; use nix flake update.",
"Do not commit plaintext secrets.",
"Use SSH URLs for code.m3ta.dev flake inputs.",
"Operate Babysitter semi-autonomously with breakpoints for destructive, deployment, or architecture-changing decisions."
]
},
"repositories": [
{
"name": "nixos-config",
"path": "/home/m3tam3re/p/NIX/nixos-config",
"role": "primary multi-host NixOS configuration"
},
{
"name": "m3ta-home",
"url": "git+ssh://gitea@code.m3ta.dev/m3tam3re/m3ta-home",
"role": "external Home Manager profiles"
},
{
"name": "m3ta-nixpkgs",
"url": "git+ssh://gitea@code.m3ta.dev/m3tam3re/nixpkgs",
"role": "external custom packages/modules/overlays"
}
],
"claudeMdInstructions": [
"Respect AGENTS.md as the source of project workflow rules.",
"Resolve the active Babysitter process library before using library processes.",
"Use cradle/project-install for project setup or profile refresh.",
"Use evolutionary GSD: map affected Nix modules/hosts, make focused changes, verify, and iterate.",
"Prefer alejandra, statix, deadnix, nix flake show, and targeted host dry-runs for Nix changes.",
"Preserve boundaries between nixos-config, m3ta-home, and m3ta-nixpkgs.",
"Use breakpoints for destructive operations, deployments, architecture changes, and secret-handling decisions.",
"Babysitter CI/CD is not currently enabled; if re-added later, use Gitea Actions rather than GitHub Actions."
],
"installedSkills": [
"project-install",
"babysit",
"specializations/devops-sre-platform/skills/cicd-pipelines/SKILL.md",
"specializations/devops-sre-platform/skills/gitops/SKILL.md",
"specializations/devops-sre-platform/skills/secrets-management/SKILL.md"
],
"installedAgents": [
"general-purpose",
"specializations/devops-sre-platform/agents/platform-engineer/AGENT.md",
"specializations/devops-sre-platform/agents/cicd-specialist/AGENT.md"
],
"installedProcesses": [
"cradle/project-install",
"methodologies/gsd/quick.js",
"methodologies/gsd/verify-work.js",
"methodologies/gsd/iterative-convergence.js",
"methodologies/evolutionary.js",
"specializations/devops-sre-platform/iac-testing.js"
],
"preferences": {
"babysitterAutonomy": "semi-autonomous",
"breakpointTolerance": "moderate",
"externalIntegrationsRequested": false,
"cicdDesired": false,
"cicdNote": "Deferred for now; Gitea Actions is the preferred provider if CI/CD is added later."
},
"createdAt": "2026-05-29T15:50:48.754Z",
"updatedAt": "2026-05-29T16:07:19.245463Z",
"version": 1
}
+238
View File
@@ -0,0 +1,238 @@
# Project Profile: nixos-config
A reliable, elegant, multi-system NixOS flake configuration for personal desktop, server, cloud, Home Manager, package, overlay, and secret management.
> Last updated: 2026-05-29T16:02:11.092188Z | Version: 1
## Goals
- **reliability** [high]: Keep all managed NixOS systems reproducible, reliable, and easy to validate before deployment. (active)
- **architecture** [high]: Maintain an elegant multi-system architecture with clear host boundaries and reusable common modules. (active)
- **modularization** [high]: Continue breaking up the former monorepo by keeping Home Manager profiles in m3ta-home and custom packages/modules in m3ta-nixpkgs where appropriate. (active)
- **automation** [medium]: CI/CD is not currently configured; add useful Gitea Actions validation later for formatting, linting, flake evaluation, and safe host checks. (deferred)
## Tech Stack
### Languages
- Nix (primary system, module, overlay, and package configuration language)
- Markdown (project, agent, and workflow documentation)
- JSON/YAML (tool configuration and metadata)
### Frameworks
- Nix flakes [reproducible dependency and output model]
- NixOS modules [host and service configuration]
- Home Manager [user environment management]
- Agenix [encrypted secret management]
- Disko [server disk provisioning]
- NUR [community package access]
- llm-agents.nix [LLM agent packages overlay]
- m3ta-home [external reusable Home Manager profiles]
- m3ta-nixpkgs [external custom packages/modules/overlays]
### Infrastructure
- m3-ares [desktop NixOS host]
- m3-kratos [desktop NixOS host]
- m3-daedalus [portable laptop/Home Manager configuration]
- m3-atlas [primary server NixOS host]
- m3-helios [minimal server/AdGuard host]
- m3-hermes [secondary server/Hermes host]
- m3-aether [cloud VM/minimal server host]
**Build tools:** nix, nixos-rebuild, nix build, nix flake show, alejandra, statix, deadnix
**Package managers:** nix flakes
## Architecture
**Pattern:** Pure Nix flake-based NixOS configuration repository with host-specific modules, common shared modules, overlays, custom packages, agenix secrets, and externalized Home Manager/package inputs.
**Data flow:** flake.nix wires inputs, overlays, packages, NixOS modules, and Home Manager. Host modules import common configuration and host-specific hardware/programs/services/secrets. Host profile flags in hosts/common/users/m3tam3re.nix feed the external m3ta-home mkHome integration. Secrets flow through agenix registry and host secret modules.
### Modules
| Module | Path | Description |
|--------|------|-------------|
| flake.nix | `flake.nix` | Top-level entry point defining inputs, packages, overlays, Home Manager modules, NixOS configurations, and dev shells. |
| hosts/common | `hosts/common` | Shared NixOS configuration, nix settings, overlays, Home Manager setup, ports, extra services, and users. |
| hosts | `hosts` | Per-host NixOS/Home Manager configurations for desktops, servers, and cloud VM. |
| modules/nixos | `modules/nixos` | Reusable NixOS modules. |
| modules/home-manager | `modules/home-manager` | Reusable Home Manager module exports. |
| overlays | `overlays` | Nixpkgs overlays for stable, locked, pinned, master, temporary, and agent packages. |
| pkgs | `pkgs` | Custom package export set. |
| secrets | `secrets` | Encrypted agenix secret files and registry. |
**Entry points:** `flake.nix`, `hosts/<host>/default.nix`, `hosts/<host>/configuration.nix`, `hosts/common/default.nix`, `hosts/common/users/m3tam3re.nix`, `overlays/default.nix`, `pkgs/default.nix`, `secrets.nix`
## Team
- **m3tam3re** (solo developer and operator): architecture, implementation, host maintenance, deployments, review
- **m3ta-chiron** (agent contributor): semi-autonomous implementation, validation, documentation updates, conventional commits
## Workflows
### development
Default feature-branch workflow for solo development with conventional commits and validation before push.
**Triggers:** new feature, bug fix, refactor, agent task
1. review Beads issues with bd ready --json
2. claim work with bd update <id> --claim when applicable
3. edit Nix modules or project files
4. run alejandra .
5. run statix check .
6. run targeted nix flake or host dry-run checks
7. commit with conventional commit format
8. pull --rebase and push
### nix validation
Quality gate for Nix configuration changes.
**Triggers:** Nix code changes, before deployment, before commit
1. alejandra .
2. statix check .
3. deadnix check or deadnix -w when appropriate
4. nix flake show
5. sudo nixos-rebuild dry-run --flake .#<host> for affected hosts
### host deployment
Manual deployment after successful dry-run validation.
**Triggers:** manual host update
1. sudo nixos-rebuild dry-run --flake .#<host>
2. sudo nixos-rebuild switch --flake .#<host>
### dependency/input update
Controlled flake input updates without manually editing flake.lock.
**Triggers:** planned dependency update, security update
1. use nix flake update or nixos-rebuild --update-input <input>
2. validate affected outputs
3. commit flake.nix/flake.lock changes
### beads issue tracking
Persistent issue tracking and session handoff workflow.
**Triggers:** start of tracked work, completion of tracked work
1. bd ready --json
2. bd show <id>
3. bd update <id> --claim
4. bd close <id> --reason <summary>
5. bd dolt push
## Processes
- **Babysitter project install** (`cradle/project-install`, undefined)
## Tools
### Linting
- statix
- deadnix
### Testing
- nix flake show
- nixos-rebuild dry-run
- nix build
### Formatting
- alejandra
## Services
- **code.m3ta.dev** (git hosting) - git+ssh://gitea@code.m3ta.dev
- **GitHub** (flake input hosting) - github:* flake inputs
- **Agenix** (secret encryption) - github:ryantm/agenix
- **Hermes Agent** (NixOS module/agent service) - github:NousResearch/hermes-agent
- **RustFS** (NixOS server service flake) - github:rustfs/rustfs-flake
## CI/CD
**Status:** Not configured/enabled for now.
No Babysitter CI/CD workflow is currently installed. If CI/CD is added later, prefer Gitea Actions because this repository is hosted on code.m3ta.dev.
## Pain Points
- **high** [architecture]: The repository is transitioning away from a monorepo; boundaries with m3ta-home and m3ta-nixpkgs must remain clear.
- Remediation: Keep host-specific decisions local while moving reusable Home Manager profiles and package/module abstractions to their dedicated inputs.
- **medium** [validation]: A single shared Nix change can require validating several hosts to be confident.
- Remediation: Use targeted affected-host validation locally for now; add a Gitea Actions validation matrix later if CI/CD is re-enabled.
- **medium** [dependency management]: Multiple pinned, locked, stable, master, and external SSH flake inputs increase update complexity.
- Remediation: Update inputs intentionally, group related updates, and validate affected host outputs.
- **medium** [operations]: Service additions often need synchronized module, secret, and network/TLS changes.
- Remediation: Use checklist-style issue templates or Babysitter processes for service changes.
## Bottlenecks
- flake.nix and flake.lock are high-churn files whose changes can affect many hosts at once. at flake.nix, flake.lock (very frequent)
Impact: High; evaluation failures can block all hosts.
- Secret registry and host secret modules must stay aligned with encrypted .age files. at secrets.nix, hosts/*/secrets.nix, secrets/*.age (recurring)
Impact: Medium to high; missing or mismatched secrets break host deployment.
- Server service changes can span service modules, secrets, Traefik/networking, and flake inputs. at hosts/m3-atlas/services, hosts/m3-hermes/services, hosts/common (frequent)
Impact: High for m3-atlas and m3-hermes changes; requires host-specific dry-runs.
- Home Manager behavior depends on both the external m3ta-home input and local host flags. at flake.nix, hosts/common/users/m3tam3re.nix, m3ta-home input (frequent after migration)
Impact: Medium; may require coordinated updates across repositories.
## Conventions
### Naming
- **files:** hyphen-case for Nix/docs where practical; host directories use m3-* names
- **hosts:** m3-<greek-name>
- **modules:** one module per file/directory where possible
- **nixVariables:** camelCase
### Git
- **branchStrategy:** default feature branches for non-trivial work; master as integration branch
- **commits:** conventional commits for agent work
- **reviews:** optional for solo development
- **releaseCadence:** continuous/manual as needed
- **remote:** code.m3ta.dev over SSH for private inputs and repo access
**Import order:** module function arguments > imports > let bindings > options/config
**Error handling:** Nix configuration should fail explicitly during evaluation/build; avoid hiding errors or impure paths.
**Testing:** Run alejandra, statix, deadnix as appropriate, nix flake show, and host-specific nixos-rebuild dry-run before switching.
### Additional Rules
- Use Beads for persistent task tracking.
- Use non-interactive flags for shell file operations.
- Do not modify flake.lock directly; use nix flake update.
- Do not commit plaintext secrets.
- Use SSH URLs for code.m3ta.dev flake inputs.
- Operate Babysitter semi-autonomously with breakpoints for destructive, deployment, or architecture-changing decisions.
## Repositories
- **nixos-config** [`/home/m3tam3re/p/NIX/nixos-config`]
- **m3ta-home** - git+ssh://gitea@code.m3ta.dev/m3tam3re/m3ta-home
- **m3ta-nixpkgs** - git+ssh://gitea@code.m3ta.dev/m3tam3re/nixpkgs
## CLAUDE.md Instructions
- Respect AGENTS.md as the source of project workflow rules.
- Resolve the active Babysitter process library before using library processes.
- Use cradle/project-install for project setup or profile refresh.
- Use evolutionary GSD: map affected Nix modules/hosts, make focused changes, verify, and iterate.
- Prefer alejandra, statix, deadnix, nix flake show, and targeted host dry-runs for Nix changes.
- Preserve boundaries between nixos-config, m3ta-home, and m3ta-nixpkgs.
- Use breakpoints for destructive operations, deployments, architecture changes, and secret-handling decisions.
- Babysitter CI/CD is not currently enabled; if re-added later, use Gitea Actions rather than GitHub Actions.
## Installed Extensions
- Skills: project-install, babysit, specializations/devops-sre-platform/skills/cicd-pipelines/SKILL.md, specializations/devops-sre-platform/skills/gitops/SKILL.md, specializations/devops-sre-platform/skills/secrets-management/SKILL.md
- Agents: general-purpose, specializations/devops-sre-platform/agents/platform-engineer/AGENT.md, specializations/devops-sre-platform/agents/cicd-specialist/AGENT.md
- Processes: cradle/project-install, methodologies/gsd/quick.js, methodologies/gsd/verify-work.js, methodologies/gsd/iterative-convergence.js, methodologies/evolutionary.js, specializations/devops-sre-platform/iac-testing.js
+53
View File
@@ -0,0 +1,53 @@
{
"qualityThreshold": 80,
"testCoverage": {
"minimum": 0,
"rationale": "NixOS configuration repository without a coverage-producing test suite."
},
"formatting": [
{
"name": "alejandra",
"command": "alejandra .",
"ciCommand": "alejandra --check ."
}
],
"linting": [
{
"name": "statix",
"command": "statix check ."
},
{
"name": "deadnix",
"command": "deadnix . --fail"
}
],
"evaluation": [
{
"name": "flake outputs",
"command": "nix flake show"
},
{
"name": "affected host dry-run",
"command": "sudo nixos-rebuild dry-run --flake .#<host>",
"when": "Run for affected hosts when practical and safe."
}
],
"commitChecks": [
"alejandra .",
"statix check .",
"deadnix . --fail",
"nix flake show"
],
"deployGates": [
"formatting passes",
"linting passes",
"flake outputs evaluate",
"affected host dry-run succeeds",
"secrets are encrypted and host secret modules remain aligned"
],
"cicdIntegrationPoints": [],
"cicd": {
"enabled": false,
"notes": "No CI/CD integration is currently configured. Add Gitea Actions later if automated Babysitter or Nix validation is desired."
}
}
+73
View File
@@ -0,0 +1,73 @@
# Dolt database (managed by Dolt, not git)
dolt/
embeddeddolt/
# Runtime files
bd.sock
bd.sock.startlock
sync-state.json
last-touched
.exclusive-lock
# Daemon runtime (lock, log, pid)
daemon.*
# Interactions log (runtime, not versioned)
interactions.jsonl
# Push state (runtime, per-machine)
push-state.json
# Lock files (various runtime locks)
*.lock
# Credential key (encryption key for federation peer auth — never commit)
.beads-credential-key
# Local version tracking (prevents upgrade notification spam after git ops)
.local_version
# Worktree redirect file (contains relative path to main repo's .beads/)
# Must not be committed as paths would be wrong in other clones
redirect
# Sync state (local-only, per-machine)
# These files are machine-specific and should not be shared across clones
.sync.lock
export-state/
export-state.json
# Ephemeral store (SQLite - wisps/molecules, intentionally not versioned)
ephemeral.sqlite3
ephemeral.sqlite3-journal
ephemeral.sqlite3-wal
ephemeral.sqlite3-shm
# Dolt server management (auto-started by bd)
dolt-server.pid
dolt-server.log
dolt-server.lock
dolt-server.port
dolt-server.activity
# Corrupt backup directories (created by bd doctor --fix recovery)
*.corrupt.backup/
# Backup data (auto-exported JSONL, local-only)
backup/
# Per-project environment file (Dolt connection config, GH#2520)
.env
# Legacy files (from pre-Dolt versions)
*.db
*.db?*
*.db-journal
*.db-wal
*.db-shm
db.sqlite
bd.db
# NOTE: Do NOT add negation patterns here.
# They would override fork protection in .git/info/exclude.
# Config files (metadata.json, config.yaml) are tracked by git by default
# since no pattern above ignores them.
+81
View File
@@ -0,0 +1,81 @@
# Beads - AI-Native Issue Tracking
Welcome to Beads! This repository uses **Beads** for issue tracking - a modern, AI-native tool designed to live directly in your codebase alongside your code.
## What is Beads?
Beads is issue tracking that lives in your repo, making it perfect for AI coding agents and developers who want their issues close to their code. No web UI required - everything works through the CLI and integrates seamlessly with git.
**Learn more:** [github.com/steveyegge/beads](https://github.com/steveyegge/beads)
## Quick Start
### Essential Commands
```bash
# Create new issues
bd create "Add user authentication"
# View all issues
bd list
# View issue details
bd show <issue-id>
# Update issue status
bd update <issue-id> --claim
bd update <issue-id> --status done
# Sync with Dolt remote
bd dolt push
```
### Working with Issues
Issues in Beads are:
- **Git-native**: Stored in Dolt database with version control and branching
- **AI-friendly**: CLI-first design works perfectly with AI coding agents
- **Branch-aware**: Issues can follow your branch workflow
- **Always in sync**: Auto-syncs with your commits
## Why Beads?
**AI-Native Design**
- Built specifically for AI-assisted development workflows
- CLI-first interface works seamlessly with AI coding agents
- No context switching to web UIs
🚀 **Developer Focused**
- Issues live in your repo, right next to your code
- Works offline, syncs when you push
- Fast, lightweight, and stays out of your way
🔧 **Git Integration**
- Automatic sync with git commits
- Branch-aware issue tracking
- Dolt-native three-way merge resolution
## Get Started with Beads
Try Beads in your own projects:
```bash
# Install Beads
curl -sSL https://raw.githubusercontent.com/steveyegge/beads/main/scripts/install.sh | bash
# Initialize in your repo
bd init
# Create your first issue
bd create "Try out Beads"
```
## Learn More
- **Documentation**: [github.com/steveyegge/beads/docs](https://github.com/steveyegge/beads/tree/main/docs)
- **Quick Start Guide**: Run `bd quickstart`
- **Examples**: [github.com/steveyegge/beads/examples](https://github.com/steveyegge/beads/tree/main/examples)
---
*Beads: Issue tracking that moves at the speed of thought*
+56
View File
@@ -0,0 +1,56 @@
# Beads Configuration File
# This file configures default behavior for all bd commands in this repository
# All settings can also be set via environment variables (BD_* prefix)
# or overridden with command-line flags
# Issue prefix for this repository (used by bd init)
# If not set, bd init will auto-detect from directory name
# Example: issue-prefix: "myproject" creates issues like "myproject-1", "myproject-2", etc.
# issue-prefix: ""
# Use no-db mode: JSONL-only, no Dolt database
# When true, bd will use .beads/issues.jsonl as the source of truth
# no-db: false
# Enable JSON output by default
# json: false
# Feedback title formatting for mutating commands (create/update/close/dep/edit)
# 0 = hide titles, N > 0 = truncate to N characters
# output:
# title-length: 255
# Default actor for audit trails (overridden by BEADS_ACTOR or --actor)
# actor: ""
# Export events (audit trail) to .beads/events.jsonl on each flush/sync
# When enabled, new events are appended incrementally using a high-water mark.
# Use 'bd export --events' to trigger manually regardless of this setting.
# events-export: false
# Multi-repo configuration (experimental - bd-307)
# Allows hydrating from multiple repositories and routing writes to the correct database
# repos:
# primary: "." # Primary repo (where this database lives)
# additional: # Additional repos to hydrate from (read-only)
# - ~/beads-planning # Personal planning repo
# - ~/work-planning # Work planning repo
# JSONL backup (periodic export for off-machine recovery)
# Auto-enabled when a git remote exists. Override explicitly:
# backup:
# enabled: false # Disable auto-backup entirely
# interval: 15m # Minimum time between auto-exports
# git-push: false # Disable git push (export locally only)
# git-repo: "" # Separate git repo for backups (default: project repo)
# Integration settings (access with 'bd config get/set')
# These are stored in the database, not in this file:
# - jira.url
# - jira.project
# - linear.url
# - linear.api-key
# - github.org
# - github.repo
sync.remote: "git+ssh://gitea@code.m3ta.dev/m3tam3re/nixos-config.git"
+24
View File
@@ -0,0 +1,24 @@
#!/usr/bin/env sh
# --- BEGIN BEADS INTEGRATION v1.0.3 ---
# This section is managed by beads. Do not remove these markers.
if command -v bd >/dev/null 2>&1; then
export BD_GIT_HOOK=1
_bd_timeout=${BEADS_HOOK_TIMEOUT:-300}
if command -v timeout >/dev/null 2>&1; then
timeout "$_bd_timeout" bd hooks run post-checkout "$@"
_bd_exit=$?
if [ $_bd_exit -eq 124 ]; then
echo >&2 "beads: hook 'post-checkout' timed out after ${_bd_timeout}s — continuing without beads"
_bd_exit=0
fi
else
bd hooks run post-checkout "$@"
_bd_exit=$?
fi
if [ $_bd_exit -eq 3 ]; then
echo >&2 "beads: database not initialized — skipping hook 'post-checkout'"
_bd_exit=0
fi
if [ $_bd_exit -ne 0 ]; then exit $_bd_exit; fi
fi
# --- END BEADS INTEGRATION v1.0.3 ---
+24
View File
@@ -0,0 +1,24 @@
#!/usr/bin/env sh
# --- BEGIN BEADS INTEGRATION v1.0.3 ---
# This section is managed by beads. Do not remove these markers.
if command -v bd >/dev/null 2>&1; then
export BD_GIT_HOOK=1
_bd_timeout=${BEADS_HOOK_TIMEOUT:-300}
if command -v timeout >/dev/null 2>&1; then
timeout "$_bd_timeout" bd hooks run post-merge "$@"
_bd_exit=$?
if [ $_bd_exit -eq 124 ]; then
echo >&2 "beads: hook 'post-merge' timed out after ${_bd_timeout}s — continuing without beads"
_bd_exit=0
fi
else
bd hooks run post-merge "$@"
_bd_exit=$?
fi
if [ $_bd_exit -eq 3 ]; then
echo >&2 "beads: database not initialized — skipping hook 'post-merge'"
_bd_exit=0
fi
if [ $_bd_exit -ne 0 ]; then exit $_bd_exit; fi
fi
# --- END BEADS INTEGRATION v1.0.3 ---
+24
View File
@@ -0,0 +1,24 @@
#!/usr/bin/env sh
# --- BEGIN BEADS INTEGRATION v1.0.3 ---
# This section is managed by beads. Do not remove these markers.
if command -v bd >/dev/null 2>&1; then
export BD_GIT_HOOK=1
_bd_timeout=${BEADS_HOOK_TIMEOUT:-300}
if command -v timeout >/dev/null 2>&1; then
timeout "$_bd_timeout" bd hooks run pre-commit "$@"
_bd_exit=$?
if [ $_bd_exit -eq 124 ]; then
echo >&2 "beads: hook 'pre-commit' timed out after ${_bd_timeout}s — continuing without beads"
_bd_exit=0
fi
else
bd hooks run pre-commit "$@"
_bd_exit=$?
fi
if [ $_bd_exit -eq 3 ]; then
echo >&2 "beads: database not initialized — skipping hook 'pre-commit'"
_bd_exit=0
fi
if [ $_bd_exit -ne 0 ]; then exit $_bd_exit; fi
fi
# --- END BEADS INTEGRATION v1.0.3 ---
+24
View File
@@ -0,0 +1,24 @@
#!/usr/bin/env sh
# --- BEGIN BEADS INTEGRATION v1.0.3 ---
# This section is managed by beads. Do not remove these markers.
if command -v bd >/dev/null 2>&1; then
export BD_GIT_HOOK=1
_bd_timeout=${BEADS_HOOK_TIMEOUT:-300}
if command -v timeout >/dev/null 2>&1; then
timeout "$_bd_timeout" bd hooks run pre-push "$@"
_bd_exit=$?
if [ $_bd_exit -eq 124 ]; then
echo >&2 "beads: hook 'pre-push' timed out after ${_bd_timeout}s — continuing without beads"
_bd_exit=0
fi
else
bd hooks run pre-push "$@"
_bd_exit=$?
fi
if [ $_bd_exit -eq 3 ]; then
echo >&2 "beads: database not initialized — skipping hook 'pre-push'"
_bd_exit=0
fi
if [ $_bd_exit -ne 0 ]; then exit $_bd_exit; fi
fi
# --- END BEADS INTEGRATION v1.0.3 ---
+24
View File
@@ -0,0 +1,24 @@
#!/usr/bin/env sh
# --- BEGIN BEADS INTEGRATION v1.0.3 ---
# This section is managed by beads. Do not remove these markers.
if command -v bd >/dev/null 2>&1; then
export BD_GIT_HOOK=1
_bd_timeout=${BEADS_HOOK_TIMEOUT:-300}
if command -v timeout >/dev/null 2>&1; then
timeout "$_bd_timeout" bd hooks run prepare-commit-msg "$@"
_bd_exit=$?
if [ $_bd_exit -eq 124 ]; then
echo >&2 "beads: hook 'prepare-commit-msg' timed out after ${_bd_timeout}s — continuing without beads"
_bd_exit=0
fi
else
bd hooks run prepare-commit-msg "$@"
_bd_exit=$?
fi
if [ $_bd_exit -eq 3 ]; then
echo >&2 "beads: database not initialized — skipping hook 'prepare-commit-msg'"
_bd_exit=0
fi
if [ $_bd_exit -ne 0 ]; then exit $_bd_exit; fi
fi
# --- END BEADS INTEGRATION v1.0.3 ---
+3
View File
@@ -0,0 +1,3 @@
{"_type":"issue","id":"home-profile-restructuring-edz","title":"Create copy-hermes-skills systemd service","status":"closed","priority":1,"issue_type":"task","assignee":"m3tm3re","owner":"p@m3ta.dev","estimated_minutes":1,"created_at":"2026-04-26T12:30:10Z","created_by":"m3tm3re","updated_at":"2026-04-26T12:44:42Z","started_at":"2026-04-26T12:36:30Z","closed_at":"2026-04-26T12:44:42Z","close_reason":"Created systemd service in hosts/m3-hermes/services/hermes-agent.nix - copies skills to /var/lib/hermes/.agents/skills before hermes-agent starts","labels":["hermes-agent","nixos"],"dependencies":[{"issue_id":"home-profile-restructuring-edz","depends_on_id":"home-profile-restructuring-ycz","type":"blocks","created_at":"2026-04-26T14:30:57Z","created_by":"m3tm3re","metadata":"{}"}],"dependency_count":1,"dependent_count":1,"comment_count":0}
{"_type":"issue","id":"home-profile-restructuring-ycz","title":"Build hermes-agent skills using mkOpencodeSkills","status":"closed","priority":1,"issue_type":"task","assignee":"m3tm3re","owner":"p@m3ta.dev","estimated_minutes":2,"created_at":"2026-04-26T12:30:09Z","created_by":"m3tm3re","updated_at":"2026-04-26T12:35:15Z","started_at":"2026-04-26T12:31:35Z","closed_at":"2026-04-26T12:35:15Z","close_reason":"Added inputs to module signature and defined hermesSkills via inputs.agents.lib.mkOpencodeSkills with basecamp, anthropic, and kestra external skills. Verified with nixos-rebuild dry-run --flake .#m3-hermes (no errors).","labels":["hermes-agent","nixos"],"dependency_count":0,"dependent_count":1,"comment_count":0}
{"_type":"issue","id":"home-profile-restructuring-cxa","title":"Verify skills available at /var/lib/hermes/.agents/skills","status":"closed","priority":2,"issue_type":"task","assignee":"m3tm3re","owner":"p@m3ta.dev","estimated_minutes":1,"created_at":"2026-04-26T12:30:10Z","created_by":"m3tm3re","updated_at":"2026-04-26T12:50:58Z","started_at":"2026-04-26T12:38:15Z","closed_at":"2026-04-26T12:50:58Z","close_reason":"Manually verified - skills are present at /var/lib/hermes/.agents/skills on m3-hermes","labels":["hermes-agent","testing"],"dependencies":[{"issue_id":"home-profile-restructuring-cxa","depends_on_id":"home-profile-restructuring-edz","type":"blocks","created_at":"2026-04-26T14:30:57Z","created_by":"m3tm3re","metadata":"{}"}],"dependency_count":1,"dependent_count":0,"comment_count":0}
+7
View File
@@ -0,0 +1,7 @@
{
"database": "dolt",
"backend": "dolt",
"dolt_mode": "embedded",
"dolt_database": "home_profile_restructuring",
"project_id": "664fc7e3-94eb-4874-aab6-e47835abe9d8"
}
+3
View File
@@ -0,0 +1,3 @@
# Use bd merge for beads JSONL files
.beads/issues.jsonl merge=beads
+55
View File
@@ -0,0 +1,55 @@
# Sisyphus work session data
.sisyphus/
# Editor files
*~
.*.swp
.*.swo
.*.swx
# Build artifacts
result
result-*
.direnv/
# IDE
.vscode/
.idea/
*.iml
# OS
.DS_Store
Thumbs.db
# Opencode rules
.opencode-rules
opencode.json
# AI agent state
.claude/
.sidecar/
.sidecar-*
.sisyphus/
.sidecar-agent
.sidecar-task
.sidecar-pr
.sidecar-start.sh
.sidecar-base
.td-root
.cache
.pi*
.worktrees/
docs/plans/
CLAUDE.md
# Beads / Dolt files (added by bd init)
.dolt/
*.db
.beads-credential-key
# --- babysitter managed ---
.a5c/creds.env
.a5c/creds.env.tmp.*
.a5c/logs/
.a5c/runs/
# --- end babysitter managed ---
+445
View File
@@ -0,0 +1,445 @@
# Agent Instructions
This project uses **bd** (beads) for issue tracking. Run `bd prime` for full workflow context.
## Quick Reference
```bash
bd ready # Find available work
bd show <id> # View issue details
bd update <id> --claim # Claim work atomically
bd close <id> # Complete work
bd dolt push # Push beads data to remote
```
## Non-Interactive Shell Commands
**ALWAYS use non-interactive flags** with file operations to avoid hanging on confirmation prompts.
Shell commands like `cp`, `mv`, and `rm` may be aliased to include `-i` (interactive) mode on some systems, causing the agent to hang indefinitely waiting for y/n input.
**Use these forms instead:**
```bash
# Force overwrite without prompting
cp -f source dest # NOT: cp source dest
mv -f source dest # NOT: mv source dest
rm -f file # NOT: rm file
# For recursive operations
rm -rf directory # NOT: rm -r directory
cp -rf source dest # NOT: cp -r source dest
```
**Other commands that may prompt:**
- `scp` - use `-o BatchMode=yes` for non-interactive
- `ssh` - use `-o BatchMode=yes` to fail instead of prompting
- `apt-get` - use `-y` flag
- `brew` - use `HOMEBREW_NO_AUTO_UPDATE=1` env var
<!-- BEGIN BEADS INTEGRATION v:1 profile:minimal hash:ca08a54f -->
## Beads Issue Tracker
This project uses **bd (beads)** for persistent task tracking. Run `bd prime` for full workflow context.
### Why Beads?
- **Prefer Beads over ad-hoc markdown TODO lists** — Beads provides structured, queryable, shareable issue tracking with dependency management
- **Never use `bd edit`** — it opens an interactive editor which blocks agent workflows
- **Use flags and stdin instead** — `bd update <id> --claim`, `bd create --title "..." --estimate 2`
### Slash Commands (Agent Workflow)
| Command | Purpose |
|---------|---------|
| `/beads:ready` | Find unblocked issues |
| `/beads:create` | Create a new issue |
| `/beads:update` | Update an issue (claim, status) |
| `/beads:close` | Close completed work |
| `/beads:stats` | Project-level snapshot |
### Core Workflow (6 Steps)
#### 1. Find Unblocked Work
```bash
bd ready --json
```
Lists issues with no blocking dependencies that are ready to work on.
#### 2. Claim Work
```bash
bd update <id> --claim
```
Atomically assigns the issue to you (sets status to "in-progress").
#### 3. Inspect Details
```bash
bd show <id>
```
View full issue details including:
- Description and acceptance criteria
- Blocking/blocked-by dependencies
- Time estimates
- Status history
#### 4. Create Newly Discovered Work
```bash
# Create a new issue
bd create \
--title "Fix audio on m3-helios" \
--estimate 2 \
--priority high \
--labels nixos,audio
# Link dependencies
bd dep <id> --blocks <blocked-id> # This issue blocks another
bd dep <id> --after <after-id> # This issue after another completes
bd dep <id> --requires <requires-id> # This issue requires another
```
#### 5. Complete Work
```bash
bd close <id> --reason "Added PulseAudio fallback to configuration.nix"
```
Provide a concise summary of what was done. The `--reason` is mandatory.
#### 6. Project Snapshot
```bash
bd status --json # Current state of all issues
bd stats # Metrics: velocity, cycle time, bottlenecks
```
### Example Complete Workflow
```bash
# Start session - find work
bd ready --json
# Claim available issue
bd update 42 --claim
# Do the work...
# Discover something else needed
bd create --title "Document hermes-agent setup" --estimate 1
# Link as related
bd dep 43 --after 42
# Complete original
bd close 42 --reason "Added Hyprland idle timeout config"
# Close related
bd close 43 --reason "Added setup docs to AGENTS.md"
# Push state to remote
bd dolt push
```
### Rules
- Use `bd` for ALL task tracking — do NOT use TodoWrite, TaskCreate, or markdown TODO lists
- Run `bd prime` for detailed command reference and session close protocol
- Use `bd remember` for persistent knowledge — do NOT use MEMORY.md files
## Session Completion
**When ending a work session**, you MUST complete ALL steps below. Work is NOT complete until `git push` succeeds.
**MANDATORY WORKFLOW:**
1. **File issues for remaining work** - Create issues for anything that needs follow-up
2. **Run quality gates** (if code changed) - Tests, linters, builds
3. **Update issue status** - Close finished work, update in-progress items
4. **PUSH TO REMOTE** - This is MANDATORY:
```bash
git pull --rebase
bd dolt push
git push
git status # MUST show "up to date with origin"
```
5. **Clean up** - Clear stashes, prune remote branches
6. **Verify** - All changes committed AND pushed
7. **Hand off** - Provide context for next session
**CRITICAL RULES:**
- Work is NOT complete until `git push` succeeds
- NEVER stop before pushing - that leaves work stranded locally
- NEVER say "ready to push when you are" - YOU must push
- If push fails, resolve and retry until it succeeds
<!-- END BEADS INTEGRATION -->
# Project Agent
**Workspace Path:** `/home/m3tam3re/p/NIX/nixos-config`
_(Note to Pi: Your file write/edit tools run in a different directory by default. You MUST use absolute paths starting with the Workspace Path above for ALL file operations!)_
**Generated:** 2026-04-26
---
## Stack
| Component | Version/Source |
| ---------------- | --------------------------------- |
| **Nixpkgs** | nixos-unstable + 25.05 stable |
| **Home Manager** | github:nix-community/home-manager |
| **m3ta-home** | code.m3ta.dev/m3tam3re/m3ta-home |
| **m3ta-nixpkgs** | code.m3ta.dev/m3tam3re/nixpkgs |
| **Agenix** | github:ryantm/agenix |
| **Disko** | github:nix-community/disko |
| **NUR** | github:nix-community/NUR |
| **Formatter** | alejandra |
| **Linters** | statix, deadnix |
| **IDE** | nixd |
| **Hermes Agent** | NousResearch/hermes-agent |
| **LLM Agents** | numtide/llm-agents.nix |
---
## Structure
```
nixos-config/
├── flake.nix # Entry point: hosts, overlays, dev shells, m3ta-home input
├── coding-rules.json # Opencode rules configuration
├── hosts/ # Per-host NixOS configurations
│ ├── common/ # Shared across all hosts
│ │ ├── users/
│ │ │ └── m3tam3re.nix # ← Central user + m3ta-home integration
│ │ ├── default.nix # Shared NixOS settings, overlays, home-manager setup
│ │ ├── ports.nix # Network ports config
│ │ └── extraServices/ # Common service toggles
│ ├── m3-ares/ # TUXEDO laptop (desktop)
│ │ └── home.nix # Hyprland: eDP-1 + HDMI, XDG/MIME
│ ├── m3-kratos/ # AMD desktop (desktop)
│ │ └── home.nix # Hyprland: dual DP, XDG/MIME
│ ├── m3-daedalus/ # Portable laptop (desktop, no Hyprland)
│ │ └── home.nix # XDG/MIME only
│ ├── m3-atlas/ # Primary server (server + coding)
│ ├── m3-helios/ # AdGuard DNS server (minimal server)
│ ├── m3-hermes/ # Secondary server (minimal server)
│ └── m3-aether/ # Cloud VM (minimal server)
├── modules/ # Reusable NixOS modules
│ └── nixos/ # NixOS-specific modules
├── overlays/ # Package overlays (stable/locked/master/pinned)
│ ├── default.nix
│ └── mods/
├── pkgs/ # Custom packages
├── secrets/ # Encrypted secrets (agenix)
│ └── secrets.nix
├── .opencode-rules/ # Opencode AI rules
│ ├── concerns/
│ ├── languages/nix.md
│ └── USAGE.md
└── .pi/ # Agent configuration
```
### Home-Manager Integration
Home-Manager configs are managed centrally in the **`m3ta-home`** repository:
- **Repo**: `code.m3ta.dev/m3tam3re/m3ta-home`
- **Docs**: See m3ta-home README for full documentation
What lives where:
| Concern | Location | Why |
|---------|----------|-----|
| Shell, CLI tools, editors, apps | `m3ta-home/profiles/` | Portable across all hosts |
| User identity (git, SSH, JJ) | `m3ta-home/users/` | Switchable: private vs work |
| Feature flags (enable/disable) | `nixos-config/hosts/common/users/m3tam3re.nix` | Per-host decisions |
| Monitor layouts, window rules | `nixos-config/hosts/<name>/home.nix` | Hardware-specific |
| XDG/MIME defaults | `nixos-config/hosts/<name>/home.nix` | Host-specific preferences |
| NixOS overlays | `nixos-config/overlays/` | System-level package management |
#### Host → Profile Mapping
Defined in `hosts/common/users/m3tam3re.nix`:
```nix
hostProfiles = {
# Desktop hosts
m3-ares = { context = "desktop"; sets = ["coding" "gaming" "media"]; };
m3-kratos = { context = "desktop"; sets = ["coding" "gaming" "media"]; };
m3-daedalus = { context = "desktop"; sets = ["coding" "media"]; };
# Server hosts
m3-atlas = { context = "server"; sets = ["coding"]; };
m3-helios = { context = "server"; sets = []; };
m3-hermes = { context = "server"; sets = []; };
m3-aether = { context = "server"; sets = []; };
};
```
#### Work Identity Use Case
The same `m3ta-home` repo supports a **work identity** for company machines:
```nix
# On a work NixOS machine:
(m3ta-lib.mkHome {
user = "m3tam3re";
identity = "work"; # ← switches git to sascha.koenig, SSH to AZ hosts
context = "desktop";
sets = ["coding"];
})
```
This provides the familiar shell/editor/CLI setup but with work git credentials and SSH configuration.
---
## Commands
| Action | Command | Notes |
| -------------------- | ---------------------------------------------------------------------- | ------------------------------------------------- |
| **Enter dev shell** | `nix develop` | Includes alejandra, nixd, agenix, statix, deadnix |
| **Build host** | `sudo nixos-rebuild switch --flake .#m3-ares` | Replace hostname as needed |
| **Dry run build** | `sudo nixos-rebuild dry-run --flake .#m3-ares` | Validate without applying |
| **List hosts** | `nix flake show` | Shows all NixOS configurations |
| **Update flake** | `sudo nixos-rebuild switch --flake .#m3-ares --update-input nixpkgs` | Update specific input |
| **Format code** | `alejandra .` | Run before committing |
| **Check lint** | `statix check .` | Run statix for antipatterns |
| **Remove dead code** | `deadnix -w .` | Clean up unused let bindings |
| **Build ISO** | `nix build .#nixosConfigurations.m3-ares.config.system.build.isoImage` | Generate install ISO |
---
## Conventions
### Formatting & Style
- **Formatter:** `alejandra` (mandatory, run before commits)
- **Indentation:** 2 spaces (alejandra default)
- **Variables:** camelCase (e.g., `maxRetryAttempts`)
- **Types/Modules:** PascalCase (e.g., `MyService`)
- **Constants:** UPPER_SNAKE_CASE (e.g., `MAX_RETRIES`)
- **Files:** hyphen-case (e.g., `my-file.nix`)
### Nix Module Patterns
```nix
{ config, lib, pkgs, ... }:
{
options.myService.enable = lib.mkEnableOption "my service";
config = lib.mkIf config.myService.enable {
services.myService.enable = true;
};
}
```
### Conditionals
```nix
config = lib.mkMerge [
(lib.mkIf cfg.enable { ... })
(lib.mkIf cfg.extraConfig { ... })
];
```
### Anti-Patterns (AVOID)
- **Never use `with pkgs;`** — always use explicit package references
- **Never use `builtins.fetchTarball`** — use flake inputs instead
- **Never use `import <nixpkgs>`** — always use inputs
- **Never use `builtins.getAttr/hasAttr`** — use `lib.attrByPath` or `lib.optionalAttrs`
- **Avoid anonymous functions in config** — extract to named lets
### Imports
- Use flake inputs for dependencies (e.g., `inputs.home-manager.nixosModules.home-manager`)
- Import relative paths with `./` or `../`
- Never use absolute paths in imports
### Secrets
- Secrets managed via **agenix** in `secrets/` directory
- Never commit plaintext secrets
- Use `.nix` extension for secret files
### Flake Input URLs
All `code.m3ta.dev` inputs use **SSH** URLs:
```nix
url = "git+ssh://gitea@code.m3ta.dev/m3tam3re/<repo>";
```
Anonymous HTTPS git on Gitea is unreliable and prompts for auth. SSH works with configured keys.
---
## Key Files
| File | Purpose |
| ---------------------------------- | ------------------------------------------------------------------------------------------ |
| `flake.nix` | Central entry point defining all hosts, overlays, packages, dev shells, and nixpkgs config |
| `hosts/common/default.nix` | Shared Nix settings, nixpkgs overlays, home-manager setup (`useGlobalPkgs = true`) |
| `hosts/common/users/m3tam3re.nix` | User definition + m3ta-home mkHome integration + per-host feature flags |
| `hosts/<name>/home.nix` | Host-specific overrides: monitors, workspaces, window rules, XDG/MIME |
| `overlays/default.nix` | Package version overrides (stable/locked/master branches) |
| `.opencode-rules/languages/nix.md` | Nix-specific conventions and patterns |
---
## What to Avoid
1. **Don't modify `flake.lock`** directly — use `nix flake update`
2. **Don't use impure operations** — this is a pure flake-based config
3. **Don't commit without formatting** — always run `alejandra .` first
4. **Don't add packages to hosts directly** — prefer adding to overlays or using NUR
5. **Don't hardcode paths** — use `inputs` and relative imports
6. **Don't create monolithic modules** — keep functions under 20 lines
7. **Don't skip the dry-run** — always test with `--dry-run` before switching
8. **Don't use lib.mkDefault lightly** — understand the precedence implications
---
## Notes
### Adding a New Host
1. Add entry to `flake.nix` → `nixosConfigurations`
2. Create directory in `hosts/` with:
- `default.nix` — imports common + specific configs
- `configuration.nix` — host-specific system config
- `hardware-configuration.nix` — from `nixos-generate-config`
- `programs.nix`, `services/`, `secrets.nix` as needed
3. Add entry to `hostProfiles` in `hosts/common/users/m3tam3re.nix`
4. Add feature flags in the `hostFlags` section
5. Create `hosts/<name>/home.nix` if the host needs monitor/XDG overrides
6. Run `sudo nixos-generate-config --dir ./hosts/new-host` first time
### Adding a New Package
1. For simple packages: add to appropriate overlay in `overlays/default.nix`
2. For complex packages: create in `pkgs/` directory
3. For upstream packages: use NUR or add as flake input
### Adding a New Home-Manager Feature
1. Create the module in `m3ta-home` under the appropriate profile directory
2. Add the import to the parent `default.nix` in m3ta-home
3. Enable it per-host via feature flags in `hosts/common/users/m3tam3re.nix`
### Development Workflow
1. Edit config files
2. Run `alejandra .` to format
3. Run `statix check .` for linting
4. Run `sudo nixos-rebuild dry-run --flake .#m3-ares`
5. If successful: `sudo nixos-rebuild switch --flake .#m3-ares`
### Remote Building
```bash
# Build on remote machine
nix copy --to ssh://user@host .#nixosConfigurations.m3-ares.config.system.build.toplevel
ssh user@host 'sudo nixos-rebuild switch --flake /nix/store/...-closure'
```
File diff suppressed because it is too large Load Diff
+1
View File
@@ -0,0 +1 @@
{"$schema":"https://opencode.ai/config.json","instructions":[".opencode-rules/concerns/coding-style.md",".opencode-rules/concerns/naming.md",".opencode-rules/concerns/documentation.md",".opencode-rules/concerns/testing.md",".opencode-rules/concerns/git-workflow.md",".opencode-rules/concerns/project-structure.md",".opencode-rules/languages/nix.md"]}
Generated
+1194 -119
View File
File diff suppressed because it is too large Load Diff
+88 -15
View File
@@ -15,12 +15,21 @@
url = "github:nix-community/home-manager"; url = "github:nix-community/home-manager";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
}; };
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
nixpkgs-stable.url = "github:nixos/nixpkgs/nixos-24.11"; nixpkgs-stable.url = "github:nixos/nixpkgs/nixos-25.11";
nixpkgs-2744d98.url = "github:nixos/nixpkgs/2744d988fa116fc6d46cdfa3d1c936d0abd7d121"; nixpkgs-45570c2.url = "github:nixos/nixpkgs/45570c299dc2b63c8c574c4cd77f0b92f7e2766e";
nixpkgs-locked.url = "github:nixos/nixpkgs/2744d988fa116fc6d46cdfa3d1c936d0abd7d121"; nixpkgs-locked.url = "github:nixos/nixpkgs/2744d988fa116fc6d46cdfa3d1c936d0abd7d121";
nixpkgs-9e58ed7.url = "github:nixos/nixpkgs/9e58ed7ba759d81c98f033b7f5eba21ca68f53b0";
nixpkgs-master.url = "github:nixos/nixpkgs/master"; nixpkgs-master.url = "github:nixos/nixpkgs/master";
m3ta-nixpkgs.url = "git+ssh://gitea@code.m3ta.dev/m3tam3re/nixpkgs";
llm-agents.url = "github:numtide/llm-agents.nix";
#
nur = {
url = "github:nix-community/NUR";
inputs.nixpkgs.follows = "nixpkgs";
};
agenix.url = "github:ryantm/agenix"; agenix.url = "github:ryantm/agenix";
disko = { disko = {
@@ -28,21 +37,37 @@
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
}; };
hyprpanel.url = "github:Jas-SinghFSU/HyprPanel"; nixos-generators = {url = "github:nix-community/nixos-generators";};
rose-pine-hyprcursor.url = "github:ndom91/rose-pine-hyprcursor";
dotfiles = { rose-pine-hyprcursor.url = "github:ndom91/rose-pine-hyprcursor";
url = "git+https://code.m3tam3re.com/m3tam3re/dotfiles.git"; nix-colors.url = "github:misterio77/nix-colors";
flake = false;
m3ta-home = {
url = "git+ssh://gitea@code.m3ta.dev/m3tam3re/m3ta-home";
# url = "path:/home/m3tam3re/p/NIX/m3ta-home";
inputs.nixpkgs.follows = "nixpkgs";
};
agent-lib = {
url = "git+ssh://gitea@code.m3ta.dev/m3tam3re/agent-lib";
inputs.nixpkgs.follows = "nixpkgs";
};
hermes-agent.url = "github:NousResearch/hermes-agent/v2026.5.29.2";
rustfs = {
url = "github:rustfs/rustfs-flake";
inputs.nixpkgs.follows = "nixpkgs";
}; };
}; };
outputs = { outputs = {
self, self,
agenix, agenix,
dotfiles,
home-manager, home-manager,
nixpkgs, nixpkgs,
m3ta-nixpkgs,
nur,
... ...
} @ inputs: let } @ inputs: let
inherit (self) outputs; inherit (self) outputs;
@@ -54,60 +79,108 @@
"x86_64-darwin" "x86_64-darwin"
]; ];
forAllSystems = nixpkgs.lib.genAttrs systems; forAllSystems = nixpkgs.lib.genAttrs systems;
allOverlays = import ./overlays {inherit inputs outputs;};
in { in {
packages = packages =
forAllSystems (system: import ./pkgs nixpkgs.legacyPackages.${system}); forAllSystems (system: import ./pkgs nixpkgs.legacyPackages.${system});
overlays = import ./overlays {inherit inputs outputs;}; overlays = removeAttrs allOverlays ["mkLlmAgentsOverlay"];
lib.mkLlmAgentsOverlay = allOverlays.mkLlmAgentsOverlay;
homeManagerModules = import ./modules/home-manager; homeManagerModules = import ./modules/home-manager;
nixosConfigurations = { nixosConfigurations = {
m3-ares = nixpkgs.lib.nixosSystem { m3-ares = nixpkgs.lib.nixosSystem {
specialArgs = { specialArgs = {
inherit inputs outputs; inherit inputs outputs;
system = "x86_64-linux";
hostname = "m3-ares"; hostname = "m3-ares";
}; };
modules = [ modules = [
./hosts/m3-ares ./hosts/m3-ares
agenix.nixosModules.default agenix.nixosModules.default
m3ta-nixpkgs.nixosModules.default
inputs.hermes-agent.nixosModules.default
]; ];
}; };
m3-atlas = nixpkgs.lib.nixosSystem { m3-atlas = nixpkgs.lib.nixosSystem {
specialArgs = {inherit inputs outputs;}; specialArgs = {
inherit inputs outputs;
system = "x86_64-linux"; system = "x86_64-linux";
};
modules = [ modules = [
./hosts/m3-atlas ./hosts/m3-atlas
inputs.disko.nixosModules.disko inputs.disko.nixosModules.disko
agenix.nixosModules.default agenix.nixosModules.default
m3ta-nixpkgs.nixosModules.default
inputs.rustfs.nixosModules.rustfs
]; ];
}; };
m3-kratos = nixpkgs.lib.nixosSystem { m3-kratos = nixpkgs.lib.nixosSystem {
specialArgs = { specialArgs = {
inherit inputs outputs; inherit inputs outputs;
system = "x86_64-linux";
hostname = "m3-kratos"; hostname = "m3-kratos";
}; };
modules = [ modules = [
./hosts/m3-kratos ./hosts/m3-kratos
agenix.nixosModules.default agenix.nixosModules.default
nur.modules.nixos.default
m3ta-nixpkgs.nixosModules.default
inputs.hermes-agent.nixosModules.default
]; ];
}; };
m3-helios = nixpkgs.lib.nixosSystem { m3-helios = nixpkgs.lib.nixosSystem {
specialArgs = {inherit inputs outputs;}; specialArgs = {
inherit inputs outputs;
system = "x86_64-linux"; system = "x86_64-linux";
};
modules = [ modules = [
./hosts/m3-helios ./hosts/m3-helios
inputs.disko.nixosModules.disko inputs.disko.nixosModules.disko
agenix.nixosModules.default agenix.nixosModules.default
m3ta-nixpkgs.nixosModules.default
];
};
m3-hermes = nixpkgs.lib.nixosSystem {
specialArgs = {
inherit inputs outputs;
system = "x86_64-linux";
};
modules = [
./hosts/m3-hermes
inputs.disko.nixosModules.disko
agenix.nixosModules.default
m3ta-nixpkgs.nixosModules.default
inputs.hermes-agent.nixosModules.default
]; ];
}; };
}; };
homeConfigurations = { homeConfigurations = {
"m3tam3re@m3-ares" = home-manager.lib.homeManagerConfiguration { "m3tam3re@m3-daedalus" = home-manager.lib.homeManagerConfiguration {
pkgs = nixpkgs.legacyPackages."x86_64-linux"; pkgs = nixpkgs.legacyPackages."x86_64-linux";
extraSpecialArgs = { extraSpecialArgs = {
inherit inputs outputs; inherit inputs outputs;
hostname = "m3-ares"; system = "x86_64-linux";
hostname = "m3-daedalus";
}; };
modules = [./home/m3tam3re/m3tam3re-ares.nix]; modules = [./home/m3tam3re/m3-daedalus.nix];
}; };
}; };
devShells = forAllSystems (system: let
pkgs = import nixpkgs {
inherit system;
config.allowUnfree = true; # Allow unfree packages in devShell
};
in {
default = pkgs.mkShell {
buildInputs = with pkgs; [
alejandra
nixd
openssh
agenix.packages.${system}.default
statix
deadnix
];
};
});
}; };
} }
-48
View File
@@ -1,48 +0,0 @@
{
lib,
outputs,
pkgs,
...
}: {
imports = [
(import
../../modules/home-manager/zellij-ps.nix)
]; #imports = builtins.attrValues outputs.homeManagerModules;
nixpkgs = {
# You can add overlays here
overlays = [
# Add overlays your own flake exports (from overlays and pkgs dir):
outputs.overlays.additions
outputs.overlays.modifications
outputs.overlays.stable-packages
outputs.overlays.locked-packages
outputs.overlays.pinned-packages
outputs.overlays.master-packages
# You can also add overlays exported from other flakes:
# neovim-nightly-overlay.overlays.default
# Or define it inline, for example:
# (final: prev: {
# hi = final.hello.overrideAttrs (oldAttrs: {
# patches = [ ./change-hello-to-hi.patch ];
# });
# })
];
# Configure your nixpkgs instance
config = {
# Disable if you don't want unfree packages
allowUnfree = true;
# Workaround for https://github.com/nix-community/home-manager/issues/2942
allowUnfreePredicate = _: true;
};
};
nix = {
package = lib.mkDefault pkgs.nix;
settings = {
experimental-features = ["nix-command" "flakes"];
warn-dirty = false;
};
};
}
-99
View File
@@ -1,99 +0,0 @@
{pkgs, ...}: {
imports = [
./fish.nix
./skim.nix
./nitch.nix
./nushell.nix
./secrets.nix
./starship.nix
./zellij.nix
];
programs.carapace = {
enable = true;
enableFishIntegration = true;
enableNushellIntegration = true;
enableBashIntegration = true;
};
programs.zoxide = {
enable = true;
enableFishIntegration = true;
enableNushellIntegration = true;
};
programs.neovim = {
enable = true;
defaultEditor = true;
viAlias = true;
vimAlias = true;
vimdiffAlias = true;
withNodeJs = true;
withPython3 = true;
};
programs.bat = {enable = true;};
programs.direnv = {
enable = true;
enableNushellIntegration = true;
nix-direnv.enable =
true;
};
programs.eza = {
enable = true;
enableFishIntegration = true;
enableBashIntegration = true;
extraOptions = ["-l" "--icons" "--git" "-a"];
};
programs.lf = {
enable = true;
settings = {
preview = true;
drawbox = true;
hidden = true;
icons = true;
theme = "Dracula";
previewer = "bat";
};
};
home.packages = with pkgs; [
agenix-cli
alejandra
bc
comma
coreutils
devenv
fd
gcc
go
htop
httpie
jq
just
lazygit
llm
lf
nix-index
nushellPlugins.skim
progress
ripgrep
rocmPackages.rocm-smi
rocmPackages.rocminfo
rocmPackages.rocm-runtime
tldr
pomodoro-timer
trash-cli
unimatrix
unzip
vulkan-tools
wttrbar
wireguard-tools
yazi
zellij-ps
zip
];
}
-77
View File
@@ -1,77 +0,0 @@
{
config,
lib,
...
}:
with lib; let
cfg = config.features.cli.fish;
in {
options.features.cli.fish.enable = mkEnableOption "enable fish shell";
config = mkIf cfg.enable {
programs.fish = {
enable = true;
loginShellInit = ''
set -x NIX_PATH nixpkgs=channel:nixos-unstable
set -x NIX_LOG info
set -x WEBKIT_DISABLE_COMPOSITING_MODE 1
set -x TERMINAL kitty
set -x EDITOR nvim
set -x VISUAL zed
set -x XDG_DATA_HOME $HOME/.local/share
set -x FZF_CTRL_R_OPTS "
--preview='bat --color=always -n {}'
--preview-window up:3:hidden:wrap
--bind 'ctrl-/:toggle-preview'
--bind 'ctrl-y:execute-silent(echo -n {2..} | wl-copy)+abort'
--color header:bold
--header 'Press CTRL-Y to copy command into clipboard'"
set -x FZF_DEFAULT_COMMAND fd --type f --exclude .git --follow --hidden
set -x FZF_CTRL_T_COMMAND "$FZF_DEFAULT_COMMAND"
set -x FLAKE $HOME/p/nixos/nixos-config
source /run/agenix/${config.home.username}-secrets
if test (tty) = "/dev/tty1"
exec uwsm start -S -F /run/current-system/sw/bin/Hyprland
end
if test (tty) = "/dev/tty2"
exec gamescope -O HDMI-A-1 -W 1920 -H 1080 --adaptive-sync --hdr-enabled --rt --steam -- steam -pipewire-dmabuf -tenfoot
end
'';
shellAbbrs = {
".." = "cd ..";
"..." = "cd ../..";
b = "yazi";
ls = "eza";
l = "eza -l --icons --git -a";
lt = "eza --tree --level=2 --long --icons --git";
grep = "rg";
ps = "procs";
just = "just --unstable";
fs = "du -ah . | sort -hr | head -n 10";
n = "nix";
nd = "nix develop -c $SHELL";
ns = "nix shell";
nsn = "nix shell nixpkgs#";
nb = "nix build";
nbn = "nix build nixpkgs#";
nf = "nix flake";
nr = "sudo nixos-rebuild --flake .";
nrs = "sudo nixos-rebuild switch --flake .#(uname -n)";
snr = "sudo nixos-rebuild --flake .";
snrs = "sudo nixos-rebuild --flake . switch";
hm = "home-manager --flake .";
hms = "home-manager --flake . switch";
hmr = "cd ~/projects/nix-configurations; nix flake lock --update-input dotfiles; home-manager --flake .#(whoami)@(hostname) switch";
tsu = "sudo tailscale up";
tsd = "sudo tailscale down";
vi = "nvim";
vim = "nvim";
};
};
};
}
-15
View File
@@ -1,15 +0,0 @@
{
config,
lib,
pkgs,
...
}:
with lib; let
cfg = config.features.cli.nitch;
in {
options.features.cli.nitch.enable = mkEnableOption "enable nitch";
config = mkIf cfg.enable {
home.packages = with pkgs; [nitch];
};
}
-154
View File
@@ -1,154 +0,0 @@
{
config,
pkgs,
lib,
...
}:
with lib; let
cfg = config.features.cli.nushell;
in {
options.features.cli.nushell.enable = mkEnableOption "enable nushell";
config = mkIf cfg.enable {
programs.nushell = {
enable = true;
plugins = with pkgs.nushellPlugins; [
skim
];
envFile.text = ''
$env.config.show_banner = false
$env.NIX_PATH = "nixpkgs=channel:nixos-unstable"
$env.NIX_LOG = "iunfo"
$env.WEBKIT_DISABLE_COMPOSITING_MODE = "1"
$env.TERMINAL = "kitty"
$env.EDITOR = "nvim"
$env.VISUAL = "zed"
$env.XDG_DATA_HOME = $"($env.HOME)/.local/share"
$env.FZF_DEFAULT_COMMAND = "fd --type f --exclude .git --follow --hidden"
$env.FZF_DEFAULT_OPTS = "
--preview='bat --color=always -n {}'
--preview-window up:3:hidden:wrap
--bind 'ctrl-/:toggle-preview'
--bind 'ctrl-y:execute-silent(echo -n {2..} | wl-copy)+abort'
--color header:bold
--header 'Press CTRL-Y to copy command into clipboard'"
$env.FLAKE = $"($env.HOME)/p/nixos/nixos-config"
'';
configFile.text = ''
if (tty) == "/dev/tty1" {
exec uwsm start -S -F /run/current-system/sw/bin/Hyprland
}
if (tty) == "/dev/tty2" {
exec gamescope -O HDMI-A-1 -W 1920 -H 1080 --adaptive-sync --hdr-enabled --rt --steam -- steam -pipewire-dmabuf -tenfoot
}
alias .. = cd ..
alias ... = cd ...
alias h = cd $env.HOME
alias b = yazi
alias lt = eza --tree --level=2 --long --icons --git
alias grep = rg
alias just = just --unstable
alias n = nix
alias nd = nix develop -c $nu.current-shell
alias ns = nix shell
alias nsn = nix shell nixpkgs#
alias nb = nix build
alias nbn = nix build nixpkgs#
alias nf = nix flake
alias nr = sudo nixos-rebuild --flake .
alias nrs = sudo nixos-rebuild switch --flake .#(sys host | get hostname)
alias snr = sudo nixos-rebuild --flake .
alias snrs = sudo nixos-rebuild --flake . switch
alias hm = home-manager --flake .
alias hms = home-manager --flake . switch
alias hmr = do { cd ~/projects/nix-configurations; nix flake lock --update-input dotfiles; home-manager --flake .#(whoami)@(hostname) switch }
alias tsu = sudo tailscale up
alias tsd = sudo tailscale down
alias vi = nvim
alias vim = nvim
def history_fuzzy [] {
let selected = (
history
| uniq
| get command
| sk --height 40% --layout=reverse --color=fg:#f8f8f2,bg:#282a36,current_bg:#ff79c6,current_fg:#bd93f9,info:#ffb86c,marker:#6272a4,pointer:#50fa7b,spinner:#50fa7b
)
if ($selected | is-not-empty) {
^nu -c ($selected)
} else {
null
}
}
def --env dir_fuzzy [] {
let selected = (
fd --type directory
| ^sk --preview 'eza --tree --no-permissions --no-filesize --no-user --no-time --only-dirs {}' --height 40% --layout=reverse --color=fg:#f8f8f2,bg:#282a36,current_bg:#ff79c6,current_fg:#bd93f9,info:#ffb86c,marker:#6272a4,pointer:#50fa7b,spinner:#50fa7b
)
cd $selected
}
def find_fuzzy [] {
# Find non-hidden text files with matches for any content and select one via fuzzy search
let selected = (
^fd --type file --no-hidden -X rg -l --files-with-matches .
| lines
| sk --format { $in }
--height 40%
--layout=reverse
--preview { open $in | bat --color=always --line-range :50 }
--color=fg:#f8f8f2,bg:#282a36,current_bg:#ff79c6,current_fg:#bd93f9,info:#ffb86c,marker:#6272a4,pointer:#50fa7b,spinner:##50fa7b
)
if ($selected | is-not-empty) {
^$env.EDITOR $selected
}
}
$env.config = {
keybindings: [
{
name: history_fuzzy
modifier: control
keycode: char_r
mode: [emacs, vi_insert, vi_normal]
event: [
{
send: executehostcommand
cmd: "history_fuzzy"
}
]
}
{
name: dir_fuzzy
modifier: alt
keycode: char_c
mode: [emacs, vi_insert, vi_normal]
event: [
{
send: executehostcommand
cmd: "dir_fuzzy"
}
]
}
{
name: history_fuzzy
modifier: control
keycode: char_t
mode: [emacs, vi_insert, vi_normal]
event: [
{
send: executehostcommand
cmd: "find_fuzzy"
}
]
}
]
}
'';
};
};
}
-21
View File
@@ -1,21 +0,0 @@
{
config,
lib,
pkgs,
...
}:
with lib; let
cfg = config.features.cli.secrets;
in {
options.features.cli.secrets.enable = mkEnableOption "enable secrets";
config = mkIf cfg.enable {
programs.password-store = {
enable = true;
package =
pkgs.pass-wayland.withExtensions
(exts: [exts.pass-otp exts.pass-import]);
};
home.packages = with pkgs; [pinentry];
};
}
-23
View File
@@ -1,23 +0,0 @@
{
config,
lib,
...
}:
with lib; let
cfg = config.features.cli.skim;
in {
options.features.cli.skim.enable = mkEnableOption "enable skim fuzzy finder";
config = mkIf cfg.enable {
programs.skim = {
enable = true;
enableFishIntegration = true;
defaultOptions = [
"--preview='bat --color=always -n {}'"
"--bind 'ctrl-/:toggle-preview'"
];
defaultCommand = "fd --type f --exclude .git --follow --hidden";
changeDirWidgetCommand = "fd --type d --exclude .git --follow --hidden";
};
};
}
-18
View File
@@ -1,18 +0,0 @@
{
config,
lib,
...
}:
with lib; let
cfg = config.features.cli.starship;
in {
options.features.cli.starship.enable = mkEnableOption "enable starship prompt";
config = mkIf cfg.enable {
programs.starship = {
enable = true;
enableFishIntegration = true;
enableNushellIntegration = true;
};
};
}
-16
View File
@@ -1,16 +0,0 @@
{
config,
lib,
...
}:
with lib; let
cfg = config.features.cli.zellij;
in {
options.features.cli.zellij.enable = mkEnableOption "enable tmux";
config = mkIf cfg.enable {
programs.zellij = {
enable = true;
};
};
}
-21
View File
@@ -1,21 +0,0 @@
{pkgs, ...}: {
home.packages = with pkgs; [
devpod
devpod-desktop
code2prompt
(python3.withPackages (ps:
with ps; [
pip
# Scientific packages
numba
numpy
openai-whisper
torch
srt
]))
nixd
alejandra
tailwindcss
tailwindcss-language-server
];
}
-105
View File
@@ -1,105 +0,0 @@
{
config,
lib,
pkgs,
...
}:
with lib; let
cfg = config.features.desktop.office;
in {
options.features.desktop.coding.enable =
mkEnableOption "install coding related stuff";
config = mkIf cfg.enable {
home.packages = with pkgs; [
bruno
insomnia
];
programs.zed-editor = {
enable = true;
userSettings = {
features = {
inline_prediction_provider = "zed";
edit_prediction_provider = "zed";
copilot = false;
};
telemetry = {
metrics = false;
};
lsp = {
rust_analyzer = {
binary = {path_lookup = true;};
};
};
languages = {
Nix = {
language_servers = ["nixd"];
formatter = {
external = {
command = "alejandra";
arguments = ["-q" "-"];
};
};
};
Python = {
language_servers = ["pyright"];
formatter = {
external = {
command = "black";
arguments = ["-"];
};
};
};
};
assistant = {
version = "2";
default_model = {
provider = "zed.dev";
model = "claude-3-5-sonnet-latest";
};
};
language_models = {
anthropic = {
version = "1";
api_url = "https://api.anthropic.com";
};
openai = {
version = "1";
api_url = "https://api.openai.com/v1";
};
ollama = {
api_url = "http://localhost:11434";
};
};
ssh_connections = [
{
host = "152.53.85.162";
nickname = "m3-atlas";
args = ["-i" "~/.ssh/m3tam3re"];
}
{
host = "95.217.189.186";
port = 2222;
nickname = "self-host-playbook";
args = ["-i" "~/.ssh/self-host-playbook"];
"projects" = [
{
paths = ["/etc/nixos/current-systemconfig"];
}
];
}
];
auto_update = false;
format_on_save = "on";
vim_mode = true;
load_direnv = "shell_hook";
theme = "Dracula";
buffer_font_family = "FiraCode Nerd Font";
ui_font_size = 16;
buffer_font_size = 16;
show_edit_predictions = true;
};
};
};
}
-15
View File
@@ -1,15 +0,0 @@
{
config,
lib,
pkgs,
...
}:
with lib; let
cfg = config.features.desktop.crypto;
in {
options.features.desktop.crypto.enable = mkEnableOption "Enable Crypto";
config = mkIf cfg.enable {
home.packages = with pkgs; [bisq2 monero-gui trezor-suite];
};
}
-135
View File
@@ -1,135 +0,0 @@
{pkgs, ...}: {
imports = [
./coding.nix
./crypto.nix
./fonts.nix
./gaming.nix
./hyprland.nix
./media.nix
./office.nix
./rofi.nix
./theme.nix
./wayland.nix
./wofi.nix
];
xdg = {
enable = true;
configFile."mimeapps.list".force = true;
mimeApps = {
enable = true;
associations.added = {
"application/zip" = ["org.gnome.FileRoller.desktop"];
"application/csv" = ["calc.desktop"];
"application/pdf" = ["okularApplication_pdf.desktop"];
};
defaultApplications = {
"application/zip" = ["org.gnome.FileRoller.desktop"];
"application/csv" = ["calc.desktop"];
"application/pdf" = ["okularApplication_pdf.desktop"];
"application/md" = ["nvim.desktop"];
"application/text" = ["nvim.desktop"];
"x-scheme-handler/http" = ["io.github.zen_browser.zen"];
"x-scheme-handler/https" = ["io.github.zen_browser.zen"];
};
};
userDirs = {
enable = true;
createDirectories = true;
};
};
home.sessionVariables = {
WEBKIT_DISABLE_COMPOSITING_MODE = "1";
NIXOS_OZONE_WL = "1";
TERMINAL = "kitty";
QT_QPA_PLATFORM = "wayland";
XDG_CURRENT_DESKTOP = "Hyprland";
XDG_SESSION_TYPE = "wayland";
XDG_SESSION_DESKTOP = "Hyprland";
};
home.sessionPath = ["\${XDG_BIN_HOME}" "\${HOME}/.cargo/bin" "$HOME/.npm-global/bin"];
fonts.fontconfig.enable = true;
services.mako = {
enable = true;
backgroundColor = "#282a36";
textColor = "#80FFEA";
borderColor = "#9742b5";
width = 400;
height = 150;
padding = "10,20";
borderRadius = 8;
borderSize = 1;
margin = "20,20";
};
programs.kitty = {
enable = true;
shellIntegration = {
enableFishIntegration = true;
enableBashIntegration = true;
};
font = {name = "Fira Code";};
themeFile = "Dracula";
settings = {copy_on_select = "yes";};
};
home.pointerCursor = {
gtk.enable = true;
package = pkgs.bibata-cursors;
name = "Bibata-Modern-Ice";
size = 20;
};
home.packages = with pkgs; [
appimage-run
anytype
# blueberry
brave
# brightnessctl
# clipman
distrobox
# eww
# firefox-devedition
file-roller
hyprpanel
seahorse
sushi
# glib
# google-chrome
# gsettings-desktop-schemas
# graphviz
# ksnip
msty
msty-sidecar
nwg-look
# pamixer
# pavucontrol
# libsForQt5.qtstyleplugins
# stable.nyxt
# pcmanfm
rose-pine-hyprcursor
# qt5ct
# qt6.qtwayland
#rustdesk
# socat
# unrar
# unzip
# usbutils
# v4l-utils
remmina
slack
telegram-desktop
vivaldi
vivaldi-ffmpeg-codecs
warp-terminal
# wl-clipboard
# wlogout
# wtype
# xdg-utils
# ydotool
# zip
];
}
-24
View File
@@ -1,24 +0,0 @@
{
config,
lib,
pkgs,
...
}:
with lib; let
cfg = config.features.desktop.fonts;
in {
options.features.desktop.fonts.enable =
mkEnableOption "install additional fonts for desktop apps";
config = mkIf cfg.enable {
home.packages = with pkgs; [
fira-code
fira-code-symbols
nerd-fonts.fira-code
nerd-fonts.jetbrains-mono
font-manager
font-awesome_5
noto-fonts
];
};
}
-21
View File
@@ -1,21 +0,0 @@
{
config,
lib,
pkgs,
...
}:
with lib; let
cfg = config.features.desktop.gaming;
in {
options.features.desktop.gaming.enable =
mkEnableOption "install gaming related stuff";
config = mkIf cfg.enable {
home.packages = with pkgs; [
gamescope
goverlay
mangohud
protonup-ng
];
};
}
-186
View File
@@ -1,186 +0,0 @@
{
wayland.windowManager.hyprland = {
settings = {
xwayland = {
force_zero_scaling = true;
};
exec-once = [
"hyprpanel"
"hyprpaper"
"hypridle"
"wl-paste --type text --watch cliphist store" # Stores only text data
"wl-paste --type image --watch cliphist store" # Stores only image data "wl-paste -p -t text --watch clipman store -P --histpath=\"~/.local/share/clipman-primary.json\""
];
env = [
"XCURSOR_SIZE,32"
"HYPRCURSOR_THEME,Bibata-Modern-Ice"
"WLR_NO_HARDWARE_CURSORS,1"
"GTK_THEME,Dracula"
];
input = {
kb_layout = "de,us";
kb_variant = "";
kb_model = "";
kb_rules = "";
kb_options = "ctrl:nocaps";
follow_mouse = 1;
};
general = {
gaps_in = 5;
gaps_out = 5;
border_size = 1;
"col.active_border" = "rgba(9742b5ee) rgba(9742b5ee) 45deg";
"col.inactive_border" = "rgba(595959aa)";
layout = "dwindle";
};
decoration = {
shadow = {
enabled = true;
range = 60;
render_power = 3;
color = "rgba(1E202966)";
offset = "1 2";
scale = 0.97;
};
rounding = 8;
blur = {
enabled = true;
size = 3;
passes = 3;
};
active_opacity = 0.9;
inactive_opacity = 0.5;
};
animations = {
enabled = true;
bezier = "myBezier, 0.05, 0.9, 0.1, 1.05";
animation = [
"windows, 1, 7, myBezier"
"windowsOut, 1, 7, default, popin 80%"
"border, 1, 10, default"
"borderangle, 1, 8, default"
"fade, 1, 7, default"
"workspaces, 1, 6, default"
];
};
dwindle = {
pseudotile = true;
preserve_split = true;
};
master = {
new_status = "master";
};
gestures = {
workspace_swipe = false;
};
device = [
{
name = "epic-mouse-v1";
sensitivity = -0.5;
}
{
name = "zsa-technology-labs-moonlander-mark-i";
kb_layout = "us";
}
{
name = "keychron-keychron-k7";
kb_layout = "us";
}
];
windowrule = [
"float, class:file_progress"
"float, class:confirm"
"float, class:dialog"
"float, class:download"
"float, class:notification"
"float, class:error"
"float, class:splash"
"float, class:confirmreset"
"float, title:Open File"
"float, title:branchdialog"
"float, class:pavucontrol-qt"
"float, class:pavucontrol"
"fullscreen, class:wlogout"
"float, title:wlogout"
"fullscreen, title:wlogout"
"float, class:mpv"
"idleinhibit focus, class:mpv"
"opacity 1.0 override, class:mpv"
"float, title:^(Media viewer)$"
"float, title:^(Volume Control)$"
"float, title:^(Picture-in-Picture)$"
"float,title:^(floating-pomodoro)$"
"size 250 50, title:^(floating-pomodoro)$"
"move 12 100%-150,title:^(floating-pomodoro)$"
"pin,title:^(floating-pomodoro)$"
];
"$mainMod" = "SUPER";
bind = [
"$mainMod, return, exec, nu -c zellij-ps"
# "$mainMod, t, exec, warp-terminal"
"$mainMod, t, exec, kitty -e nu -c 'nitch; exec nu'"
"$mainMod SHIFT, t, exec, launch-timer"
"$mainMod SHIFT, e, exec, kitty -e zellij_nvim"
"$mainMod, o, exec, hyprctl setprop activewindow opaque toggle"
"$mainMod, b, exec, thunar"
"$mainMod SHIFT, B, exec, vivaldi"
"$mainMod, Escape, exec, wlogout -p layer-shell"
"$mainMod, Space, togglefloating"
"$mainMod, q, killactive"
"$mainMod, M, exit"
"$mainMod, F, fullscreen"
"$mainMod SHIFT, V, togglefloating"
"$mainMod, D, exec, rofi -show"
"$mainMod, V, exec, cliphist list | rofi -dmenu | cliphist decode | wl-copy"
"$mainMod SHIFT, S, exec, bemoji"
"$mainMod, P, exec, rofi-pass"
"$mainMod SHIFT, P, pseudo"
"$mainMod, J, togglesplit"
"$mainMod, h, movefocus, l"
"$mainMod, l, movefocus, r"
"$mainMod, k, movefocus, u"
"$mainMod, j, movefocus, d"
"$mainMod, 1, workspace, 1"
"$mainMod, 2, workspace, 2"
"$mainMod, 3, workspace, 3"
"$mainMod, 4, workspace, 4"
"$mainMod, 5, workspace, 5"
"$mainMod, 6, workspace, 6"
"$mainMod, 7, workspace, 7"
"$mainMod, 8, workspace, 8"
"$mainMod, 9, workspace, 9"
"$mainMod, 0, workspace, 10"
"$mainMod SHIFT, 1, movetoworkspace, 1"
"$mainMod SHIFT, 2, movetoworkspace, 2"
"$mainMod SHIFT, 3, movetoworkspace, 3"
"$mainMod SHIFT, 4, movetoworkspace, 4"
"$mainMod SHIFT, 5, movetoworkspace, 5"
"$mainMod SHIFT, 6, movetoworkspace, 6"
"$mainMod SHIFT, 7, movetoworkspace, 7"
"$mainMod SHIFT, 8, movetoworkspace, 8"
"$mainMod SHIFT, 9, movetoworkspace, 9"
"$mainMod SHIFT, 0, movetoworkspace, 10"
"$mainMod, mouse_down, workspace, e+1"
"$mainMod, mouse_up, workspace, e-1"
];
bindm = [
"$mainMod, mouse:272, movewindow"
"$mainMod, mouse:273, resizewindow"
];
};
};
}
-57
View File
@@ -1,57 +0,0 @@
{
config,
lib,
pkgs,
...
}:
with lib; let
cfg = config.features.desktop.media;
in {
options.features.desktop.media.enable =
mkEnableOption "enable media features";
config = mkIf cfg.enable {
home.packages = with pkgs; [
# handbrake
# kdePackages.kdenlive
# makemkv
# mediainfo
amf
blueberry
ffmpeg_6-full
gst_all_1.gstreamer
gst_all_1.gst-vaapi
handbrake
inkscape
kdePackages.kdenlive
krita
libation
#makemkv
pamixer
pavucontrol
qpwgraph
v4l-utils
plexamp
webcord
# uxplay
# vlc
# webcord
# yt-dlp
unimatrix
];
programs = {
mpv = {
enable = true;
bindings = {
WHEEL_UP = "seek 10";
WHEEL_DOWN = "seek -10";
};
config = {
profile = "gpu-hq";
ytdl-format = "bestvideo+bestaudio";
};
};
};
};
}
-18
View File
@@ -1,18 +0,0 @@
{
config,
lib,
pkgs,
...
}:
with lib; let
cfg = config.features.desktop.office;
in {
options.features.desktop.office.enable =
mkEnableOption "install office and paperwork stuff";
config = mkIf cfg.enable {
home.packages = with pkgs; [
libreoffice-fresh
];
};
}
-38
View File
@@ -1,38 +0,0 @@
{
config,
pkgs,
lib,
...
}:
with lib; let
cfg = config.features.desktop.rofi;
in {
options.features.desktop.rofi.enable = mkEnableOption "enable rofi";
config = mkIf cfg.enable {
programs.rofi = with pkgs; {
enable = true;
package = rofi.override {
plugins = [
rofi-calc
rofi-emoji
stable.rofi-file-browser
];
};
pass = {
enable = true;
package = rofi-pass-wayland;
};
terminal = "\${pkgs.kitty}/bin/kitty";
font = "Fira Code";
extraConfig = {
show-icons = true;
disable-history = false;
modi = "drun,calc,emoji,filebrowser";
kb-primary-paste = "Control+V,Shift+Insert";
kb-secondary-paste = "Control+v,Insert";
};
theme = "dracula";
};
};
}
-17
View File
@@ -1,17 +0,0 @@
{pkgs, ...}: {
qt = {
enable = true;
platformTheme.name = "gtk";
};
gtk = {
enable = true;
theme = {
name = "Dracula";
package = pkgs.dracula-theme;
};
iconTheme = {
name = "Dracula";
package = pkgs.dracula-icon-theme;
};
};
}
-285
View File
@@ -1,285 +0,0 @@
{
config,
lib,
pkgs,
...
}:
with lib; let
cfg = config.features.desktop.wayland;
in {
options.features.desktop.wayland.enable = mkEnableOption "wayland extra tools and config";
config = mkIf cfg.enable {
programs.waybar = {
enable = true;
style = ''
@define-color background-darker rgba(30, 31, 41, 230);
@define-color background #282a36;
@define-color selection #44475a;
@define-color foreground #f8f8f2;
@define-color comment #6272a4;
@define-color cyan #8be9fd;
@define-color green #50fa7b;
@define-color orange #ffb86c;
@define-color pink #ff79c6;
@define-color purple #bd93f9;
@define-color red #ff5555;
@define-color yellow #f1fa8c;
* {
border: none;
border-radius: 0;
font-family: FiraCode Nerd Font;
font-weight: bold;
font-size: 14px;
min-height: 0;
}
window#waybar {
background: rgba(21, 18, 27, 0);
color: #cdd6f4;
}
tooltip {
background: #1e1e2e;
border-radius: 10px;
border-width: 2px;
border-style: solid;
border-color: #11111b;
}
#workspaces button {
padding: 5px;
color: #313244;
margin-right: 5px;
}
#workspaces button.active {
color: #11111b;
background: #a6e3a1;
border-radius: 10px;
}
#workspaces button.focused {
color: #a6adc8;
background: #eba0ac;
border-radius: 10px;
}
#workspaces button.urgent {
color: #11111b;
background: #a6e3a1;
border-radius: 10px;
}
#workspaces button:hover {
background: #11111b;
color: #cdd6f4;
border-radius: 10px;
}
#custom-language,
#custom-updates,
#custom-caffeine,
#custom-weather,
#window,
#clock,
#battery,
#pulseaudio,
#network,
#workspaces,
#tray,
#backlight {
background: #1e1e2e;
padding: 0px 10px;
margin: 3px 0px;
margin-top: 10px;
border: 1px solid #181825;
}
#tray {
border-radius: 10px;
margin-right: 10px;
}
#workspaces {
background: #1e1e2e;
border-radius: 10px;
margin-left: 10px;
padding-right: 0px;
padding-left: 5px;
}
#custom-caffeine {
color: #89dceb;
border-radius: 10px 0px 0px 10px;
border-right: 0px;
margin-left: 10px;
}
#custom-language {
color: #f38ba8;
border-left: 0px;
border-right: 0px;
}
#custom-updates {
color: #f5c2e7;
border-left: 0px;
border-right: 0px;
}
#window {
border-radius: 10px;
margin-left: 60px;
margin-right: 60px;
}
#clock {
color: #fab387;
border-radius: 10px 0px 0px 10px;
margin-left: 0px;
border-right: 0px;
}
#network {
color: #f9e2af;
border-left: 0px;
border-right: 0px;
}
#pulseaudio {
color: #89b4fa;
border-left: 0px;
border-right: 0px;
}
#pulseaudio.microphone {
color: #cba6f7;
border-left: 0px;
border-right: 0px;
}
#battery {
color: #a6e3a1;
border-radius: 0 10px 10px 0;
margin-right: 10px;
border-left: 0px;
}
#custom-weather {
border-radius: 0px 10px 10px 0px;
border-right: 0px;
margin-left: 0px;
}
'';
settings = {
mainbar = {
layer = "top";
position = "top";
mod = "dock";
exclusive = true;
passthrough = false;
gtk-layer-shell = true;
height = 0;
modules-left = ["clock" "custom/weather" "hyprland/workspaces"];
modules-center = ["hyprland/window"];
modules-right = [
"tray"
"custom/language"
"battery"
"backlight"
"pulseaudio"
"pulseaudio#microphone"
];
"hyprland/window" = {
format = "👉 {}";
seperate-outputs = true;
};
"hyprland/workspaces" = {
disable-scroll = true;
all-outputs = true;
on-click = "activate";
format = " {name} {icon} ";
on-scroll-up = "hyprctl dispatch workspace e+1";
on-scroll-down = "hyprctl dispatch workspace e-1";
format-icons = {
"1" = "";
"2" = "";
"3" = "";
"4" = "";
"5" = "";
"6" = "";
"7" = "";
};
persistent_workspaces = {
"1" = [];
"2" = [];
"3" = [];
"4" = [];
};
};
"custom/weather" = {
format = "{}°C";
tooltip = true;
interval = 3600;
exec = "wttrbar --location Pockau-Lengefeld";
return-type = "json";
};
tray = {
icon-size = 13;
spacing = 10;
};
clock = {
format = " {:%R %d/%m}";
tooltip-format = "<big>{:%Y %B}</big>\n<tt><small>{calendar}</small></tt>";
};
pulseaudio = {
format = "{icon} {volume}%";
tooltip = false;
format-muted = " Muted";
on-click = "pamixer -t";
on-scroll-up = "pamixer -i 5";
on-scroll-down = "pamixer -d 5";
scroll-step = 5;
format-icons = {
headphone = "";
hands-free = "";
headset = "";
phone = "";
portable = "";
car = "";
default = ["" "" ""];
};
};
"pulseaudio#microphone" = {
format = "{format_source}";
format-source = " {volume}%";
format-source-muted = " Muted";
on-click = "pamixer --default-source -t";
on-scroll-up = "pamixer --default-source -i 5";
on-scroll-down = "pamixer --default-source -d 5";
scroll-step = 5;
};
};
};
};
home.packages = with pkgs; [
grim
hyprcursor
hyprlock
hyprpaper
qt6.qtwayland
slurp
waypipe
pinned.wl-clipboard
wf-recorder
wl-mirror
wlogout
wtype
ydotool
];
};
}
-6
View File
@@ -1,6 +0,0 @@
{pkgs, ...}: {
home.packages = with pkgs; [
wofi
bemoji
];
}
-26
View File
@@ -1,26 +0,0 @@
{
pkgs,
inputs,
...
}: {
home.file.".config/bat" = {
source = "${inputs.dotfiles}/bat";
recursive = true;
};
home.file.".config/nyxt" = {
source = "${inputs.dotfiles}/nyxt";
recursive = true;
};
# home.file.".config/hypr" = {
# source = "${inputs.dotfiles}/hypr";
# recursive = true;
# };
home.file.".config/nvim" = {
source = "${inputs.dotfiles}/nvim";
recursive = true;
};
home.file.".config/zellij" = {
source = "${inputs.dotfiles}/zellij";
recursive = true;
};
}
-115
View File
@@ -1,115 +0,0 @@
# This is a default home.nix generated by the follwing hone-manager command
#
# home-manager init ./
{
config,
lib,
pkgs,
...
}: {
# Home Manager needs a bit of information about you and the paths it should
# manage.
home.username = lib.mkDefault "your-name";
home.homeDirectory = lib.mkDefault "/home/${config.home.username}";
# This value determines the Home Manager release that your configuration is
# compatible with. This helps avoid breakage when a new Home Manager release
# introduces backwards incompatible changes.
#
# You should not change this value, even if you update Home Manager. If you do
# want to update the value, then make sure to first check the Home Manager
# release notes.
home.stateVersion = "24.11"; # Please read the comment before changing.
# The home.packages option allows you to install Nix packages into your
# environment.
home.packages = with pkgs; [
# # Adds the 'hello' command to your environment. It prints a friendly
# # "Hello, world!" when run.
# pkgs.hello
# # It is sometimes useful to fine-tune packages, for example, by applying
# # overrides. You can do that directly here, just don't forget the
# # parentheses. Maybe you want to install Nerd Fonts with a limited number of
# # fonts?
# (pkgs.nerdfonts.override { fonts = [ "FantasqueSansMono" ]; })
# # You can also create simple shell scripts directly inside your
# # configuration. For example, this adds a command 'my-hello' to your
# # environment:
# (pkgs.writeShellScriptBin "my-hello" ''
# echo "Hello, ${config.home.username}!"
# '')
];
# Home Manager is pretty good at managing dotfiles. The primary way to manage
# plain files is through 'home.file'.
home.file = {
# # Building this configuration will create a copy of 'dotfiles/screenrc' in
# # the Nix store. Activating the configuration will then make '~/.screenrc' a
# # symlink to the Nix store copy.
# ".screenrc".source = dotfiles/screenrc;
# # You can also set the file content immediately.
# ".gradle/gradle.properties".text = ''
# org.gradle.console=verbose
# org.gradle.daemon.idletimeout=3600000
# '';
};
# Home Manager can also manage your environment variables through
# 'home.sessionVariables'. If you don't want to manage your shell through Home
# Manager then you have to manually source 'hm-session-vars.sh' located at
# either
#
# ~/.nix-profile/etc/profile.d/hm-session-vars.sh
#
# or
#
# ~/.local/state/nix/profiles/profile/etc/profile.d/hm-session-vars.sh
#
# or
#
# /etc/profiles/per-user/m3tam3re/etc/profile.d/hm-session-vars.sh
#
home.sessionVariables = {
# EDITOR = "emacs";
};
# Let Home Manager install and manage itself.
programs.home-manager.enable = true;
programs.git = {
enable = true;
userName = "m3tam3re";
userEmail = "m@m3tam3re.com";
aliases = {st = "status";};
extraConfig = {
core.excludesfile = "~/.gitignore_global";
init.defaultBranch = "master";
};
};
programs.zellij-ps = {
enable = true;
projectFolders = [
"${config.home.homeDirectory}/p/c"
"${config.home.homeDirectory}/p"
"${config.home.homeDirectory}/.config"
];
layout = ''
layout {
pane size=1 borderless=true {
plugin location="zellij:tab-bar"
}
pane size="70%" command="nvim"
pane split_direction="vertical" {
pane
pane command="unimatrix"
}
pane size=1 borderless=true {
plugin location="zellij:status-bar"
}
}
'';
};
}
-230
View File
@@ -1,230 +0,0 @@
# This is a default home.nix generated by the follwing hone-manager command
#
# home-manager init ./
{
config,
lib,
pkgs,
...
}: {
# Home Manager needs a bit of information about you and the paths it should
# manage.
home.username = lib.mkDefault "your-name";
home.homeDirectory = lib.mkDefault "/home/${config.home.username}";
# This value determines the Home Manager release that your configuration is
# compatible with. This helps avoid breakage when a new Home Manager release
# introduces backwards incompatible changes.
#
# You should not change this value, even if you update Home Manager. If you do
# want to update the value, then make sure to first check the Home Manager
# release notes.
home.stateVersion = "24.11"; # Please read the comment before changing.
# The home.packages option allows you to install Nix packages into your
# environment.
home.packages = with pkgs; [
aider-chat-env
libgtop
# # Adds the 'hello' command to your environment. It prints a friendly
# # "Hello, world!" when run.
# pkgs.hello
# # It is sometimes useful to fine-tune packages, for example, by applying
# # overrides. You can do that directly here, just don't forget the
# # parentheses. Maybe you want to install Nerd Fonts with a limited number of
# # fonts?
# (pkgs.nerdfonts.override { fonts = [ "FantasqueSansMono" ]; })
# # You can also create simple shell scripts directly inside your
# # configuration. For example, this adds a command 'my-hello' to your
# # environment:
# (pkgs.writeShellScriptBin "my-hello" ''
# echo "Hello, ${config.home.username}!"
# '')
];
# Home Manager is pretty good at managing dotfiles. The primary way to manage
# plain files is through 'home.file'.
home.file = {
# # Building this configuration will create a copy of 'dotfiles/screenrc' in
# # the Nix store. Activating the configuration will then make '~/.screenrc' a
# # symlink to the Nix store copy.
# ".screenrc".source = dotfiles/screenrc;
# # You can also set the file content immediately.
# ".gradle/gradle.properties".text = ''
# org.gradle.console=verbose
# org.gradle.daemon.idletimeout=3600000
# '';
};
# Home Manager can also manage your environment variables through
# 'home.sessionVariables'. If you don't want to manage your shell through Home
# Manager then you have to manually source 'hm-session-vars.sh' located at
# either
#
# ~/.nix-profile/etc/profile.d/hm-session-vars.sh
#
# or
#
# ~/.local/state/nix/profiles/profile/etc/profile.d/hm-session-vars.sh
#
# or
#
# /etc/profiles/per-user/m3tam3re/etc/profile.d/hm-session-vars.sh
#
home.sessionVariables = {
# EDITOR = "emacs";
};
# Let Home Manager install and manage itself.
programs.home-manager.enable = true;
services.cliphist = {
enable = true;
allowImages = true;
};
programs.git = {
enable = true;
difftastic.enable = true;
userName = "m3tam3re";
userEmail = "m@m3tam3re.com";
aliases = {
st = "status";
logd = "log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit";
};
extraConfig = {
core.excludesfile = "~/.gitignore_global";
init.defaultBranch = "master";
};
};
programs.jujutsu = {
enable = true;
settings = {
user = {
email = "m@m3tam3re.com";
name = "Sascha Koenig";
};
};
};
programs.ssh = {
enable = true;
matchBlocks = {
"github.com" = {
hostname = "github.com";
user = "m3tam3re";
port = 22;
identityFile = "~/.ssh/github";
};
"code.m3tam3re.com" = {
hostname = "code.m3tam3re.com";
user = "m3tam3re";
identityFile = "~/.ssh/gitea";
};
"lkk-nix-1" = {
hostname = "89.58.10.189";
user = "lkk-admin";
identityFile = "~/.ssh/lkk-admin";
};
"m3-r1" = {
hostname = "202.61.226.110";
user = "m3tam3re";
identityFile = "~/.ssh/m3tam3re";
};
"lkk-prod-test" = {
hostname = "192.168.122.215";
user = "root";
identityFile = "~/.ssh/m3tam3re";
};
"lkk-prod-1" = {
hostname = "192.168.0.24";
user = "root";
identityFile = "~/.ssh/m3tam3re";
};
"lkk-prod-2" = {
hostname = "192.168.0.20";
user = "root";
identityFile = "~/.ssh/m3tam3re";
};
"m3-deck" = {
hostname = "192.168.178.193";
user = "m3tam3re";
identityFile = "~/.ssh/m3tam3re";
};
"m3-kratos-vm" = {
hostname = "192.168.122.43";
user = "m3tam3re";
identityFile = "~/.ssh/m3tam3re";
};
"m3-hermes" = {
hostname = "95.216.214.142";
user = "m3tam3re";
identityFile = "~/.ssh/m3tam3re";
};
"m3-helios" = {
hostname = "192.168.178.210";
user = "m3tam3re";
identityFile = "~/.ssh/m3tam3re";
};
"m3-ares" = {
hostname = "192.168.1.30";
user = "m3tam3re";
identityFile = "~/.ssh/m3tam3re";
};
"m3-atlas" = {
hostname = "152.53.85.162";
user = "m3tam3re";
identityFile = "~/.ssh/m3tam3re";
};
"m3-zelda" = {
hostname = "95.217.189.186";
user = "m3tam3re";
identityFile = "~/.ssh/m3tam3re";
};
"m3-skynet" = {
hostname = "m3-skynet";
user = "admin";
identityFile = "~/.ssh/m3tam3re";
};
"shp-old" = {
hostname = "95.217.3.250";
port = 2222;
user = "m3tam3re";
identityFile = "~/.ssh/self-host-playbook";
};
"shp-1" = {
hostname = "95.217.189.186";
port = 2222;
user = "m3tam3re";
identityFile = "~/.ssh/self-host-playbook";
};
};
};
programs.zellij-ps = {
enable = true;
projectFolders = [
"${config.home.homeDirectory}/p/c"
"${config.home.homeDirectory}/p"
"${config.home.homeDirectory}/.config"
];
layout = ''
layout {
pane size=1 borderless=true {
plugin location="zellij:tab-bar"
}
pane size="70%" command="nvim"
pane split_direction="vertical" {
pane
pane command="unimatrix"
}
pane size=1 borderless=true {
plugin location="zellij:status-bar"
}
}
'';
};
}
-17
View File
@@ -1,17 +0,0 @@
{
imports = [
../common
../features/cli
./home-server.nix
];
features = {
cli = {
nushell.enable = true;
skim.enable = true;
nitch.enable = true;
secrets.enable = false;
starship.enable = true;
};
};
}
-17
View File
@@ -1,17 +0,0 @@
{
imports = [
../common
../features/cli
./home-server.nix
];
features = {
cli = {
fish.enable = true;
fzf.enable = true;
nitch.enable = true;
secrets.enable = false;
starship.enable = true;
};
};
}
-18
View File
@@ -1,18 +0,0 @@
{
systemd.user.services.librechat = {
Unit = {
Description = "LibreChat Start";
After = ["network-online.target"];
Wants = ["network-online.target"];
};
Install = {WantedBy = ["default.target"];};
Service = {
Type = "oneshot";
RemainAfterExit = "yes";
WorkingDirectory = "/home/m3tam3re/p/r/ai/LibreChat";
ExecStart = "/run/current-system/sw/bin/podman-compose up -d";
ExecStop = "/run/current-system/sw/bin/podman-compose down";
Restart = "on-failure";
};
};
}
+76
View File
@@ -0,0 +1,76 @@
# COMMON HOST CONFIGURATION
**Shared base configuration and abstractions for all hosts**
## OVERVIEW
Common imports, overlays, and custom patterns (extraServices, ports) used across 6 hosts.
## STRUCTURE
```
common/
├── default.nix # Base imports, overlays, nix settings
├── ports.nix # Centralized port registry
├── extraServices/ # Optional service modules
│ ├── default.nix
│ ├── flatpak.nix
│ ├── ollama.nix
│ ├── podman.nix
│ └── virtualisation.nix
└── users/
├── default.nix
└── m3tam3re.nix # Primary user definition
```
## WHERE TO LOOK
| Task | Location | Notes |
|------|----------|-------|
| Add port definition | ports.nix | Use config.m3ta.ports.get |
| Enable optional service | Host config extraServices | Boolean flags |
| Modify overlays | default.nix lines 27-36 | 5 overlay sources |
| Add new user | users/ | Shared across all hosts |
## CONVENTIONS
### Port Registry Pattern
```nix
# Define in ports.nix
definitions = {
myservice = 3099;
};
# Access in host config
config.m3ta.ports.get "myservice" # Returns 3099
```
### extraServices Abstraction
Host configs enable via boolean:
```nix
extraServices = {
podman.enable = true; # Container runtime
ollama.enable = true; # LLM inference
flatpak.enable = false; # Flatpak apps
virtualisation.enable = true; # QEMU/KVM
};
```
### Overlay Precedence (bottom overrides top)
1. stable-packages (nixpkgs-stable)
2. locked-packages (nixpkgs-locked)
3. pinned-packages (nixpkgs-45570c2, nixpkgs-9e58ed7)
4. master-packages (nixpkgs-master)
5. m3ta-nixpkgs (local custom overlay)
## ANTI-PATTERNS
- **DON'T** add host-specific logic to common/ - belongs in hosts/<name>/
- **DON'T** bypass port registry - hardcoded ports break consistency
- **DON'T** modify user shell globally - set per-user if needed
## NOTES
- Nix GC runs weekly, keeps 30 days
- Trusted users: root, m3tam3re
- Default shell: Nushell (set line 77)
- Home-manager integrated at common level, not per-host
- TODO on line 69: ports should only return actually used ports
+16 -7
View File
@@ -1,37 +1,46 @@
# Common configuration for all hosts # Common configuration for all hosts
{ {
config,
pkgs, pkgs,
lib, lib,
inputs, inputs,
outputs, outputs,
system,
... ...
}: { }: {
imports = [ imports = [
./extraServices ./extraServices
./ports.nix
./users ./users
inputs.home-manager.nixosModules.home-manager inputs.home-manager.nixosModules.home-manager
]; ];
environment.pathsToLink = [ environment.pathsToLink = ["/share/xdg-desktop-portal" "/share/applications"];
"/share/xdg-desktop-portal"
"/share/applications"
];
home-manager = { home-manager = {
useGlobalPkgs = true;
useUserPackages = true; useUserPackages = true;
extraSpecialArgs = {inherit inputs outputs;}; extraSpecialArgs = {
inputs = inputs // {agents = null;};
inherit outputs system;
videoDrivers = config.services.xserver.videoDrivers or [];
}; };
};
nixpkgs = { nixpkgs = {
# You can add overlays here # You can add overlays here
overlays = [ overlays = [
# Add overlays your own flake exports (from overlays and pkgs dir): # Add overlays your own flake exports (from overlays and pkgs dir):
outputs.overlays.additions #outputs.overlays.additions
outputs.overlays.modifications #outputs.overlays.modifications
outputs.overlays.stable-packages outputs.overlays.stable-packages
outputs.overlays.locked-packages outputs.overlays.locked-packages
outputs.overlays.pinned-packages outputs.overlays.pinned-packages
outputs.overlays.master-packages outputs.overlays.master-packages
inputs.m3ta-nixpkgs.overlays.default
inputs.m3ta-nixpkgs.overlays.modifications
(outputs.lib.mkLlmAgentsOverlay system)
# You can also add overlays exported from other flakes: # You can also add overlays exported from other flakes:
# neovim-nightly-overlay.overlays.default # neovim-nightly-overlay.overlays.default
-1
View File
@@ -14,7 +14,6 @@ in {
xdg.portal = { xdg.portal = {
# xdg desktop intergration (required for flatpak) # xdg desktop intergration (required for flatpak)
enable = true; enable = true;
wlr.enable = true;
extraPortals = with pkgs; [ extraPortals = with pkgs; [
xdg-desktop-portal-hyprland xdg-desktop-portal-hyprland
]; ];
+5 -4
View File
@@ -12,16 +12,17 @@ in {
config = mkIf cfg.enable { config = mkIf cfg.enable {
services.ollama = { services.ollama = {
enable = true; enable = true;
acceleration = package =
if config.services.xserver.videoDrivers == ["amdgpu"] if config.services.xserver.videoDrivers == ["amdgpu"]
then "rocm" then pkgs.ollama-rocm
else if config.services.xserver.videoDrivers == ["nvidia"] else if config.services.xserver.videoDrivers == ["nvidia"]
then "cuda" then pkgs.ollama-cuda
else null; else pkgs.ollama-cpu;
host = "[::]"; host = "[::]";
openFirewall = true; openFirewall = true;
environmentVariables = { environmentVariables = {
OLLAMA_ORIGINS = "https://msty.studio"; OLLAMA_ORIGINS = "https://msty.studio";
OLLAMA_HOST = "0.0.0.0";
}; };
}; };
nixpkgs.config = { nixpkgs.config = {
+4 -10
View File
@@ -17,19 +17,13 @@ in {
package = pkgs.qemu_kvm; package = pkgs.qemu_kvm;
runAsRoot = true; runAsRoot = true;
swtpm.enable = true; swtpm.enable = true;
ovmf = {
enable = true;
packages = [
(pkgs.locked.OVMF.override {
secureBoot = true;
tpmSupport = true;
})
.fd
];
};
}; };
}; };
}; };
programs.virt-manager.enable = true; programs.virt-manager.enable = true;
systemd.services.virt-secret-init-encryption.enable = false;
environment = {
systemPackages = [pkgs.qemu];
};
}; };
} }
+80
View File
@@ -0,0 +1,80 @@
{config, ...}: {
m3ta.ports = {
enable = true;
definitions = {
# System services
ssh = 22;
# Web & proxy services
traefik = 80;
traefik-ssl = 443;
# Databases
postgres = 5432;
mysql = 3306;
redis = 6379;
# VPN & networking
wireguard = 51820;
tailscale = 41641;
headscale = 3009;
netbird-stun = 3478;
netbird-proxy = 8443;
netbird-metrics = 9090;
netbird-health = 9000;
# Containers & web apps
gitea = 3030;
baserow = 3001;
ghost = 3002;
wastebin = 3003;
littlelink = 3004;
searx = 3005;
restreamer = 3006;
paperless = 3012;
vaultwarden = 3013;
slash = 3010;
slash-nemoti = 3016;
kestra = 3018;
outline = 3019;
authentik = 3023;
tuwunel = 3024;
honcho = 3025;
# Agent infrastructure
hermes-api = 8642;
hermes-dashboard = 9119;
# Home automation
homarr = 7575;
# DNS
adguardhome = 53;
};
hostOverrides = {
# Host-specific overrides
m3-ares = {
# Any custom port overrides for m3-ares
};
m3-atlas = {
# Any custom port overrides for m3-atlas
};
m3-helios = {
# Any custom port overrides for m3-helios
};
m3-kratos = {
# Any custom port overrides for m3-kratos
};
};
};
environment.etc."info/all-ports.json" = {
text = builtins.toJSON {
hostname = config.networking.hostName;
ports = config.m3ta.ports.all; # TODO should only return actually used ports
};
};
}
+230 -5
View File
@@ -1,11 +1,215 @@
# hosts/common/users/m3tam3re.nix — Central user definition with m3ta-home integration.
#
# This module:
# 1. Creates the m3tam3re NixOS user
# 2. Loads the m3ta-home profile system via mkHome
# 3. Sets per-host feature flags based on a host profile mapping
# 4. Imports per-host home.nix overrides (monitors, HW-specific config)
#
# To add a new host:
# 1. Add entry to hostProfiles below
# 2. Add feature flags in the hostFlags section
# 3. Create hosts/<hostname>/home.nix if the host needs overrides (monitors, etc.)
{ {
config, config,
pkgs, pkgs,
inputs, inputs,
... ...
}: { }: let
hostname = config.networking.hostName;
# ── Per-host profile mapping ──
# Determines which m3ta-home context and sets each host gets.
hostProfiles = {
# ── Desktop hosts ──
m3-ares = {
context = "desktop";
sets = ["coding" "gaming" "media"];
};
m3-kratos = {
context = "desktop";
sets = ["coding" "gaming" "media"];
};
m3-daedalus = {
context = "desktop";
sets = ["coding" "media"];
};
# ── Server hosts ──
m3-atlas = {
context = "server";
sets = [];
};
m3-helios = {
context = "server";
sets = [];
};
m3-hermes = {
context = "server";
sets = [];
};
m3-aether = {
context = "server";
sets = [];
};
};
profile =
hostProfiles.${
hostname
} or {
context = "server";
sets = [];
};
m3ta-lib = inputs.m3ta-home.lib;
# Check if a per-host home.nix exists
hostHomeFile = ./../../${hostname}/home.nix;
hostHomeExists = builtins.pathExists hostHomeFile;
# ── Per-host feature flags ──
# These enable/disable specific m3ta-home modules per host.
hostFlags =
if hostname == "m3-ares" || hostname == "m3-kratos"
then {
# Full desktop workstation
base = {
shell = {
fish.enable = true;
nushell.enable = true;
starship.enable = true;
};
cliTools = {
fzf.enable = true;
nitch.enable = true;
television.enable = true;
};
secrets.enable = true;
};
desktop = {
wm = {
hyprland.enable = true;
rofi.enable = true;
wayland.enable = true;
dms.enable = true;
};
apps = {
crypto.enable = true;
obsidian.enable = true;
office.enable = true;
};
theme = {
fonts.enable = true;
wallpapers.enable = true;
};
};
coding = {
editors = {
neovim.enable = true;
zed.enable = true;
};
lsp.enable = true;
packages.enable = true;
languages = {
python.enable = true;
javascript.enable = true;
rustToolchain.enable = true;
go.enable = true;
typescript.enable = true;
};
};
profiles.gaming = {
steam.enable = true;
gamescope.enable = true;
};
profiles.media = {
obs.enable = true;
ffmpeg.enable = true;
kdenlive.enable = true;
ytDlp.enable = true;
};
}
else if hostname == "m3-daedalus"
then {
# Portable laptop — desktop without gaming, no Hyprland
base = {
shell = {
fish.enable = true;
nushell.enable = true;
starship.enable = true;
};
cliTools = {
fzf.enable = true;
nitch.enable = true;
television.enable = true;
};
secrets.enable = true;
};
desktop = {
wm = {
hyprland.enable = false;
wayland.enable = false;
};
apps = {
crypto.enable = false;
obsidian.enable = true;
office.enable = false;
};
theme = {
fonts.enable = true;
wallpapers.enable = false;
};
};
coding = {
editors = {
neovim.enable = true;
zed.enable = true;
};
lsp.enable = true;
packages.enable = true;
languages = {
python.enable = true;
javascript.enable = true;
rustToolchain.enable = true;
go.enable = true;
typescript.enable = true;
};
};
profiles.media = {
ytDlp.enable = true;
};
}
else if hostname == "m3-atlas"
then {
# Primary server — coding capable
base = {
shell = {
nushell.enable = true;
starship.enable = true;
};
cliTools = {
fzf.enable = true;
nitch.enable = true;
zellij.enable = true;
};
};
coding.editors.neovim.enable = true;
}
else {
# m3-helios, m3-hermes, m3-aether — minimal server
base = {
shell = {
fish.enable = true;
starship.enable = true;
};
cliTools = {
fzf.enable = true;
nitch.enable = true;
};
};
};
in {
# ── NixOS user definition ──
users.users.m3tam3re = { users.users.m3tam3re = {
#initialHashedPassword = "$y$j9T$IoChbWGYRh.rKfmm0G86X0$bYgsWqDRkvX.EBzJTX.Z0RsTlwspADpvEF3QErNyCMC";
password = "12345"; password = "12345";
isNormalUser = true; isNormalUser = true;
description = "m3tam3re"; description = "m3tam3re";
@@ -24,9 +228,30 @@
]; ];
openssh.authorizedKeys.keys = [ openssh.authorizedKeys.keys = [
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC3YEmpYbM+cpmyD10tzNRHEn526Z3LJOzYpWEKdJg8DaYyPbDn9iyVX30Nja2SrW4Wadws0Y8DW+Urs25/wVB6mKl7jgPJVkMi5hfobu3XAz8gwSdjDzRSWJrhjynuaXiTtRYED2INbvjLuxx3X8coNwMw58OuUuw5kNJp5aS2qFmHEYQErQsGT4MNqESe3jvTP27Z5pSneBj45LmGK+RcaSnJe7hG+KRtjuhjI7RdzMeDCX73SfUsal+rHeuEw/mmjYmiIItXhFTDn8ZvVwpBKv7xsJG90DkaX2vaTk0wgJdMnpVIuIRBa4EkmMWOQ3bMLGkLQeK/4FUkNcvQ/4+zcZsg4cY9Q7Fj55DD41hAUdF6SYODtn5qMPsTCnJz44glHt/oseKXMSd556NIw2HOvihbJW7Rwl4OEjGaO/dF4nUw4c9tHWmMn9dLslAVpUuZOb7ykgP0jk79ldT3Dv+2Hj0CdAWT2cJAdFX58KQ9jUPT3tBnObSF1lGMI7t77VU= m3tam3re@m3-nix" "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC3YEmpYbM+cpmyD10tzNRHEn526Z3LJOzYpWEKdJg8DaYyPbDn9iyVX30Nja2SrW4Wadws0Y8DW+Urs25/wVB6mKl7jgPJVkMi5hfobu3XAz8gwSdjDzRSWJrhjynuaXiTtRYED2INbvjLuxx3X8coNwMw58OuUuw5kNJp5aS2qFmHEYQErQsGT4MNqESe3jvTP27Z5pSneBj45LmGK+RcaSnJe7hG+KRtjuhjI7RdzMeDCX73SfUsal+rHeuEw/mmjYmiIItXhFTDn8ZvVwpBKv7xsJG90DkaX2vaTk0wgJdMnpVIuIRBa4EkmMWOQ3bMLGkLQeK/4FUkNcvQ/4+zcZsg4cY9Q7Fj55DD41hAUdF6SYODtn5qMPsTCnJz44glHt/oseKXMSd556NIw2HOvihbJW7Rwl4OEjGaO/dF4nUw4c9tHWmMn9dLslAVpUuZOb7ykgP0jk79ldT3Dv+2Hj0CdAWT2cJAdFX58KQ9jUPT3tBnObSF1lGMI7t77VU= m3tam3re@m3-nix"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBZcjCKl0DRuOUOMXbM0GKY5JjvmyFpVZ/tRlTKWu/zp razr"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEZbg/Z9mnflXuLahGY8WOSBMqbgeqVIkIwRkquys1Ml sascha.koenig@azintec.com"
]; ];
packages = [inputs.home-manager.packages.${pkgs.system}.default]; packages = [inputs.home-manager.packages.${pkgs.stdenv.hostPlatform.system}.default];
};
# ── Home-Manager configuration via m3ta-home ──
home-manager.users.m3tam3re = {
imports =
[
# Load m3ta-home composition engine
(m3ta-lib.mkHome {
user = "m3tam3re";
identity = "private";
inherit (profile) context sets;
})
# Per-host feature flags
hostFlags
]
# Per-host home.nix (Hyprland monitors, XDG/MIME, HW-specific overrides)
++ (
if hostHomeExists
then [hostHomeFile]
else []
);
}; };
home-manager.users.m3tam3re =
import ../../../home/m3tam3re/${config.networking.hostName}.nix;
} }
+111
View File
@@ -0,0 +1,111 @@
# Edit this configuration file to define what should be installed on
# your system. Help is available in the configuration.nix(5) man page, on
# https://search.nixos.org/options and in the NixOS manual (`nixos-help`).
{pkgs, ...}: {
imports = [
# Include the results of the hardware scan.
./disko-config.nix
./hardware-configuration.nix
];
# Bootloader.
boot.loader.grub = {
efiSupport = true;
efiInstallAsRemovable = true;
};
networking.hostName = "m3-helios"; # Define your hostname.
networking.hostId = "3ebf1cd3";
# Pick only one of the below networking options.
# networking.wireless.enable = true; # Enables wireless support via wpa_supplicant.
networking.networkmanager.enable =
true; # Easiest to use and most distros use this by default.
# Set your time zone.
time.timeZone = "Europe/Berlin";
# Configure network proxy if necessary
# networking.proxy.default = "http://user:password@proxy:port/";
# networking.proxy.noProxy = "127.0.0.1,localhost,internal.domain";
# Select internationalisation properties.
i18n.defaultLocale = "en_US.UTF-8";
# console = {
# font = "Lat2-Terminus16";
# keyMap = "us";
# useXkbConfig = true; # use xkb.options in tty.
# };
# Enable the X11 windowing system.
# services.xserver.enable = true;
# Enable the GNOME Desktop Environment.
# services.xserver.displayManager.gdm.enable = true;
# services.xserver.desktopManager.gnome.enable = true;
# Configure keymap in X11
# services.xserver.xkb.layout = "us";
# services.xserver.xkb.options = "eurosign:e,caps:escape";
# Enable CUPS to print documents.
# services.printing.enable = true;
# Enable sound.
# hardware.pulseaudio.enable = true;
# OR
# Enable touchpad support (enabled default in most desktopManager).
# services.libinput.enable = true;
# Define a user account. Don't forget to set a password with passwd.
# List packages installed in system profile. To search, run:
# $ nix search wget
environment.systemPackages = with pkgs; [neovim git];
# Some programs need SUID wrappers, can be configured further or are
# started in user sessions.
# programs.mtr.enable = true;
# programs.gnupg.agent = {
# enable = true;
# enableSSHSupport = true;
# };
# List services that you want to enable:
# Enable the OpenSSH daemon.
services.openssh.enable = true;
services.fstrim = {
enable = true; # For SSD/thin-provisioned storage
interval = "weekly";
};
# Open ports in the firewall.
# networking.firewall.allowedTCPPorts = [ ... ];
# networking.firewall.allowedUDPPorts = [ ... ];
# Or disable the firewall altogether.
# networking.firewall.enable = false;
# Copy the NixOS configuration file and link it from the resulting system
# (/run/current-system/configuration.nix). This is useful in case you
# accidentally delete configuration.nix.
# system.copySystemConfiguration = true;
# This option defines the first version of NixOS you have installed on this particular machine,
# and is used to maintain compatibility with application data (e.g. databases) created on older NixOS versions.
#
# Most users should NEVER change this value after the initial install, for any reason,
# even if you've upgraded your system to a new NixOS release.
#
# This value does NOT affect the Nixpkgs version your packages and OS are pulled from,
# so changing it will NOT upgrade your system - see https://nixos.org/manual/nixos/stable/#sec-upgrading for how
# to actually do that.
#
# This value being lower than the current NixOS release does NOT mean your system is
# out of date, out of support, or vulnerable.
#
# Do NOT change this value unless you have manually inspected all the changes it would make to your configuration,
# and migrated your data accordingly.
#
# For more information, see `man configuration.nix` or https://nixos.org/manual/nixos/stable/options#opt-system.stateVersion .
system.stateVersion = "24.11"; # Did you read the comment?
}
+50
View File
@@ -0,0 +1,50 @@
# A staring point is the basic NIXOS configuration generated by the ISO installer.
# On an existing NIXOS install you can use the following command in your flakes basedir:
# sudo nixos-generate-config --dir ./hosts/m3tam3re
#
# Please make sure to change the first couple of lines in your configuration.nix:
# { config, inputs, ouputs, lib, pkgs, ... }:
#
# {
# imports = [ # Include the results of the hardware scan.
# ./hardware-configuration.nix
# inputs.home-manager.nixosModules.home-manager
# ];
# ...
#
# Moreover please update the packages option in your user configuration and add the home-manager options:
# users.users = {
# m3tam3re = {
# isNormalUser = true;
# initialPassword = "12345";
# extraGroups = [ "wheel" ]; # Enable sudo for the user.
# packages = [ inputs.home-manager.packages.${pkgs.system}.default ];
# };
# };
#
# home-manager = {
# useUserPackages = true;
# extraSpecialArgs = { inherit inputs outputs; };
# users.m3tam3re =
# import ../../home/m3tam3re/${config.networking.hostName}.nix;
# };
#
# Please also change your hostname accordingly:
#:w
# networking.hostName = "nixos"; # Define your hostname.
{
imports = [
../common
./configuration.nix
./programs.nix
./secrets.nix
./services
];
extraServices = {
flatpak.enable = true;
ollama.enable = false;
podman.enable = true;
virtualisation.enable = false;
};
}
+39
View File
@@ -0,0 +1,39 @@
{
disko.devices = {
disk = {
main = {
type = "disk";
device = "/dev/sda";
content = {
type = "gpt";
partitions = {
boot = {
size = "1M";
type = "EF02"; # for GRUB MBR
priority = 1;
};
esp = {
size = "512M";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = ["defaults" "umask=0077"];
};
};
root = {
size = "100%";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
mountOptions = ["noatime" "nodiratime" "discard"];
};
};
};
};
};
};
};
}
@@ -0,0 +1,28 @@
# Do not modify this file! It was generated by nixos-generate-config
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{
config,
lib,
pkgs,
modulesPath,
...
}: {
imports = [
(modulesPath + "/profiles/qemu-guest.nix")
];
boot.initrd.availableKernelModules = ["ata_piix" "uhci_hcd" "virtio_pci" "virtio_scsi" "sd_mod" "sr_mod"];
boot.initrd.kernelModules = [];
boot.kernelModules = [];
boot.extraModulePackages = [];
# Enables DHCP on each ethernet and wireless interface. In case of scripted networking
# (the default) this is the recommended approach. When using systemd-networkd it's
# still possible to use this option, but it's recommended to use it in conjunction
# with explicit per-interface declarations with `networking.interfaces.<interface>.useDHCP`.
networking.useDHCP = lib.mkDefault true;
# networking.interfaces.ens18.useDHCP = lib.mkDefault true;
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
}
+14
View File
@@ -0,0 +1,14 @@
{pkgs, ...}: {
programs.nix-ld.enable = true;
programs.nix-ld.libraries = with pkgs; [
# Add any missing dynamic libraries for unpackaged programs
# here, NOT in environment.systemPackages
];
programs.fish.enable = true;
programs.nh = {
enable = true;
clean.enable = true;
clean.extraArgs = "--keep-since 4d --keep 3";
flake = "/home/m3tam3re/p/nixos/nixos-config";
};
}
+15
View File
@@ -0,0 +1,15 @@
{
age = {
secrets = {
traefik = {
file = ../../secrets/traefik.age;
mode = "770";
owner = "traefik";
};
m3tam3re-secrets = {
file = ../../secrets/m3tam3re-secrets.age;
owner = "m3tam3re";
};
};
};
}
+7
View File
@@ -0,0 +1,7 @@
{
services.cloud-init = {
enable = true;
ext4.enable = true;
network.enable = true;
};
}
+5
View File
@@ -0,0 +1,5 @@
{
imports = [
./cloud-init.nix
];
}
+6 -11
View File
@@ -1,11 +1,7 @@
# Edit this configuration file to define what should be installed on # Edit this configuration file to define what should be installed on
# your system. Help is available in the configuration.nix(5) man page, on # your system. Help is available in the configuration.nix(5) man page, on
# https://search.nixos.org/options and in the NixOS manual (`nixos-help`). # https://search.nixos.org/options and in the NixOS manual (`nixos-help`).
{ {pkgs, ...}: {
config,
pkgs,
...
}: {
imports = [ imports = [
# Include the results of the hardware scan. # Include the results of the hardware scan.
./hardware-configuration.nix ./hardware-configuration.nix
@@ -13,7 +9,6 @@
specialisation = { specialisation = {
"NVIDIA".configuration = { "NVIDIA".configuration = {
boot.kernelParams = ["nvidia.NVreg_PreserveVideoMemoryAllocations=1"];
system.nixos.tags = ["NVIDIA"]; system.nixos.tags = ["NVIDIA"];
services.xserver.videoDrivers = ["nvidia"]; services.xserver.videoDrivers = ["nvidia"];
hardware.nvidia-container-toolkit.enable = true; hardware.nvidia-container-toolkit.enable = true;
@@ -24,15 +19,15 @@
boot.loader.systemd-boot.enable = true; boot.loader.systemd-boot.enable = true;
boot.loader.systemd-boot.memtest86.enable = true; boot.loader.systemd-boot.memtest86.enable = true;
boot.initrd.services.lvm.enable = false; boot.initrd.services.lvm.enable = false;
boot.extraModulePackages = with config.boot.kernelPackages; [v4l2loopback]; # boot.kernelModules = [];
boot.kernelModules = ["v4l2loopback"]; boot.kernelPackages = pkgs.linuxPackages_latest;
boot.extraModprobeConfig = '' boot.extraModprobeConfig = ''
options kvm_intel nested=1 options kvm_intel nested=1
options kvm_intel emulate_invalid_guest_state=0 options kvm_intel emulate_invalid_guest_state=0
options kvm ignore_msrs=1 options kvm ignore_msrs=1
options v4l2loopback exclusive_caps=1 max_buffers=2
''; '';
boot.blacklistedKernelModules = ["nova_core"];
# CRITICAL FIX #4: Kernel parameters to prevent nouveau from loading early
networking.hostName = "m3-ares"; # Define your hostname. networking.hostName = "m3-ares"; # Define your hostname.
# warp-terminal update fix # warp-terminal update fix
@@ -65,7 +60,7 @@
# Enable the GNOME Desktop Environment. # Enable the GNOME Desktop Environment.
# services.xserver.displayManager.gdm.enable = true; # services.xserver.displayManager.gdm.enable = true;
# services.xserver.desktopManager.gnome.enable = true; # services.xserver.desktopManager.gnome.enable = true;
# displayManager.gdm.enable = true;
# Configure keymap in X11 # Configure keymap in X11
# services.xserver.xkb.layout = "us"; # services.xserver.xkb.layout = "us";
# services.xserver.xkb.options = "eurosign:e,caps:escape"; # services.xserver.xkb.options = "eurosign:e,caps:escape";
+1 -1
View File
@@ -44,7 +44,7 @@
extraServices = { extraServices = {
flatpak.enable = true; flatpak.enable = true;
ollama.enable = true; ollama.enable = false;
podman.enable = true; podman.enable = true;
virtualisation.enable = true; virtualisation.enable = true;
}; };
+33 -1
View File
@@ -1,4 +1,15 @@
{ {
config,
pkgs,
inputs,
...
}: {
# Workaround for tuxedo-drivers module bug in unstable (nixpkgs#480391)
# The unstable module has a type error - use stable module until fix propagates
# disabledModules = [ "hardware/tuxedo-drivers.nix" ];
# imports =
# [ "${inputs.nixpkgs-stable}/nixos/modules/hardware/tuxedo-drivers.nix" ];
hardware.nvidia = { hardware.nvidia = {
prime = { prime = {
offload.enable = false; offload.enable = false;
@@ -15,12 +26,33 @@
open = false; open = false;
dynamicBoost.enable = true; dynamicBoost.enable = true;
nvidiaSettings = true; nvidiaSettings = true;
package = config.boot.kernelPackages.nvidiaPackages.production;
}; };
hardware.tuxedo-drivers.enable = true;
hardware.bluetooth.enable = true; hardware.bluetooth.enable = true;
hardware.keyboard.zsa.enable = true; hardware.keyboard.zsa.enable = true;
hardware.graphics.enable = true; hardware.graphics.enable = true;
services.hardware.bolt.enable = true; services.hardware.bolt.enable = true;
services.auto-cpufreq.enable = true; services.auto-cpufreq.enable = true;
services.tlp.enable = true; services.tlp = {
enable = true;
settings = {
START_CHARGE_THRESH_BAT0 = 75;
STOP_CHARGE_THRESH_BAT0 = 80;
};
};
environment.systemPackages = with pkgs; [tuxedo-backlight];
security.sudo.extraRules = [
{
users = ["@wheel"];
commands = [
{
command = "/run/current-system/sw/bin/set-backlight";
options = ["NOPASSWD"];
}
];
}
];
} }
@@ -1,29 +1,17 @@
# hosts/m3-ares/home.nix — Host-specific home-manager overrides.
# TUXEDO laptop: eDP-1 + HDMI-A-1 external monitor.
# Everything else (shell, editors, gaming, media, theme, etc.) comes from
# m3ta-home via the profile mapping in hosts/common/users/m3tam3re.nix.
{ {
config, config,
lib, lib,
... ...
}: }:
with lib; let with lib; {
cfg = config.features.desktop.hyprland;
in {
imports = [
../common
./dotfiles
./home.nix
../features/cli
../features/coding
../features/desktop
#./services/librechat.nix
];
options.features.desktop.hyprland.enable =
mkEnableOption "enable Hyprland";
config = mkMerge [ config = mkMerge [
# Base configuration # ── XDG / MIME defaults ──
{ {
xdg = { xdg = {
# TODO: better structure
enable = true; enable = true;
configFile."mimeapps.list".force = true; configFile."mimeapps.list".force = true;
mimeApps = { mimeApps = {
@@ -46,36 +34,17 @@ in {
}; };
}; };
}; };
features = {
cli = {
fish.enable = true;
nushell.enable = true;
skim.enable = true;
nitch.enable = true;
secrets.enable = true;
starship.enable = true;
};
desktop = {
coding.enable = true;
crypto.enable = true;
gaming.enable = true;
hyprland.enable = true;
media.enable = true;
office.enable = true;
rofi.enable = true;
fonts.enable = true;
wayland.enable = true;
};
};
} }
(mkIf cfg.enable { # ── Hyprland monitor layout ──
(mkIf config.desktop.wm.hyprland.enable {
wayland.windowManager.hyprland = { wayland.windowManager.hyprland = {
enable = true; enable = true;
settings = { settings = {
exec-once = ["tuxedo-backlight"];
monitor = [ monitor = [
"eDP-1,preferred,0x0,1.25" "eDP-1,preferred,0x0,1.25"
"HDMI-A-1,preferred,2560x0,1" "HDMI-A-1,1920x1080@120,2560x0,1"
]; ];
workspace = [ workspace = [
"1, monitor:eDP-1, default:true" "1, monitor:eDP-1, default:true"
@@ -86,14 +55,14 @@ in {
"6, monitor:HDMI-A-1" "6, monitor:HDMI-A-1"
]; ];
windowrule = [ windowrule = [
"workspace 1,class:dev.zed.Zed" "match:class dev.zed.Zed, workspace 1"
"workspace 1,class:Msty" "match:class Msty, workspace 1"
"workspace 2,class:(com.obsproject.Studio)" "match:class ^(com.obsproject.Studio)$, workspace 2"
"workspace 4,opacity 1.0, class:(brave-browser)" "match:class ^(brave-browser)$, workspace 4, opacity 1.0"
"workspace 4,opacity 1.0, class:(vivaldi-stable)" "match:class ^(vivaldi-stable)$, workspace 4, opacity 1.0"
"fullscreen,class:^steam_app_\\d+$" "match:class ^steam_app_\\d+$, fullscreen on"
"workspace 5,class:^steam_app_\\d+$" "match:class ^steam_app_\\d+$, workspace 5"
"idleinhibit focus, class:^steam_app_\\d+$" "match:class ^steam_app_\\d+$, idle_inhibit focus"
]; ];
}; };
}; };
+1 -1
View File
@@ -28,7 +28,7 @@
programs.fish.enable = true; programs.fish.enable = true;
programs.thunar = { programs.thunar = {
enable = true; enable = true;
plugins = with pkgs.xfce; [thunar-archive-plugin thunar-volman]; plugins = with pkgs; [thunar-archive-plugin thunar-volman];
}; };
programs.gnupg.agent = { programs.gnupg.agent = {
enable = true; enable = true;
+28
View File
@@ -1,6 +1,10 @@
{ {
age = { age = {
secrets = { secrets = {
anytype-key = {
file = ../../secrets/anytype-key-ares.age;
owner = "m3tam3re";
};
wg-DE = { wg-DE = {
file = ../../secrets/wg-DE.age; file = ../../secrets/wg-DE.age;
path = "/etc/wireguard/DE.conf"; path = "/etc/wireguard/DE.conf";
@@ -21,11 +25,35 @@
file = ../../secrets/wg-BR.age; file = ../../secrets/wg-BR.age;
path = "/etc/wireguard/BR.conf"; path = "/etc/wireguard/BR.conf";
}; };
ref-key = {
file = ../../secrets/ref-key.age;
owner = "m3tam3re";
};
exa-key = {
file = ../../secrets/exa-key.age;
owner = "m3tam3re";
};
outline-key = {
file = ../../secrets/outline-key.age;
owner = "m3tam3re";
};
basecamp-client-id = {
file = ../../secrets/basecamp-client-id.age;
owner = "m3tam3re";
};
basecamp-client-secret = {
file = ../../secrets/basecamp-client-secret.age;
owner = "m3tam3re";
};
tailscale-key.file = ../../secrets/tailscale-key.age; tailscale-key.file = ../../secrets/tailscale-key.age;
m3tam3re-secrets = { m3tam3re-secrets = {
file = ../../secrets/m3tam3re-secrets.age; file = ../../secrets/m3tam3re-secrets.age;
owner = "m3tam3re"; owner = "m3tam3re";
}; };
hermes-env = {
file = ../../secrets/hermes-env.age;
owner = "m3tam3re";
};
}; };
}; };
} }
+26 -8
View File
@@ -1,21 +1,44 @@
{ {pkgs, ...}: {
imports = [ imports = [
./containers ./containers
./greetd.nix
./hermes-agent.nix
./netbird.nix
#./n8n.nix #./n8n.nix
./mem0.nix
./postgres.nix ./postgres.nix
./restic.nix ./restic.nix
./sound.nix ./sound.nix
./tailscale.nix
./udev.nix ./udev.nix
./wireguard.nix ./wireguard.nix
]; ];
# console.useXkbConfig = true;
# services.xserver.xkb = {
# layout = "de,us";
# options = "ctrl:nocaps";
# };
# optional, falls du auch die TTY-Konsole deutsch willst:
services = { services = {
hypridle.enable = true; hypridle.enable = true;
espanso = {
enable = true;
package = pkgs.espanso-wayland;
};
printing.enable = true; printing.enable = true;
gvfs.enable = true; gvfs.enable = true;
trezord.enable = true; trezord.enable = true;
gnome.gnome-keyring.enable = true; gnome.gnome-keyring.enable = true;
qdrant.enable = true; qdrant.enable = true;
# qdrant = {
# enable = true;
# settings = {
# service = {
# host = "0.0.0.0";
# };
# };
# };
upower.enable = true; upower.enable = true;
avahi = { avahi = {
enable = true; enable = true;
@@ -26,11 +49,6 @@
userServices = true; userServices = true;
}; };
}; };
displayManager.gdm.enable = true;
}; };
systemd.sleep.extraConfig = ''
AllowSuspend=no
AllowHibernation=no
AllowHybridSleep=no
AllowSuspendThenHibernate=no
'';
} }
+38
View File
@@ -0,0 +1,38 @@
# greetd login manager for m3-kratos (replaces broken GDM on nixos-unstable).
# Uses tuigreet as the greeter, launching Hyprland after authentication.
{
pkgs,
config,
lib,
...
}: let
tuigreet = "${lib.getExe pkgs.tuigreet}";
# Use start-hyprland wrapper to avoid Hyprland startup warnings
# withUWSM=true is set in programs.nix; start-hyprland handles this correctly
hyprlandCmd = "${config.programs.hyprland.package}/bin/start-hyprland";
in {
services.greetd = {
enable = true;
settings = {
default_session = {
user = "greeter";
# Minimal config: verified supported flags only
# The --time and --remember are tested; power commands omitted
# to avoid potential quoting/parsing issues
command = builtins.concatStringsSep " " [
tuigreet
"--time"
"--remember"
"--asterisks"
"--cmd ${hyprlandCmd}"
];
};
};
};
# Required for --remember to persist username between logins
systemd.tmpfiles.rules = [
"d /var/cache/tuigreet 0755 greeter greeter - -"
];
}
+195
View File
@@ -0,0 +1,195 @@
{config, ...}: let
# Default ElevenLabs voice: Bella (German-capable female)
elevenlabsVoiceId = "hpp4J3VqNfWAUOO0d1Us";
in {
services.hermes-agent = {
enable = true;
addToSystemPackages = true;
# Secrets via agenix
environmentFiles = [config.age.secrets."hermes-env".path];
# Non-secret environment variables
environment = {
GLM_BASE_URL = "https://api.z.ai/api/coding/paas/v4/";
};
settings = {
# ── Model ──────────────────────────────────────────────────────────
model = {
default = "gpt-5.5";
provider = "openai-codex";
};
fallback_providers = [
{
provider = "zai";
model = "glm-5.1";
}
{
provider = "minimax";
model = "MiniMax-M2.7";
}
];
credential_pool_strategies = {
zai = "fill_first";
};
toolsets = ["all"];
# ── Agent ──────────────────────────────────────────────────────────
agent = {
max_turns = 90;
gateway_timeout = 1800;
tool_use_enforcement = "auto";
};
# ── Terminal ───────────────────────────────────────────────────────
terminal = {
backend = "ssh";
modal_mode = "auto";
cwd = ".";
timeout = 180;
persistent_shell = true;
};
# ── Browser ────────────────────────────────────────────────────────
browser = {
inactivity_timeout = 120;
command_timeout = 30;
cloud_provider = "local";
};
# ── Checkpoints / Compression ──────────────────────────────────────
checkpoints = {
enabled = true;
max_snapshots = 50;
};
file_read_max_chars = 100000;
compression = {
enabled = true;
threshold = 0.5;
target_ratio = 0.2;
protect_last_n = 20;
};
# ── Display ────────────────────────────────────────────────────────
display = {
compact = false;
personality = "kawaii";
resume_display = "full";
busy_input_mode = "interrupt";
inline_diffs = true;
skin = "default";
tool_progress = "all";
};
# ── TTS / STT / Voice ──────────────────────────────────────────────
tts = {
provider = "elevenlabs";
elevenlabs = {
voice_id = elevenlabsVoiceId;
model_id = "eleven_multilingual_v2";
};
};
stt = {
enabled = true;
provider = "local";
local = {model = "base";};
};
voice = {
record_key = "ctrl+b";
max_recording_seconds = 120;
silence_threshold = 200;
silence_duration = 3.0;
};
# ── Memory ─────────────────────────────────────────────────────────
memory = {
memory_enabled = true;
user_profile_enabled = true;
memory_char_limit = 2200;
user_char_limit = 1375;
};
# ── Delegation ─────────────────────────────────────────────────────
delegation = {
max_iterations = 50;
};
# ── Discord ────────────────────────────────────────────────────────
discord = {
require_mention = true;
auto_thread = true;
reactions = true;
};
# ── Approvals / Security ───────────────────────────────────────────
approvals = {
mode = "manual";
timeout = 60;
};
security = {
redact_secrets = true;
tirith_enabled = true;
tirith_fail_open = true;
};
# ── Cron / Session ─────────────────────────────────────────────────
cron = {wrap_response = true;};
session_reset = {
mode = "both";
idle_minutes = 1440;
at_hour = 4;
};
# ── Web ────────────────────────────────────────────────────────────
web = {backend = "exa";};
# ── Platform Toolsets ──────────────────────────────────────────────
platform_toolsets = {
cli = [
"browser"
"clarify"
"code_execution"
"cronjob"
"delegation"
"file"
"image_gen"
"memory"
"session_search"
"skills"
"terminal"
"todo"
"tts"
"vision"
"web"
];
telegram = [
"browser"
"clarify"
"code_execution"
"cronjob"
"delegation"
"file"
"image_gen"
"memory"
"session_search"
"skills"
"terminal"
"todo"
"tts"
"vision"
"web"
];
};
};
};
}
+23
View File
@@ -0,0 +1,23 @@
{
m3ta.mem0 = {
enable = true;
port = 8000;
host = "127.0.0.1";
# LLM Configuration
llm = {
provider = "openai";
apiKeyFile = "/var/lib/mem0/openai-api-key-1"; # Use agenix or sops-nix
};
# Vector Storage Configuration
vectorStore = {
provider = "qdrant"; # or "chroma", "pinecone", etc.
config = {
host = "localhost";
port = 6333;
collection_name = "mem0_alice";
};
};
};
}
+29
View File
@@ -0,0 +1,29 @@
{pkgs, ...}: {
services.netbird.enable = true;
environment.systemPackages = with pkgs; [netbird-ui];
systemd.services.netbird = {
environment = {
NB_DISABLE_SSH_CONFIG = "true";
};
path = [
pkgs.shadow
pkgs.util-linux
];
};
programs.ssh.extraConfig = ''
Match exec "${pkgs.netbird}/bin/netbird ssh detect %h %p"
PreferredAuthentications password,publickey,keyboard-interactive
PasswordAuthentication yes
PubkeyAuthentication yes
BatchMode no
ProxyCommand ${pkgs.netbird}/bin/netbird ssh proxy %h %p
StrictHostKeyChecking no
UserKnownHostsFile /dev/null
CheckHostIP no
LogLevel ERROR
'';
networking.firewall.checkReversePath = "loose";
}
+1 -3
View File
@@ -1,6 +1,4 @@
{pkgs, ...}: { {
environment.systemPackages = with pkgs; [
];
security.rtkit.enable = true; security.rtkit.enable = true;
services.pipewire = { services.pipewire = {
enable = true; enable = true;
-40
View File
@@ -1,40 +0,0 @@
{
config,
pkgs,
...
}: {
services.tailscale = {
enable = true;
useRoutingFeatures = "client";
};
# systemd.services.tailscale-autoconnect = {
# description = "Automatic connection to Tailscale";
# # make sure tailscale is running before trying to connect to tailscale
# after = ["network-pre.target" "tailscale.service"];
# wants = ["network-pre.target" "tailscale.service"];
# wantedBy = ["multi-user.target"];
# # set this service as a oneshot job
# serviceConfig = {
# Type = "oneshot";
# EnvironmentFile = "${config.age.secrets.tailscale-key.path}";
# };
# # have the job run this shell script
# script = with pkgs; ''
# # wait for tailscaled to settle
# sleep 2
# # check if we are already authenticated to tailscale
# status="$(${tailscale}/bin/tailscale status -json | ${jq}/bin/jq -r .BackendState)"
# if [ $status = "Running" ]; then # if so, then do nothing
# exit 0
# fi
# # otherwise authenticate with tailscale
# ${tailscale}/bin/tailscale up --exit-node 100.88.96.77 --authkey $TAILSCALE_KEY
# '';
# };
}
+4 -2
View File
@@ -1,7 +1,9 @@
{pkgs, ...}: { {pkgs, ...}: {
services.udev.extraRules = '' services.udev.extraRules = ''
SUBSYSTEM=="usb", MODE="0666 SUBSYSTEM=="usb", MODE="0666"
''; SUBSYSTEM=="leds", KERNEL=="rgb:kbd_backlight*", ACTION=="add", RUN+="${pkgs.coreutils}/bin/chmod a+w /sys/class/leds/%k/multi_intensity"
KERNEL=="uinput", MODE="0660", GROUP="input", OPTIONS+="static_node=uinput"
KERNEL=="event*", SUBSYSTEM=="input", MODE="0660", GROUP="input" '';
environment.systemPackages = with pkgs; [ environment.systemPackages = with pkgs; [
zsa-udev-rules zsa-udev-rules
]; ];
+1 -1
View File
@@ -10,7 +10,7 @@
}; };
NO = { NO = {
configFile = config.age.secrets.wg-NO.path; configFile = config.age.secrets.wg-NO.path;
autostart = true; autostart = false;
}; };
US = { US = {
configFile = config.age.secrets.wg-US.path; configFile = config.age.secrets.wg-US.path;
+5 -1
View File
@@ -59,7 +59,11 @@
# List packages installed in system profile. To search, run: # List packages installed in system profile. To search, run:
# $ nix search wget # $ nix search wget
environment.systemPackages = with pkgs; [neovim git]; environment.systemPackages = with pkgs; [
neovim
git
ghostty.terminfo
];
# Some programs need SUID wrappers, can be configured further or are # Some programs need SUID wrappers, can be configured further or are
# started in user sessions. # started in user sessions.
+62 -16
View File
@@ -1,38 +1,84 @@
{ {
age = { age = {
secrets = { secrets = {
baserow-env = { baserow-env = {file = ../../secrets/baserow-env.age;};
file = ../../secrets/baserow-env.age; ghost-env = {file = ../../secrets/ghost-env.age;};
honcho-selfhost-db-password = {
file = ../../secrets/honcho-selfhost-db-password.age;
owner = "postgres";
group = "postgres";
mode = "400";
}; };
ghost-env = { honcho-selfhost-env = {file = ../../secrets/honcho-selfhost-env.age;};
file = ../../secrets/ghost-env.age; honcho-selfhost-jwt-secret = {file = ../../secrets/honcho-selfhost-jwt-secret.age;};
kestra-config = {
file = ../../secrets/kestra-config.age;
mode = "644";
}; };
littlelink-m3tam3re = { kestra-env = {file = ../../secrets/kestra-env.age;};
file = ../../secrets/littlelink-m3tam3re.age; littlelink-m3tam3re = {file = ../../secrets/littlelink-m3tam3re.age;};
minio-root-cred = {file = ../../secrets/minio-root-cred.age;};
rustfs-access-key = {file = ../../secrets/rustfs-access-key.age;};
rustfs-secret-key = {file = ../../secrets/rustfs-secret-key.age;};
n8n-env = {file = ../../secrets/n8n-env.age;};
netbird-auth-secret = {
file = ../../secrets/netbird-auth-secret.age;
}; };
minio-root-cred = { netbird-db-password = {
file = ../../secrets/minio-root-cred.age; file = ../../secrets/netbird-db-password.age;
}; };
n8n-env = { netbird-encryption-key = {
file = ../../secrets/n8n-env.age; file = ../../secrets/netbird-encryption-key.age;
}; };
restreamer-env = { netbird-dashboard-env = {
file = ../../secrets/restreamer-env.age; file = ../../secrets/netbird-dashboard-env.age;
}; };
searx = { netbird-server-env = {
file = ../../secrets/searx.age; file = ../../secrets/netbird-server-env.age;
}; };
tailscale-key = { netbird-proxy-env = {
file = ../../secrets/tailscale-key.age; file = ../../secrets/netbird-proxy-env.age;
};
paperless-key = {file = ../../secrets/paperless-key.age;};
restreamer-env = {file = ../../secrets/restreamer-env.age;};
searx = {file = ../../secrets/searx.age;};
tailscale-key = {file = ../../secrets/tailscale-key.age;};
tuwunel-registration-token = {
file = ../../secrets/tuwunel-registration-token.age;
owner = "tuwunel";
}; };
traefik = { traefik = {
file = ../../secrets/traefik.age; file = ../../secrets/traefik.age;
owner = "traefik"; owner = "traefik";
}; };
vaultwarden-env = {file = ../../secrets/vaultwarden-env.age;};
m3tam3re-secrets = { m3tam3re-secrets = {
file = ../../secrets/m3tam3re-secrets.age; file = ../../secrets/m3tam3re-secrets.age;
owner = "m3tam3re"; owner = "m3tam3re";
}; };
gitea-runner-token = {
file = ../../secrets/gitea-runner-token.age;
mode = "600";
owner = "gitea-runner";
group = "gitea-runner";
};
ref-key = {
file = ../../secrets/ref-key.age;
owner = "m3tam3re";
};
exa-key = {
file = ../../secrets/exa-key.age;
owner = "m3tam3re";
};
basecamp-client-id = {
file = ../../secrets/basecamp-client-id.age;
owner = "m3tam3re";
};
basecamp-client-secret = {
file = ../../secrets/basecamp-client-secret.age;
owner = "m3tam3re";
};
authentik-env = {file = ../../secrets/authentik-env.age;};
}; };
}; };
} }
@@ -0,0 +1,85 @@
# CONTAINER SERVICES (m3-atlas)
**Container orchestration with Podman + Traefik reverse proxy**
## OVERVIEW
11 containerized services on dedicated `web` network (10.89.0.0/24) with Traefik SSL termination.
## STRUCTURE
```
containers/
├── default.nix # Network setup + service imports
├── baserow.nix # 10.89.0.10 - No-code database
├── ghost.nix # 10.89.0.11 - Blog platform
├── kestra.nix # 10.89.0.12 - Workflow orchestration
├── littlelink.nix # 10.89.0.13 - Link aggregator
├── matomo.nix # 10.89.0.14 - Analytics
├── restreamer.nix # 10.89.0.15 - Video streaming
├── slash.nix # 10.89.0.16 - Link shortener
└── slash-nemoti.nix # 10.89.0.17 - Personal link shortener
```
## WHERE TO LOOK
| Task | Action | Notes |
|------|--------|-------|
| Add container | Copy existing .nix, increment IP | Must update default.nix imports |
| Fix networking | Check IP conflicts in 10.89.0.0/24 | Gateway always 10.89.0.1 |
| Debug Traefik | Check router rules in service file | Domain must match DNS |
| Access database | Use `--add-host=mysql:10.89.0.1` | Gateway IP for host services |
## CONVENTIONS
### Container Definition Template
```nix
virtualisation.oci-containers.containers.<name> = {
image = "registry/image:tag";
ports = ["127.0.0.1:<external>:<internal>"];
volumes = ["/var/lib/<service>:/data"];
environmentFiles = [config.age.secrets.<name>-env.path];
extraOptions = [
"--network=web"
"--ip=10.89.0.<sequential>"
"--add-host=mysql:10.89.0.1" # If DB needed
];
};
```
### Traefik Integration
```nix
services.traefik.dynamicConfigOptions.http = {
services.<name>.loadBalancer.servers = [{
url = "http://127.0.0.1:<port>";
}];
routers.<name> = {
rule = "Host(`<subdomain>.m3ta.dev`)";
service = "<name>";
tls.certResolver = "godaddy";
};
# Legacy redirect (if needed)
routers.<name>-old = {
rule = "Host(`<subdomain>.m3tam3re.com`)";
service = "<name>";
middlewares = ["redirect-m3ta"];
};
};
```
### IP Allocation
- **10.89.0.1**: Gateway (host)
- **10.89.0.10-17**: Assigned containers
- **10.89.0.18+**: Available for new services
## ANTI-PATTERNS
- **DON'T** expose ports publicly - bind to 127.0.0.1 only
- **DON'T** skip static IP assignment - routing breaks without it
- **DON'T** hardcode secrets - use age-encrypted env files
- **DON'T** forget to add imports to default.nix
## NOTES
- Network created via activation script in default.nix
- All services behind Traefik - no direct external access
- MySQL/PostgreSQL run on host, accessed via gateway IP
- Secrets pattern: `<service>-env.age` with environment variables
@@ -0,0 +1,67 @@
{config, ...}: let
image = "ghcr.io/goauthentik/server:2026.2.0";
serverIp = "10.89.0.22";
workerIp = "10.89.0.23";
postgresHost = "10.89.0.1";
postgresPort = config.m3ta.ports.get "postgres";
authentikPort = config.m3ta.ports.get "authentik";
sharedEnv = {
AUTHENTIK_POSTGRESQL__HOST = postgresHost;
AUTHENTIK_POSTGRESQL__PORT = toString postgresPort;
AUTHENTIK_POSTGRESQL__USER = "authentik";
AUTHENTIK_POSTGRESQL__NAME = "authentik";
};
in {
virtualisation.oci-containers.containers = {
"authentik-server" = {
inherit image;
cmd = ["server"];
environment = sharedEnv;
environmentFiles = [config.age.secrets.authentik-env.path];
ports = ["127.0.0.1:${toString authentikPort}:9000"];
volumes = [
"authentik_media:/media"
"authentik_templates:/templates"
];
extraOptions = [
"--add-host=postgres:${postgresHost}"
"--ip=${serverIp}"
"--network=web"
];
};
"authentik-worker" = {
inherit image;
cmd = ["worker"];
user = "root";
environment = sharedEnv;
environmentFiles = [config.age.secrets.authentik-env.path];
volumes = [
"authentik_media:/media"
"authentik_certs:/certs"
"authentik_templates:/templates"
];
extraOptions = [
"--add-host=postgres:${postgresHost}"
"--ip=${workerIp}"
"--network=web"
];
};
};
services.traefik.dynamicConfigOptions.http = {
services.authentik.loadBalancer.servers = [
{url = "http://localhost:${toString authentikPort}/";}
];
routers.authentik = {
rule = "Host(`auth.m3ta.dev`)";
tls = {certResolver = "godaddy";};
service = "authentik";
entrypoints = "websecure";
};
};
}
@@ -1,8 +1,8 @@
{config, ...}: { {config, ...}: {
virtualisation.oci-containers.containers."baserow" = { virtualisation.oci-containers.containers."baserow" = {
image = "docker.io/baserow/baserow:1.31.1"; image = "docker.io/baserow/baserow:2.0.6";
environmentFiles = [config.age.secrets.baserow-env.path]; environmentFiles = [config.age.secrets.baserow-env.path];
ports = ["127.0.0.1:3001:80"]; ports = ["127.0.0.1:${toString (config.m3ta.ports.get "baserow")}:80"];
volumes = ["baserow_data:/baserow/data"]; volumes = ["baserow_data:/baserow/data"];
extraOptions = ["--add-host=postgres:10.89.0.1" "--ip=10.89.0.10" "--network=web"]; extraOptions = ["--add-host=postgres:10.89.0.1" "--ip=10.89.0.10" "--network=web"];
}; };
@@ -10,7 +10,7 @@
services.traefik.dynamicConfigOptions.http = { services.traefik.dynamicConfigOptions.http = {
services.baserow.loadBalancer.servers = [ services.baserow.loadBalancer.servers = [
{ {
url = "http://localhost:3001/"; url = "http://localhost:${toString (config.m3ta.ports.get "baserow")}/";
} }
]; ];
@@ -2,12 +2,17 @@
imports = [ imports = [
./baserow.nix ./baserow.nix
./ghost.nix ./ghost.nix
./honcho.nix
./kestra.nix
./littlelink.nix ./littlelink.nix
./matomo.nix ./matomo.nix
./netbird.nix
# ./n8n.nix # ./n8n.nix
# ./pangolin.nix # ./pangolin.nix
./restreamer.nix ./restreamer.nix
./slash.nix ./slash.nix
./slash-nemoti.nix
./authentik.nix
]; ];
system.activationScripts.createPodmanNetworkWeb = lib.mkAfter '' system.activationScripts.createPodmanNetworkWeb = lib.mkAfter ''
if ! /run/current-system/sw/bin/podman network exists web; then if ! /run/current-system/sw/bin/podman network exists web; then
@@ -0,0 +1,209 @@
{
config,
lib,
pkgs,
...
}: let
serviceName = "honcho";
image = "ghcr.io/plastic-labs/honcho:v3.0.6";
apiIp = "10.89.0.24";
deriverIp = "10.89.0.25";
redisIp = "10.89.0.26";
postgresHost = "10.89.0.1";
postgresPort = config.m3ta.ports.get "postgres";
honchoPort = config.m3ta.ports.get "honcho";
# m3-atlas Netbird mesh address, discovered from `netbird status -d`.
# Binding the host port here keeps self-hosted Honcho off public interfaces.
netbirdBindAddress = "100.81.142.56";
netbirdRange = "100.64.0.0/16";
dbName = "honcho";
dbUser = "honcho";
redisName = "${serviceName}-redis";
runtimeDirectory = "/run/${serviceName}";
runtimeEnvFile = "${runtimeDirectory}/env";
# Keep auth disabled for the first deployment because Honcho clients need
# generated JWTs. The JWT secret is still provisioned so enabling auth later is
# a one-line change here plus client token generation.
authUseAuth = false;
sharedEnvironment = {
CACHE_ENABLED = "true";
CACHE_URL = "redis://${redisName}:6379/0?suppress=true";
LOG_LEVEL = "INFO";
TELEMETRY_ENABLED = "false";
VECTOR_STORE_MIGRATED = "false";
VECTOR_STORE_TYPE = "pgvector";
AUTH_USE_AUTH = lib.boolToString authUseAuth;
};
sharedEnvironmentFiles = [
runtimeEnvFile
config.age.secrets."${serviceName}-selfhost-env".path
];
webNetwork = ip: [
"--add-host=postgres:${postgresHost}"
"--network=web:ip=${ip}"
];
# The shared web network is intentionally internal. API and deriver also join
# this egress-only network so LLM provider calls can leave the host without
# exposing any extra inbound ports.
networksWithEgress = ip:
(webNetwork ip)
++ [
"--network=${serviceName}-egress"
];
apiHealthCmd = ''/app/.venv/bin/python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/health', timeout=2).read()"'';
in {
system.activationScripts.createPodmanNetworkHonchoEgress = lib.mkAfter ''
if ! /run/current-system/sw/bin/podman network exists ${serviceName}-egress; then
/run/current-system/sw/bin/podman network create ${serviceName}-egress
fi
'';
virtualisation.oci-containers.containers = {
"${serviceName}-redis" = {
image = "docker.io/redis:8.2";
autoStart = true;
volumes = ["${serviceName}_redis_data:/data"];
extraOptions =
(webNetwork redisIp)
++ [
"--health-cmd=redis-cli ping"
"--health-interval=5s"
"--health-timeout=5s"
"--health-retries=5"
];
};
"${serviceName}-api" = {
inherit image;
autoStart = true;
entrypoint = "sh";
cmd = ["docker/entrypoint.sh"];
environment = sharedEnvironment;
environmentFiles = sharedEnvironmentFiles;
ports = ["${netbirdBindAddress}:${toString honchoPort}:8000"];
dependsOn = [redisName];
extraOptions =
(networksWithEgress apiIp)
++ [
"--health-cmd=${apiHealthCmd}"
"--health-interval=5s"
"--health-timeout=5s"
"--health-retries=5"
"--health-start-period=10s"
];
};
"${serviceName}-deriver" = {
inherit image;
autoStart = true;
entrypoint = "/app/.venv/bin/python";
cmd = ["-m" "src.deriver"];
environment = sharedEnvironment;
environmentFiles = sharedEnvironmentFiles;
dependsOn = ["${serviceName}-api" redisName];
extraOptions = networksWithEgress deriverIp;
};
};
systemd.services = {
"${serviceName}-postgres-bootstrap" = {
description = "Bootstrap Honcho PostgreSQL role, database, password, and pgvector";
after = ["postgresql.service" "agenix.service"];
requires = ["postgresql.service" "agenix.service"];
before = ["${serviceName}-env.service" "podman-${serviceName}-api.service" "podman-${serviceName}-deriver.service"];
requiredBy = ["podman-${serviceName}-api.service" "podman-${serviceName}-deriver.service"];
path = [
config.services.postgresql.package
pkgs.coreutils
];
serviceConfig = {
Type = "oneshot";
User = "postgres";
Group = "postgres";
};
script = ''
set -euo pipefail
test -s ${config.age.secrets."${serviceName}-selfhost-db-password".path}
psql -v ON_ERROR_STOP=1 --dbname=postgres <<'SQL'
DO $$
BEGIN
CREATE ROLE ${dbUser} LOGIN;
EXCEPTION WHEN duplicate_object THEN
NULL;
END
$$;
SELECT 'CREATE DATABASE ${dbName} OWNER ${dbUser}'
WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = '${dbName}')\gexec
ALTER DATABASE ${dbName} OWNER TO ${dbUser};
\set honcho_password `cat ${config.age.secrets."${serviceName}-selfhost-db-password".path}`
ALTER ROLE ${dbUser} WITH LOGIN PASSWORD :'honcho_password';
SQL
psql -v ON_ERROR_STOP=1 --dbname=${dbName} <<'SQL'
CREATE EXTENSION IF NOT EXISTS vector;
GRANT ALL PRIVILEGES ON DATABASE ${dbName} TO ${dbUser};
SQL
'';
};
"${serviceName}-env" = {
description = "Generate Honcho runtime environment file with agenix secrets";
after = ["agenix.service" "${serviceName}-postgres-bootstrap.service"];
requires = ["agenix.service" "${serviceName}-postgres-bootstrap.service"];
before = ["podman-${serviceName}-api.service" "podman-${serviceName}-deriver.service"];
requiredBy = ["podman-${serviceName}-api.service" "podman-${serviceName}-deriver.service"];
path = [
pkgs.coreutils
pkgs.python3
];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
};
script = ''
set -euo pipefail
install -d -m 0750 ${runtimeDirectory}
db_password_encoded=$(
python3 -c 'import sys, urllib.parse; print(urllib.parse.quote(sys.stdin.read().strip(), safe=""))' \
< ${config.age.secrets."${serviceName}-selfhost-db-password".path}
)
jwt_secret=$(tr -d '\r\n' < ${config.age.secrets."${serviceName}-selfhost-jwt-secret".path})
umask 077
cat > ${runtimeEnvFile} <<ENV
DB_CONNECTION_URI=postgresql+psycopg://${dbUser}:$db_password_encoded@postgres:${toString postgresPort}/${dbName}
AUTH_JWT_SECRET=$jwt_secret
ENV
'';
};
"podman-${serviceName}-api" = {
after = ["${serviceName}-env.service" "${serviceName}-postgres-bootstrap.service"];
requires = ["${serviceName}-env.service" "${serviceName}-postgres-bootstrap.service"];
};
"podman-${serviceName}-deriver" = {
after = ["${serviceName}-env.service" "${serviceName}-postgres-bootstrap.service"];
requires = ["${serviceName}-env.service" "${serviceName}-postgres-bootstrap.service"];
};
};
networking.firewall.extraCommands = ''
# Self-hosted Honcho API: only Netbird mesh peers may reach ${netbirdBindAddress}:${toString honchoPort}.
ip46tables -A nixos-fw -p tcp --dport ${toString honchoPort} -s ${netbirdRange} -j nixos-fw-accept
'';
}
@@ -0,0 +1,32 @@
{config, ...}: {
virtualisation.oci-containers.containers."kestra" = {
image = "docker.io/kestra/kestra:latest";
environmentFiles = [config.age.secrets.kestra-env.path];
cmd = ["server" "standalone" "--config" "/etc/config/application.yaml"];
ports = ["127.0.0.1:3018:8080"];
user = "root";
volumes = [
"/var/run/docker.sock:/var/run/docker.sock"
"${config.age.secrets.kestra-config.path}:/etc/config/application.yaml"
"kestra_data:/app/storage"
"/tmp/kestra-wd:/tmp/kestra-wd"
];
extraOptions = ["--add-host=postgres:10.89.0.1" "--ip=10.89.0.18" "--network=web"];
};
systemd.tmpfiles.rules = [
"d /tmp/kestra-wd 0750 1000 1000 - -"
];
# Traefik configuration specific to littlelink
services.traefik.dynamicConfigOptions.http = {
services.kestra.loadBalancer.servers = [{url = "http://localhost:3018/";}];
routers.kestra = {
rule = "Host(`k.m3ta.dev`)";
tls = {certResolver = "godaddy";};
service = "kestra";
entrypoints = "websecure";
};
};
}
@@ -0,0 +1,244 @@
{
config,
pkgs,
...
}: let
serviceName = "netbird";
stunPort = config.m3ta.ports.get "netbird-stun";
proxyTlsPort = config.m3ta.ports.get "netbird-proxy";
metricsPort = config.m3ta.ports.get "netbird-metrics";
healthPort = config.m3ta.ports.get "netbird-health";
postgresPort = config.m3ta.ports.get "postgres";
wireguardPort = config.m3ta.ports.get "wireguard";
domain = "v.m3ta.dev";
proxyDomain = "p.m3ta.dev";
ipBase = "10.89.0";
ipOffset = 50;
dashboardIp = "${ipBase}.${toString ipOffset}";
serverIp = "${ipBase}.${toString (ipOffset + 1)}";
proxyIp = "${ipBase}.${toString (ipOffset + 2)}";
# Database configuration
dbName = "netbird";
dbUser = "netbird";
dbHost = "${ipBase}.1";
# NetBird config as Nix attribute set
netbirdConfig = {
server = {
listenAddress = ":80";
exposedAddress = "https://${domain}:443";
stunPorts = [stunPort];
metricsPort = metricsPort;
healthcheckAddress = ":${toString healthPort}";
logLevel = "info";
logFile = "console";
dataDir = "/var/lib/netbird";
auth = {
issuer = "https://${domain}/oauth2";
localAuthDisabled = true;
signKeyRefreshEnabled = true;
dashboardRedirectURIs = [
"https://${domain}/nb-auth"
"https://${domain}/nb-silent-auth"
];
cliRedirectURIs = ["http://localhost:53000/"];
};
reverseProxy = {
trustedHTTPProxies = ["${ipBase}.1/32"];
};
# Proxy feature
proxy = {
enabled = true;
domain = proxyDomain;
};
store = {
engine = "postgres";
postgres = {
host = dbHost;
port = postgresPort;
database = dbName;
username = dbUser;
};
};
};
};
# Generate YAML from Nix attribute set
yamlFormat = pkgs.formats.yaml {};
configYamlBase = yamlFormat.generate "netbird-config-base.yaml" netbirdConfig;
# Script that injects secrets at runtime
configGenScript = pkgs.writeShellScript "netbird-gen-config" ''
set -euo pipefail
AUTH_SECRET=$(cat "$1")
DB_PASSWORD=$(cat "$2")
ENCRYPTION_KEY=$(cat "$3")
${pkgs.yq-go}/bin/yq eval "
.server.authSecret = \"$AUTH_SECRET\" |
.server.store.encryptionKey = \"$ENCRYPTION_KEY\" |
.server.store.postgres.password = \"$DB_PASSWORD\"
" ${configYamlBase}
'';
in {
age.secrets."${serviceName}-auth-secret".file = ../../../../secrets/${serviceName}-auth-secret.age;
age.secrets."${serviceName}-db-password".file = ../../../../secrets/${serviceName}-db-password.age;
age.secrets."${serviceName}-encryption-key".file = ../../../../secrets/${serviceName}-encryption-key.age;
age.secrets."${serviceName}-dashboard-env".file = ../../../../secrets/${serviceName}-dashboard-env.age;
age.secrets."${serviceName}-server-env".file = ../../../../secrets/${serviceName}-server-env.age;
age.secrets."${serviceName}-proxy-env".file = ../../../../secrets/${serviceName}-proxy-env.age;
# Oneshot systemd service that generates the config with injected secrets
systemd.services."${serviceName}-config" = {
description = "Generate NetBird config with secrets";
wantedBy = ["multi-user.target"];
before = ["podman-${serviceName}-server.service"];
requiredBy = ["podman-${serviceName}-server.service"];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
ExecStart = pkgs.writeShellScript "netbird-write-config" ''
mkdir -p /var/lib/${serviceName}
${configGenScript} \
${config.age.secrets."${serviceName}-auth-secret".path} \
${config.age.secrets."${serviceName}-db-password".path} \
${config.age.secrets."${serviceName}-encryption-key".path} \
> /var/lib/${serviceName}/config.yaml
chmod 600 /var/lib/${serviceName}/config.yaml
'';
};
};
virtualisation.oci-containers.containers = {
"${serviceName}-dashboard" = {
image = "netbirdio/dashboard:latest";
autoStart = true;
environmentFiles = [config.age.secrets."${serviceName}-dashboard-env".path];
extraOptions = [
"--ip=${dashboardIp}"
"--network=web"
];
};
"${serviceName}-server" = {
image = "netbirdio/netbird-server:latest";
autoStart = true;
ports = ["${toString stunPort}:${toString stunPort}/udp"];
environmentFiles = [config.age.secrets."${serviceName}-server-env".path];
volumes = [
"${serviceName}_data:/var/lib/netbird"
"/var/lib/${serviceName}/config.yaml:/etc/netbird/config.yaml:ro"
];
cmd = ["--config" "/etc/netbird/config.yaml"];
extraOptions = [
"--ip=${serverIp}"
"--network=web"
];
};
"${serviceName}-proxy" = {
image = "netbirdio/reverse-proxy:latest";
autoStart = true;
ports = ["${toString wireguardPort}:${toString wireguardPort}/udp"];
volumes = [
"${serviceName}_proxy_certs:/certs"
];
environmentFiles = [config.age.secrets."${serviceName}-proxy-env".path];
cmd = [
"--domain=${proxyDomain}"
"--mgmt=https://${domain}:443"
"--addr=:${toString proxyTlsPort}"
"--cert-dir=/certs"
"--acme-certs"
"--trusted-proxies=${ipBase}.1/32"
];
dependsOn = ["${serviceName}-server"];
extraOptions = [
"--ip=${proxyIp}"
"--network=web"
];
};
};
services.traefik.dynamicConfigOptions = {
# HTTP services and routers
http = {
services = {
"${serviceName}-dashboard".loadBalancer.servers = [
{url = "http://${dashboardIp}:80/";}
];
"${serviceName}-server".loadBalancer.servers = [
{url = "http://${serverIp}:80/";}
];
"${serviceName}-server-h2c".loadBalancer.servers = [
{url = "h2c://${serverIp}:80";}
];
};
routers = {
# gRPC (Signal + Management)
"${serviceName}-grpc" = {
rule = "Host(`${domain}`) && (PathPrefix(`/signalexchange.SignalExchange/`) || PathPrefix(`/management.ManagementService/`) || PathPrefix(`/management.ProxyService/`))";
entrypoints = "websecure";
tls.certResolver = "godaddy";
service = "${serviceName}-server-h2c";
priority = 100;
};
# Backend (relay, WebSocket, API, OAuth2)
"${serviceName}-backend" = {
rule = "Host(`${domain}`) && (PathPrefix(`/relay`) || PathPrefix(`/ws-proxy/`) || PathPrefix(`/api`) || PathPrefix(`/oauth2`))";
entrypoints = "websecure";
tls.certResolver = "godaddy";
service = "${serviceName}-server";
priority = 100;
};
# Dashboard (catch-all, lowest priority)
"${serviceName}-dashboard" = {
rule = "Host(`${domain}`)";
entrypoints = "websecure";
tls.certResolver = "godaddy";
service = "${serviceName}-dashboard";
priority = 1;
};
};
};
# TCP for proxy TLS passthrough
tcp = {
services."${serviceName}-proxy-tls".loadBalancer.servers = [
{address = "${proxyIp}:${toString proxyTlsPort}";}
];
routers."${serviceName}-proxy-passthrough" = {
entryPoints = ["websecure"];
rule = "HostSNI(`*`)";
service = "${serviceName}-proxy-tls";
priority = 1;
tls.passthrough = true;
};
};
# ServersTransport for Proxy Protocol v2 (optional)
serversTransports."pp-v2" = {
proxyProtocol.version = 2;
};
};
networking.firewall.allowedUDPPorts = [
stunPort # STUN
wireguardPort # WireGuard for proxy
];
}
@@ -1,211 +0,0 @@
{
config,
pkgs,
lib,
...
}: let
# Define the Pangolin configuration as a Nix attribute set
pangolinConfig = {
app = {
dashboard_url = "https://vpn.m3tam3re.com";
log_level = "info";
save_logs = false;
};
domains = {
vpn = {
base_domain = "m3tam3re.com";
cert_resolver = "godaddy";
prefer_wildcard_cert = false;
};
};
server = {
external_port = 3000;
internal_port = 3001;
next_port = 3002;
internal_hostname = "pangolin";
session_cookie_name = "p_session_token";
resource_access_token_param = "p_token";
resource_session_request_param = "p_session_request";
};
traefik = {
cert_resolver = "godaddy";
http_entrypoint = "web";
https_entrypoint = "websecure";
};
gerbil = {
start_port = 51820;
base_endpoint = "vpn.m3tam3re.com";
use_subdomain = false;
block_size = 24;
site_block_size = 30;
subnet_group = "100.89.137.0/20";
};
rate_limits = {
global = {
window_minutes = 1;
max_requests = 100;
};
};
email = {
smtp_host = config.age.secrets.smtp-host.path;
smtp_port = 587;
smtp_user = config.age.secrets.smtp-user.path;
smtp_pass = config.age.secrets.smtp-pass.path;
no_reply = config.age.secrets.smtp-user.path;
};
users = {
server_admin = {
email = "admin@m3tam3re.com";
password = config.age.secrets.pangolin-admin-password.path;
};
};
flags = {
require_email_verification = true;
disable_signup_without_invite = true;
disable_user_create_org = true;
allow_raw_resources = true;
allow_base_domain_resources = true;
};
};
# Convert Nix attribute set to YAML using a simpler approach
pangolinConfigYaml = pkgs.writeTextFile {
name = "config.yml";
text = lib.generators.toYAML {} pangolinConfig;
};
in {
# Define the containers
virtualisation.oci-containers.containers = {
"pangolin" = {
image = "fosrl/pangolin:1.1.0";
autoStart = true;
volumes = [
"${pangolinConfigYaml}:/app/config/config.yml:ro" # Mount the config file directly
"pangolin_config:/app/config/data" # Volume for persistent data
];
ports = [
"127.0.0.1:3020:3001" # API server
"127.0.0.1:3021:3002" # Next.js server
"127.0.0.1:3022:3000" # API/WebSocket server
];
extraOptions = ["--ip=10.89.0.20" "--network=web"];
};
"gerbil" = {
image = "fosrl/gerbil:1.0.0";
autoStart = true;
volumes = [
"pangolin_config:/var/config" # Share the volume for persistent data
];
cmd = [
"--reachableAt=http://gerbil:3003"
"--generateAndSaveKeyTo=/var/config/key"
"--remoteConfig=http://pangolin:3001/api/v1/gerbil/get-config"
"--reportBandwidthTo=http://pangolin:3001/api/v1/gerbil/receive-bandwidth"
];
ports = [
"51820:51820/udp" # WireGuard port
];
extraOptions = [
"--ip=10.89.0.21"
"--network=web"
"--cap-add=NET_ADMIN"
"--cap-add=SYS_MODULE"
];
};
};
# Secrets for Pangolin
# age.secrets = {
# "smtp-host" = {
# file = ../secrets/smtp-host.age;
# owner = "root";
# group = "root";
# mode = "0400";
# };
# "smtp-user" = {
# file = ../secrets/smtp-user.age;
# owner = "root";
# group = "root";
# mode = "0400";
# };
# "smtp-pass" = {
# file = ../secrets/smtp-pass.age;
# owner = "root";
# group = "root";
# mode = "0400";
# };
# "pangolin-admin-password" = {
# file = ../secrets/pangolin-admin-password.age;
# owner = "root";
# group = "root";
# mode = "0400";
# };
# };
# Traefik configuration for Pangolin
services.traefik.dynamicConfigOptions = {
http = {
# Next.js service (front-end)
services.pangolin-next-service.loadBalancer.servers = [
{url = "http://localhost:3021";}
];
# API service
services.pangolin-api-service.loadBalancer.servers = [
{url = "http://localhost:3022";}
];
# Routers
routers = {
# Next.js router (handles everything except API paths)
"pangolin-next" = {
rule = "Host(`vpn.m3tam3re.com`) && !PathPrefix(`/api/v1`)";
service = "pangolin-next-service";
entrypoints = ["websecure"];
tls = {
certResolver = "godaddy";
};
};
# API router
"pangolin-api" = {
rule = "Host(`vpn.m3tam3re.com`) && PathPrefix(`/api/v1`)";
service = "pangolin-api-service";
entrypoints = ["websecure"];
tls = {
certResolver = "godaddy";
};
};
};
};
};
# Add HTTP provider to Traefik for dynamic configuration from Pangolin
services.traefik.staticConfigOptions.providers.http = {
endpoint = "http://localhost:3020/api/v1/traefik-config";
pollInterval = "5s";
};
# Add experimental section for Badger plugin
services.traefik.staticConfigOptions.experimental = {
plugins = {
#TODO create an overlay for the plugin
badger = {
moduleName = "github.com/fosrl/badger";
version = "v1.0.0";
};
};
};
# Firewall configuration for WireGuard
networking.firewall.allowedUDPPorts = [51820]; # WireGuard port
}
@@ -4,7 +4,7 @@
environmentFiles = [config.age.secrets.restreamer-env.path]; environmentFiles = [config.age.secrets.restreamer-env.path];
# Modified ports to include RTMPS # Modified ports to include RTMPS
ports = [ ports = [
"127.0.0.1:3006:8080" # Web UI "127.0.0.1:${toString (config.m3ta.ports.get "restreamer")}:8080" # Web UI
"127.0.0.1:1936:1935" # RTMP "127.0.0.1:1936:1935" # RTMP
]; ];
volumes = [ volumes = [
@@ -20,7 +20,7 @@
http = { http = {
services.restreamer.loadBalancer.servers = [ services.restreamer.loadBalancer.servers = [
{ {
url = "http://localhost:3006/"; url = "http://localhost:${toString (config.m3ta.ports.get "restreamer")}/";
} }
]; ];
@@ -0,0 +1,27 @@
{
virtualisation.oci-containers.containers."slash-nemoti" = {
image = "docker.io/yourselfhosted/slash:latest";
ports = ["127.0.0.1:3016:5231"];
volumes = [
"slash-nemoti_data:/var/opt/slash"
];
extraOptions = ["--ip=10.89.0.17" "--network=web"];
};
# Traefik configuration specific to littlelink
services.traefik.dynamicConfigOptions.http = {
services.slash-nemoti.loadBalancer.servers = [
{
url = "http://localhost:3016/";
}
];
routers.slash-nemoti = {
rule = "Host(`l.nemoti.art`)";
tls = {
certResolver = "godaddy";
};
service = "slash-nemoti";
entrypoints = "websecure";
};
};
}
+6 -3
View File
@@ -1,15 +1,18 @@
{ {
imports = [ imports = [
./tuwunel.nix
./containers ./containers
./gitea.nix ./gitea.nix
./headscale.nix ./gitea-actions-runner.nix
./minio.nix ./rustfs.nix
./mysql.nix ./mysql.nix
./netbird.nix
./n8n.nix ./n8n.nix
./paperless.nix
./postgres.nix ./postgres.nix
./searx.nix ./searx.nix
./tailscale.nix
./traefik.nix ./traefik.nix
./vaultwarden.nix
./wastebin.nix ./wastebin.nix
]; ];
} }
@@ -0,0 +1,57 @@
{
config,
pkgs,
...
}: {
services.gitea-actions-runner = {
instances.default = {
enable = true;
name = "${config.networking.hostName}-runner";
url = "https://code.m3ta.dev";
tokenFile = config.age.secrets.gitea-runner-token.path;
# nixos:host is primary, ubuntu is fallback
labels = [
"nixos:host"
];
# Host execution packages
hostPackages = with pkgs; [
bash
curl
coreutils
git
jq
nix
nix-update
nodejs
# Add any other tools you need for nix-update workflows
];
# Advanced settings
settings = {
runner = {
capacity = 4; # One job at a time (increase if you have resources)
timeout = "4h"; # Nix builds can take a while
};
cache = {enabled = true;};
container = {
enable_ipv6 = true;
privileged = false;
};
};
};
};
# User management (auto-created by module, but ensuring proper setup)
users.users.gitea-runner = {
home = "/var/lib/gitea-runner";
group = "gitea-runner";
isSystemUser = true;
createHome = true;
};
users.groups.gitea-runner = {};
# Firewall: Allow Podman bridge networks for cache actions
networking.firewall.trustedInterfaces = ["br-+"];
}
+7 -3
View File
@@ -1,8 +1,12 @@
{ {config, ...}: {
services.gitea = { services.gitea = {
enable = true; enable = true;
settings = { settings = {
server.ROOT_URL = "https://code.m3ta.dev"; server = {
ROOT_URL = "https://code.m3ta.dev";
HTTP_PORT = config.m3ta.ports.get "gitea";
};
mailer.SENDMAIL_PATH = "/run/wrappers/bin/sendmail";
service.DISABLE_REGISTRATION = true; service.DISABLE_REGISTRATION = true;
}; };
lfs.enable = true; lfs.enable = true;
@@ -17,7 +21,7 @@
services.traefik.dynamicConfigOptions.http = { services.traefik.dynamicConfigOptions.http = {
services.gitea.loadBalancer.servers = [ services.gitea.loadBalancer.servers = [
{ {
url = "http://localhost:3000/"; url = "http://localhost:${toString (config.m3ta.ports.get "gitea")}/";
} }
]; ];
-121
View File
@@ -1,121 +0,0 @@
{
pkgs,
config,
lib,
...
}: {
# Define a new option for the admin user
options.services.headscale = {
adminUser = lib.mkOption {
type = lib.types.str;
default = "m3tam3re";
description = "Username for the headscale admin user";
};
};
config = let
adminUser = config.services.headscale.adminUser;
aclConfig = {
# Groups definition
groups = {
"group:admins" = ["${adminUser}"];
};
acls = [
# Allow all connections within the tailnet
{
action = "accept";
src = ["*"];
dst = ["*:*"];
}
# Allow admin to connect to their own services
{
action = "accept";
src = ["${adminUser}"];
dst = ["${adminUser}:*"];
}
];
# Auto-approvers section for routes
autoApprovers = {
routes = {
"0.0.0.0/0" = ["${adminUser}"];
"10.0.0.0/8" = ["${adminUser}"];
"172.16.0.0/12" = ["${adminUser}"];
"192.168.0.0/16" = ["${adminUser}"];
};
exitNode = ["${adminUser}"];
};
};
# Convert to HuJSON format with comments
aclHuJson = ''
// Headscale ACL Policy - Generated by NixOS
// Admin user: ${adminUser}
${builtins.toJSON aclConfig}
'';
aclFile = pkgs.writeText "acl-policy.hujson" aclHuJson;
in {
services = {
headscale = {
enable = true;
port = 3009;
adminUser = "m3tam3re";
settings = {
server_url = "https://va.m3tam3re.com";
dns = {
base_domain = "m3ta.loc";
};
logtail.enabled = false;
policy.path = "${aclFile}";
};
};
};
# Traefik configuration
services.traefik.dynamicConfigOptions.http = {
services.headscale.loadBalancer.servers = [
{
url = "http://localhost:3009/";
}
];
routers.headscale = {
rule = "Host(`va.m3tam3re.com`)";
tls = {
certResolver = "godaddy";
};
service = "headscale";
entrypoints = "websecure";
};
};
# Create a systemd service to ensure the admin user exists
systemd.services.headscale-ensure-admin = lib.mkIf config.services.headscale.enable {
description = "Ensure Headscale admin user exists";
after = ["headscale.service"];
requires = ["headscale.service"];
wantedBy = ["multi-user.target"];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
User = "headscale";
Group = "headscale";
};
script = ''
# Check if user exists and create if needed
if ! ${pkgs.headscale}/bin/headscale users list | grep -q "${adminUser}"; then
echo "Creating headscale admin user: ${adminUser}"
${pkgs.headscale}/bin/headscale users create "${adminUser}"
else
echo "Headscale admin user ${adminUser} already exists"
fi
'';
};
};
}
+6 -1
View File
@@ -18,5 +18,10 @@
calendar = "03:00:00"; calendar = "03:00:00";
databases = ["ghost" "matomo"]; databases = ["ghost" "matomo"];
}; };
networking.firewall.allowedTCPPorts = [3306]; networking.firewall = {
extraCommands = ''
iptables -A INPUT -p tcp -s 127.0.0.1 --dport 3306 -j ACCEPT
iptables -A INPUT -p tcp -s 10.89.0.0/24 --dport 3306 -j ACCEPT
'';
};
} }
+10 -2
View File
@@ -1,8 +1,16 @@
{config, ...}: { {
config,
lib,
...
}: {
services.n8n = { services.n8n = {
enable = true; enable = true;
webhookUrl = "https://wf.m3tam3re.com"; environment.WEBHOOK_URL = "https://wf.m3tam3re.com";
}; };
# Temporary fix for upstream module
systemd.services.n8n.serviceConfig.LoadCredential = lib.mkForce [];
systemd.services.n8n.environment.N8N_RUNNERS_AUTH_TOKEN_FILE = lib.mkForce null;
systemd.services.n8n.serviceConfig = { systemd.services.n8n.serviceConfig = {
EnvironmentFile = ["${config.age.secrets.n8n-env.path}"]; EnvironmentFile = ["${config.age.secrets.n8n-env.path}"];
}; };

Some files were not shown because too many files have changed in this diff Show More