1 Commits

Author SHA1 Message Date
m3ta-chiron 826569ed98 feat: migrate host Hyprland configs to Lua (Hyprland 0.55+)
- m3-kratos/home.nix: use hl.monitor({}), hl.workspace_rule({}),
  hl.window_rule({}) table-based Lua API
- m3-ares/home.nix: same Lua API + tuxedo-backlight via hl.on()
- Update flake.lock: home-manager -> 74f170c6 (2026-05-18)
2026-05-18 17:27:15 +02:00
39 changed files with 497 additions and 8241 deletions
-3
View File
@@ -1,3 +0,0 @@
node_modules/
runs/
*.log
@@ -1,5 +0,0 @@
{
"nixosConfigDir": "/home/m3tam3re/p/NIX/nixos-config",
"m3taHomeDir": "/home/m3tam3re/p/NIX/m3ta-home",
"specPath": "/home/m3tam3re/p/NIX/nixos-config/.a5c/inputs/fix-eval-warnings-spec.md"
}
-10
View File
@@ -1,10 +0,0 @@
Fix the following Nix/Home Manager evaluation warnings except for the gc/nh conflict warning:
- `evaluation warning: 'system' has been renamed to/replaced by 'stdenv.hostPlatform.system'`
- `evaluation warning: m3tam3re profile: programs.ssh.matchBlocks defined in /nix/store/...-users/m3tam3re/identities/private.nix is deprecated. Use programs.ssh.settings.`
Do not fix or change the warning:
- `evaluation warning: programs.nh.clean.enable and nix.gc.automatic are both enabled. Please use one or the other to avoid conflict.`
The private identity source file is in `/home/m3tam3re/p/NIX/m3ta-home/users/m3tam3re/identities/private.nix`.
-5
View File
@@ -1,5 +0,0 @@
{
"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
@@ -1,9 +0,0 @@
{
"name": "nixos-config-a5c",
"version": "1.0.0",
"private": true,
"type": "module",
"dependencies": {
"@a5c-ai/babysitter-sdk": "latest"
}
}
-301
View File
@@ -1,301 +0,0 @@
/**
* @process local/fix-nix-eval-warnings
* @description Fix Nix/Home Manager evaluation warnings except the nh/gc conflict warning.
* @skill systematic-debugging methodologies/superpowers/systematic-debugging.js
* @skill verification-before-completion methodologies/superpowers/verification-before-completion.js
* @skill root-cause-diagnosis methodologies/shared/root-cause-diagnosis.js
*/
import { defineTask } from '@a5c-ai/babysitter-sdk';
const q = (value) => `'${String(value).replace(/'/g, `'\\''`)}'`;
export async function process(inputs, ctx) {
const nixosConfigDir = inputs.nixosConfigDir || '/home/m3tam3re/p/NIX/nixos-config';
const m3taHomeDir = inputs.m3taHomeDir || '/home/m3tam3re/p/NIX/m3ta-home';
const specPath = inputs.specPath || `${nixosConfigDir}/.a5c/inputs/fix-eval-warnings-spec.md`;
const spec = await ctx.task(readSpecTask, { specPath });
const inspection = await ctx.task(inspectWarningSourcesTask, {
nixosConfigDir,
m3taHomeDir,
});
const implementation = await ctx.task(implementFixesTask, {
nixosConfigDir,
m3taHomeDir,
spec: spec.stdout,
inspection: inspection.stdout,
});
const formatting = await ctx.task(formatChangedNixTask, {
m3taHomeDir,
});
const verification = await ctx.task(verifyWarningsTask, {
nixosConfigDir,
m3taHomeDir,
});
const artifacts = await ctx.task(collectArtifactsTask, {
nixosConfigDir,
m3taHomeDir,
verifyStdout: verification.stdout || '',
verifyStderr: verification.stderr || '',
});
const acceptance = await ctx.task(acceptanceReviewTask, {
spec: spec.stdout,
artifacts: artifacts.stdout,
});
if (!acceptance.accepted) {
await ctx.breakpoint({
title: 'Warning fix acceptance review failed',
question: `Acceptance review did not approve the changes: ${acceptance.reason}`,
context: {
runId: ctx.runId,
files: [
{ path: `${m3taHomeDir}/users/m3tam3re/identities/private.nix`, format: 'nix', label: 'Private SSH identity' },
{ path: `${m3taHomeDir}/profiles/sets/coding/agents/agents.nix`, format: 'nix', label: 'Agent packages' },
{ path: `${m3taHomeDir}/profiles/contexts/desktop/default.nix`, format: 'nix', label: 'Desktop packages' },
],
},
});
}
return {
success: acceptance.accepted,
summary: implementation.summary,
changedFiles: implementation.changedFiles,
verification: {
formatting: formatting.stdout,
warnings: verification.stdout,
review: acceptance,
},
};
}
export const readSpecTask = defineTask('read-spec', (args, taskCtx) => ({
kind: 'shell',
title: 'Read warning-fix spec',
shell: {
command: `cat ${q(args.specPath)}`,
expectedExitCode: 0,
timeout: 10000,
},
io: {
inputJsonPath: `tasks/${taskCtx.effectId}/input.json`,
outputJsonPath: `tasks/${taskCtx.effectId}/output.json`,
},
labels: ['spec', 'shell'],
}));
export const inspectWarningSourcesTask = defineTask('inspect-warning-sources', (args, taskCtx) => ({
kind: 'shell',
title: 'Inspect current warning sources',
shell: {
command: [
'set -euo pipefail',
`echo '== nixos-config status =='`,
`cd ${q(args.nixosConfigDir)} && git status --short`,
`echo`,
`echo '== m3ta-home status =='`,
`cd ${q(args.m3taHomeDir)} && git status --short`,
`echo`,
`echo '== active pkgs.system-style package selectors =='`,
`grep -RIn --include='*.nix' -E 'packages[.]\\$\\{pkgs[.]system\\}|packages[.]\\$\\{prev[.]system\\}|packages[.]\\$\\{final[.]system\\}' ${q(args.nixosConfigDir)} ${q(args.m3taHomeDir)} || true`,
`echo`,
`echo '== SSH matchBlocks in m3ta-home identities =='`,
`grep -RIn --include='*.nix' 'matchBlocks' ${q(`${args.m3taHomeDir}/users/m3tam3re/identities`)} || true`,
].join('\n'),
expectedExitCode: 0,
timeout: 30000,
},
io: {
inputJsonPath: `tasks/${taskCtx.effectId}/input.json`,
outputJsonPath: `tasks/${taskCtx.effectId}/output.json`,
},
labels: ['diagnosis', 'shell'],
}));
export const implementFixesTask = defineTask('implement-warning-fixes', (args, taskCtx) => ({
kind: 'agent',
title: 'Implement requested warning fixes',
agent: {
name: 'worker',
prompt: {
role: 'Nix/Home Manager maintenance engineer',
task: 'Edit the repositories to remove the requested evaluation warnings, excluding the nh/gc warning by request.',
context: {
nixosConfigDir: args.nixosConfigDir,
m3taHomeDir: args.m3taHomeDir,
specVerbatim: args.spec,
inspectionStdout: args.inspection,
},
instructions: [
'Execute the task fully; do not just provide a plan.',
'Do not invoke the babysit skill or create another babysitter run.',
'Read every file before editing it.',
'Preserve unrelated existing user changes, especially any dirty files in nixos-config such as flake.nix or flake.lock.',
'Fix active uses of pkgs.system/prev.system/final.system that trigger the Nixpkgs deprecation warning by using stdenv.hostPlatform.system through the appropriate package set.',
'Migrate /home/m3tam3re/p/NIX/m3ta-home/users/m3tam3re/identities/private.nix from programs.ssh.matchBlocks to programs.ssh.settings.',
'For programs.ssh.settings, use OpenSSH directive names such as HostName, User, Port, and IdentityFile; do not keep legacy camelCase option names under settings.',
'Do not change programs.nh.clean.enable or nix.gc.automatic; the user explicitly excluded that warning.',
'Keep the change minimal and focused on the warnings in the spec.',
'Run a quick static check of the edited files if practical, but leave deterministic verification to the process quality gate.',
],
outputFormat: 'JSON with summary, changedFiles, and verificationNotes.',
},
outputSchema: {
type: 'object',
required: ['summary', 'changedFiles', 'verificationNotes'],
properties: {
summary: { type: 'string' },
changedFiles: { type: 'array', items: { type: 'string' } },
verificationNotes: { type: 'array', items: { type: 'string' } },
},
},
},
io: {
inputJsonPath: `tasks/${taskCtx.effectId}/input.json`,
outputJsonPath: `tasks/${taskCtx.effectId}/output.json`,
},
labels: ['implementation', 'agent', 'nix'],
}));
export const formatChangedNixTask = defineTask('format-changed-nix', (args, taskCtx) => ({
kind: 'shell',
title: 'Format changed Nix files',
shell: {
command: [
'set -euo pipefail',
`cd ${q(args.m3taHomeDir)}`,
`if command -v alejandra >/dev/null 2>&1; then`,
` alejandra users/m3tam3re/identities/private.nix profiles/sets/coding/agents/agents.nix profiles/contexts/desktop/default.nix`,
`else`,
` nix run nixpkgs#alejandra -- users/m3tam3re/identities/private.nix profiles/sets/coding/agents/agents.nix profiles/contexts/desktop/default.nix`,
`fi`,
].join('\n'),
expectedExitCode: 0,
timeout: 120000,
},
io: {
inputJsonPath: `tasks/${taskCtx.effectId}/input.json`,
outputJsonPath: `tasks/${taskCtx.effectId}/output.json`,
},
labels: ['format', 'shell'],
}));
export const verifyWarningsTask = defineTask('verify-warning-removal', (args, taskCtx) => ({
kind: 'shell',
title: 'Verify requested warnings are gone',
shell: {
command: [
'set -euo pipefail',
`echo '== static checks =='`,
`! grep -RIn --include='*.nix' -E 'packages[.]\\$\\{pkgs[.]system\\}|packages[.]\\$\\{prev[.]system\\}|packages[.]\\$\\{final[.]system\\}' ${q(`${args.m3taHomeDir}/profiles`)} || { echo 'Found deprecated package system selector' >&2; exit 1; }`,
`! grep -n 'matchBlocks' ${q(`${args.m3taHomeDir}/users/m3tam3re/identities/private.nix`)} || { echo 'private.nix still uses matchBlocks' >&2; exit 1; }`,
`grep -n 'settings = {' ${q(`${args.m3taHomeDir}/users/m3tam3re/identities/private.nix`)}`,
`echo`,
`echo '== nix eval m3-ares =='`,
`cd ${q(args.nixosConfigDir)}`,
`eval_stdout=$(mktemp)`,
`eval_stderr=$(mktemp)`,
`set +e`,
`nix eval .#nixosConfigurations.m3-ares.config.system.build.toplevel.drvPath --show-trace >"$eval_stdout" 2>"$eval_stderr"`,
`status=$?`,
`set -e`,
`cat "$eval_stdout"`,
`cat "$eval_stderr" >&2`,
`if [ "$status" -ne 0 ]; then exit "$status"; fi`,
`if grep -F "'system' has been renamed" "$eval_stderr"; then echo 'Deprecated system warning still present' >&2; exit 1; fi`,
`if grep -F 'programs.ssh.matchBlocks' "$eval_stderr"; then echo 'Deprecated SSH matchBlocks warning still present' >&2; exit 1; fi`,
`if grep -F 'programs.nh.clean.enable and nix.gc.automatic' "$eval_stderr" >/dev/null; then echo 'Allowed nh/gc warning remains by request.'; fi`,
].join('\n'),
expectedExitCode: 0,
timeout: 300000,
},
io: {
inputJsonPath: `tasks/${taskCtx.effectId}/input.json`,
outputJsonPath: `tasks/${taskCtx.effectId}/output.json`,
},
labels: ['verification', 'shell', 'nix'],
}));
export const collectArtifactsTask = defineTask('collect-artifacts', (args, taskCtx) => ({
kind: 'shell',
title: 'Collect diffs and verification output',
shell: {
command: [
'set -euo pipefail',
`echo '== m3ta-home diff =='`,
`cd ${q(args.m3taHomeDir)} && git diff -- users/m3tam3re/identities/private.nix profiles/sets/coding/agents/agents.nix profiles/contexts/desktop/default.nix`,
`echo`,
`echo '== nixos-config diff (should not include warning fix unless needed) =='`,
`cd ${q(args.nixosConfigDir)} && git diff -- overlays/default.nix flake.nix flake.lock || true`,
`echo`,
`echo '== verification stdout =='`,
`cat <<'VERIFY_STDOUT'`,
args.verifyStdout || '',
`VERIFY_STDOUT`,
`echo`,
`echo '== verification stderr =='`,
`cat <<'VERIFY_STDERR'`,
args.verifyStderr || '',
`VERIFY_STDERR`,
].join('\n'),
expectedExitCode: 0,
timeout: 30000,
},
io: {
inputJsonPath: `tasks/${taskCtx.effectId}/input.json`,
outputJsonPath: `tasks/${taskCtx.effectId}/output.json`,
},
labels: ['artifacts', 'shell'],
}));
export const acceptanceReviewTask = defineTask('acceptance-review', (args, taskCtx) => ({
kind: 'agent',
title: 'Review changes against requested warning fixes',
agent: {
name: 'reviewer',
prompt: {
role: 'Acceptance reviewer for a Nix/Home Manager warning fix',
task: 'Compare SPEC to ARTIFACTS directly and decide whether the requested warnings were fixed without touching the excluded nh/gc warning.',
instructions: [
'Ignore any narrative in your context about how ARTIFACTS were built.',
'Do not ask for additional changes unless they are required by the SPEC.',
'Accept if the system deprecation warning and private SSH matchBlocks warning are addressed, and the nh/gc conflict remains untouched.',
'',
'SPEC (verbatim):',
'---',
args.spec,
'---',
'',
'ARTIFACTS (verbatim):',
'---',
args.artifacts,
'---',
'',
'Compare SPEC to ARTIFACTS directly. Ignore any narrative in your context about how ARTIFACTS were built.',
],
outputFormat: 'JSON with accepted boolean, reason string, and checkedCriteria array.',
},
outputSchema: {
type: 'object',
required: ['accepted', 'reason', 'checkedCriteria'],
properties: {
accepted: { type: 'boolean' },
reason: { type: 'string' },
checkedCriteria: { type: 'array', items: { type: 'string' } },
},
},
},
io: {
inputJsonPath: `tasks/${taskCtx.effectId}/input.json`,
outputJsonPath: `tasks/${taskCtx.effectId}/output.json`,
},
labels: ['acceptance', 'agent', 'review'],
}));
-596
View File
@@ -1,596 +0,0 @@
{
"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
@@ -1,238 +0,0 @@
# 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
@@ -1,53 +0,0 @@
{
"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."
}
}
-7
View File
@@ -46,10 +46,3 @@ CLAUDE.md
.dolt/ .dolt/
*.db *.db
.beads-credential-key .beads-credential-key
# --- babysitter managed ---
.a5c/creds.env
.a5c/creds.env.tmp.*
.a5c/logs/
.a5c/runs/
# --- end babysitter managed ---
File diff suppressed because it is too large Load Diff
Generated
+327 -456
View File
File diff suppressed because it is too large Load Diff
+36 -3
View File
@@ -15,7 +15,7 @@
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/nixpkgs-unstable"; nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
nixpkgs-stable.url = "github:nixos/nixpkgs/nixos-25.11"; nixpkgs-stable.url = "github:nixos/nixpkgs/nixos-25.11";
nixpkgs-45570c2.url = "github:nixos/nixpkgs/45570c299dc2b63c8c574c4cd77f0b92f7e2766e"; nixpkgs-45570c2.url = "github:nixos/nixpkgs/45570c299dc2b63c8c574c4cd77f0b92f7e2766e";
nixpkgs-locked.url = "github:nixos/nixpkgs/2744d988fa116fc6d46cdfa3d1c936d0abd7d121"; nixpkgs-locked.url = "github:nixos/nixpkgs/2744d988fa116fc6d46cdfa3d1c936d0abd7d121";
@@ -25,6 +25,7 @@
m3ta-nixpkgs.url = "git+ssh://gitea@code.m3ta.dev/m3tam3re/nixpkgs"; m3ta-nixpkgs.url = "git+ssh://gitea@code.m3ta.dev/m3tam3re/nixpkgs";
llm-agents.url = "github:numtide/llm-agents.nix"; llm-agents.url = "github:numtide/llm-agents.nix";
#
nur = { nur = {
url = "github:nix-community/NUR"; url = "github:nix-community/NUR";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
@@ -38,16 +39,41 @@
nixos-generators = {url = "github:nix-community/nixos-generators";}; nixos-generators = {url = "github:nix-community/nixos-generators";};
hyprpanel.url = "github:Jas-SinghFSU/HyprPanel";
rose-pine-hyprcursor.url = "github:ndom91/rose-pine-hyprcursor"; rose-pine-hyprcursor.url = "github:ndom91/rose-pine-hyprcursor";
nix-colors.url = "github:misterio77/nix-colors"; nix-colors.url = "github:misterio77/nix-colors";
m3ta-home = { m3ta-home = {
url = "git+ssh://gitea@code.m3ta.dev/m3tam3re/m3ta-home"; url = "git+ssh://gitea@code.m3ta.dev/m3tam3re/m3ta-home";
# url = "path:/home/m3tam3re/p/NIX/m3ta-home";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
}; };
hermes-agent.url = "github:NousResearch/hermes-agent/v2026.6.5"; agents = {
# url = "path:/home/m3tam3re/p/AI/AGENTS";
url = "git+ssh://gitea@code.m3ta.dev/m3tam3re/AGENTS";
};
## Skills
skills-basecamp = {
url = "github:basecamp/basecamp-cli";
flake = false;
};
skills-anthropic = {
url = "github:anthropics/skills";
flake = false;
};
skills-kestra = {
url = "github:kestra-io/agent-skills";
flake = false;
};
skills-superpowers = {
url = "github:obra/superpowers";
flake = false;
};
skills-vercel = {
url = "github:vercel-labs/skills";
flake = false;
};
hermes-agent.url = "github:NousResearch/hermes-agent/v2026.5.7";
rustfs = { rustfs = {
url = "github:rustfs/rustfs-flake"; url = "github:rustfs/rustfs-flake";
@@ -62,6 +88,7 @@
nixpkgs, nixpkgs,
m3ta-nixpkgs, m3ta-nixpkgs,
nur, nur,
agents,
... ...
} @ inputs: let } @ inputs: let
inherit (self) outputs; inherit (self) outputs;
@@ -164,6 +191,11 @@
inherit system; inherit system;
config.allowUnfree = true; # Allow unfree packages in devShell config.allowUnfree = true; # Allow unfree packages in devShell
}; };
m3taLib = m3ta-nixpkgs.lib.x86_64-linux;
rules = m3taLib.coding-rules.mkCodingRules {
inherit agents;
languages = ["nix"];
};
in { in {
default = pkgs.mkShell { default = pkgs.mkShell {
buildInputs = with pkgs; [ buildInputs = with pkgs; [
@@ -174,6 +206,7 @@
statix statix
deadnix deadnix
]; ];
inherit (rules) instructions shellHook;
}; };
}); });
}; };
+1 -2
View File
@@ -21,8 +21,7 @@
useGlobalPkgs = true; useGlobalPkgs = true;
useUserPackages = true; useUserPackages = true;
extraSpecialArgs = { extraSpecialArgs = {
inputs = inputs // {agents = null;}; inherit inputs outputs system;
inherit outputs system;
videoDrivers = config.services.xserver.videoDrivers or []; videoDrivers = config.services.xserver.videoDrivers or [];
}; };
}; };
-5
View File
@@ -39,11 +39,6 @@
outline = 3019; outline = 3019;
authentik = 3023; authentik = 3023;
tuwunel = 3024; tuwunel = 3024;
honcho = 3025;
# Agent infrastructure
hermes-api = 8642;
hermes-dashboard = 9119;
# Home automation # Home automation
homarr = 7575; homarr = 7575;
-1
View File
@@ -90,7 +90,6 @@
hyprland.enable = true; hyprland.enable = true;
rofi.enable = true; rofi.enable = true;
wayland.enable = true; wayland.enable = true;
dms.enable = true;
}; };
apps = { apps = {
crypto.enable = true; crypto.enable = true;
+24 -20
View File
@@ -36,35 +36,39 @@ with lib; {
}; };
} }
# ── Hyprland monitor layout ── # ── Hyprland monitor layout & host-specific rules ──
(mkIf config.desktop.wm.hyprland.enable { (mkIf config.desktop.wm.hyprland.enable {
wayland.windowManager.hyprland = { wayland.windowManager.hyprland = {
enable = true; enable = true;
settings = { settings = {
exec-once = ["tuxedo-backlight"]; # Laptop internal + external HDMI
monitor = [ monitor = [
"eDP-1,preferred,0x0,1.25" { output = "eDP-1"; mode = "preferred"; position = "0x0"; scale = 1.25; }
"HDMI-A-1,1920x1080@120,2560x0,1" { output = "HDMI-A-1"; mode = "1920x1080@120"; position = "2560x0"; scale = 1; }
]; ];
workspace = [ workspace_rule = [
"1, monitor:eDP-1, default:true" { workspace = 1; monitor = "eDP-1"; default = true; }
"2, monitor:eDP-1" { workspace = 2; monitor = "eDP-1"; }
"3, monitor:eDP-1" { workspace = 3; monitor = "eDP-1"; }
"4, monitor:HDMI-A-1" { workspace = 4; monitor = "HDMI-A-1"; }
"5, monitor:HDMI-A-1,border:false,rounding:false" { workspace = 5; monitor = "HDMI-A-1"; border = false; rounding = false; }
"6, monitor:HDMI-A-1" { workspace = 6; monitor = "HDMI-A-1"; }
]; ];
windowrule = [ window_rule = [
"match:class dev.zed.Zed, workspace 1" { match = { class = "dev.zed.Zed" }; workspace = "1"; }
"match:class Msty, workspace 1" { match = { class = "Msty" }; workspace = "1"; }
"match:class ^(com.obsproject.Studio)$, workspace 2" { match = { class = "^com.obsproject.Studio$" }; workspace = "2"; }
"match:class ^(brave-browser)$, workspace 4, opacity 1.0" { match = { class = "^(brave-browser)$" }; workspace = "4"; opacity = 1.0; }
"match:class ^(vivaldi-stable)$, workspace 4, opacity 1.0" { match = { class = "^(vivaldi-stable)$" }; workspace = "4"; opacity = 1.0; }
"match:class ^steam_app_\\d+$, fullscreen on" { match = { class = "^steam_app_\\d+$" }; fullscreen = true; workspace = "5"; idle_inhibit = "focus"; }
"match:class ^steam_app_\\d+$, workspace 5"
"match:class ^steam_app_\\d+$, idle_inhibit focus"
]; ];
}; };
extraConfig = mkAfter ''
-- Host startup: TUXEDO backlight
hl.on("hyprland.start", function()
hl.exec_cmd("tuxedo-backlight")
end)
'';
}; };
}) })
]; ];
-1
View File
@@ -1,7 +1,6 @@
{pkgs, ...}: { {pkgs, ...}: {
imports = [ imports = [
./containers ./containers
./greetd.nix
./hermes-agent.nix ./hermes-agent.nix
./netbird.nix ./netbird.nix
#./n8n.nix #./n8n.nix
-38
View File
@@ -1,38 +0,0 @@
# 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 - -"
];
}
+2 -13
View File
@@ -17,20 +17,9 @@ in {
settings = { settings = {
# ── Model ────────────────────────────────────────────────────────── # ── Model ──────────────────────────────────────────────────────────
model = { model = {
default = "gpt-5.5"; default = "glm-5.1";
provider = "openai-codex";
};
fallback_providers = [
{
provider = "zai"; provider = "zai";
model = "glm-5.1"; };
}
{
provider = "minimax";
model = "MiniMax-M2.7";
}
];
credential_pool_strategies = { credential_pool_strategies = {
zai = "fill_first"; zai = "fill_first";
-8
View File
@@ -3,14 +3,6 @@
secrets = { secrets = {
baserow-env = {file = ../../secrets/baserow-env.age;}; baserow-env = {file = ../../secrets/baserow-env.age;};
ghost-env = {file = ../../secrets/ghost-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";
};
honcho-selfhost-env = {file = ../../secrets/honcho-selfhost-env.age;};
honcho-selfhost-jwt-secret = {file = ../../secrets/honcho-selfhost-jwt-secret.age;};
kestra-config = { kestra-config = {
file = ../../secrets/kestra-config.age; file = ../../secrets/kestra-config.age;
mode = "644"; mode = "644";
@@ -2,7 +2,6 @@
imports = [ imports = [
./baserow.nix ./baserow.nix
./ghost.nix ./ghost.nix
./honcho.nix
./kestra.nix ./kestra.nix
./littlelink.nix ./littlelink.nix
./matomo.nix ./matomo.nix
+1 -1
View File
@@ -1,6 +1,6 @@
{config, ...}: { {config, ...}: {
virtualisation.oci-containers.containers."ghost" = { virtualisation.oci-containers.containers."ghost" = {
image = "docker.io/ghost:6-alpine"; image = "docker.io/ghost:latest";
environmentFiles = [config.age.secrets.ghost-env.path]; environmentFiles = [config.age.secrets.ghost-env.path];
ports = ["127.0.0.1:3002:2368"]; ports = ["127.0.0.1:3002:2368"];
volumes = ["ghost_data:/var/lib/ghost/content"]; volumes = ["ghost_data:/var/lib/ghost/content"];
@@ -1,209 +0,0 @@
{
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
'';
}
+1 -2
View File
@@ -28,7 +28,6 @@
host kestra kestra 10.89.0.0/24 scram-sha-256 host kestra kestra 10.89.0.0/24 scram-sha-256
host netbird netbird 10.89.0.0/24 scram-sha-256 host netbird netbird 10.89.0.0/24 scram-sha-256
host authentik authentik 10.89.0.0/24 scram-sha-256 host authentik authentik 10.89.0.0/24 scram-sha-256
host honcho honcho 10.89.0.0/24 scram-sha-256
# Deny all other connections # Deny all other connections
local all all reject local all all reject
@@ -39,7 +38,7 @@
services.postgresqlBackup = { services.postgresqlBackup = {
enable = true; enable = true;
startAt = "03:10:00"; startAt = "03:10:00";
databases = ["baserow" "paperless" "kestra" "authentik" "netbird" "honcho"]; databases = ["baserow" "paperless" "kestra" "authentik" "netbird"];
}; };
networking.firewall = { networking.firewall = {
extraCommands = '' extraCommands = ''
+54 -64
View File
@@ -1,6 +1,5 @@
{ {
config, config,
lib,
pkgs, pkgs,
inputs, inputs,
... ...
@@ -8,41 +7,40 @@
# Edge TTS: Seraphina — friendly, multilingual German female voice (free, no API key) # Edge TTS: Seraphina — friendly, multilingual German female voice (free, no API key)
edgeVoice = "de-DE-SeraphinaMultilingualNeural"; edgeVoice = "de-DE-SeraphinaMultilingualNeural";
agentSkillExclusions = { # Extra Python packages from the container's writable venv layer.
m3ta-agents = []; # matrix-nio is installed via pip in /home/hermes/.venv but the hermes
anthropic = ["pdf" "skill-creator" "xlsx"]; # process uses the read-only Nix store Python, so we inject the venv's
basecamp = []; # site-packages via PYTHONPATH and provide libstdc++ for libolm (e2e).
kestra = []; # NOTE: v0.13.0 upgraded to Python 3.12 — path updated accordingly.
mattpocock = ["grill-me" "caveman"]; venvSitePackages = "/home/hermes/.venv/lib/python3.12/site-packages";
superpowers = ["brainstorming" "systematic-debugging"]; gccLibPath = "${pkgs.stdenv.cc.cc.lib}/lib";
vercel = [];
};
agentLibSourceSelections = # Build skills using agents flake lib for hermes user
lib.mapAttrs (_sourceName: exclude: { hermesSkills = inputs.agents.lib.mkSkills {
skills = { inherit pkgs;
all = true; customSkills = "${inputs.agents}/skills";
inherit exclude; externalSkills = [
}; {
}) src = inputs.skills-basecamp;
agentSkillExclusions; skillsDir = "skills";
}
# Deterministic store renderer consumed directly by Hermes. m3ta-home {
# re-exports the focused helper so nixos-config does not need a direct src = inputs.skills-anthropic;
# agent-lib flake input. skillsDir = "skills";
hermesSkills = inputs.m3ta-home.lib.mkHermesSkillsDir { }
system = pkgs.stdenv.hostPlatform.system; {
name = "hermes-agent-lib-skills"; src = inputs.skills-kestra;
lockFile = ../../../agent-sources.lock.json; skillsDir = "skills";
sources = agentLibSourceSelections; }
];
}; };
in { in {
virtualisation.docker.enable = true; virtualisation.docker.enable = true;
systemd.tmpfiles.rules = [ systemd.tmpfiles.rules = [
"d /var/lib/hermes/.config 0755 hermes hermes -" "d /home/hermes/.config 0755 hermes hermes -"
"d /var/lib/hermes/.config/tea 0755 hermes hermes -" "d /home/hermes/.config/tea 0755 hermes hermes -"
"L+ /var/lib/hermes/.config/tea/yml - - - - ${pkgs.writeText "tea-yml" '' "L+ /home/hermes/.config/tea/yml - - - - ${pkgs.writeText "tea-yml" ''
logins: logins:
- name: m3ta - name: m3ta
url: https://code.m3ta.dev url: https://code.m3ta.dev
@@ -53,33 +51,32 @@ in {
''}" ''}"
]; ];
systemd.services.hermes-agent.restartTriggers = [hermesSkills]; systemd.services.copy-hermes-skills = {
description = "Copy agent skills to hermes home directory";
wantedBy = ["hermes-agent.service"];
before = ["hermes-agent.service"];
serviceConfig.Type = "oneshot";
serviceConfig.RemainAfterExit = true;
script = ''
mkdir -p /var/lib/hermes/.agents
cp -rT ${hermesSkills} /var/lib/hermes/.agents/skills
chown -R hermes:hermes /var/lib/hermes/.agents
'';
};
# Ensure 'uv' is in the hermes-agent service PATH so CronJobs and terminal
# sessions can use 'uv run' for PEP 723 scripts (e.g. garmin-daily.py).
systemd.services.hermes-agent.path = [pkgs.uv];
services.hermes-agent = { services.hermes-agent = {
enable = true; enable = true;
addToSystemPackages = true; addToSystemPackages = true;
# v0.14 lazy-installs heavy optional backends by default. In the sealed
# Nix package, include the backends this host config actively uses so the
# gateway, Matrix bridge, memory, web search, TTS, and local STT work
# without runtime pip/uv mutation.
extraDependencyGroups = [
"matrix"
"honcho"
"exa"
"edge-tts"
"voice"
];
extraPackages = with pkgs; [ extraPackages = with pkgs; [
basecamp
docker docker
git git
curl
jq
tea tea
nix nix
python3Minimal
uv
zellij zellij
]; ];
@@ -108,7 +105,7 @@ in {
# Bind to 0.0.0.0 so the Netbird interface can reach it. # Bind to 0.0.0.0 so the Netbird interface can reach it.
API_SERVER_ENABLED = "true"; API_SERVER_ENABLED = "true";
API_SERVER_HOST = "0.0.0.0"; API_SERVER_HOST = "0.0.0.0";
API_SERVER_PORT = toString (config.m3ta.ports.get "hermes-api"); API_SERVER_PORT = "8642";
}; };
# ── Container mode (podman) ────────────────────────────────────────── # ── Container mode (podman) ──────────────────────────────────────────
@@ -116,26 +113,20 @@ in {
enable = false; enable = false;
backend = "podman"; backend = "podman";
extraVolumes = ["/home/m3tam3re/p:/projects:rw"]; extraVolumes = ["/home/m3tam3re/p:/projects:rw"];
extraOptions = []; extraOptions = [
"--env"
"PYTHONPATH=${venvSitePackages}"
"--env"
"LD_LIBRARY_PATH=${gccLibPath}"
];
}; };
settings = { settings = {
# ── Model ────────────────────────────────────────────────────────── # ── Model ──────────────────────────────────────────────────────────
model = { model = {
default = "gpt-5.5"; default = "glm-5.1";
provider = "openai-codex";
};
fallback_providers = [
{
provider = "zai"; provider = "zai";
model = "glm-5.1"; };
}
{
provider = "minimax";
model = "MiniMax-M2.7";
}
];
credential_pool_strategies = { credential_pool_strategies = {
zai = "fill_first"; zai = "fill_first";
@@ -148,14 +139,13 @@ in {
max_turns = 90; max_turns = 90;
gateway_timeout = 1800; gateway_timeout = 1800;
tool_use_enforcement = "auto"; tool_use_enforcement = "auto";
reasoning_effort = "high";
}; };
# ── Skills ───────────────────────────────────────────────────────── # ── Skills ─────────────────────────────────────────────────────────
skills = { skills = {
external_dirs = [ external_dirs = [
hermesSkills "/var/lib/hermes/.agents/skills"
]; ];
}; };
+7 -11
View File
@@ -7,8 +7,6 @@
# Netbird mesh VPN range — dashboard only accessible from mesh peers. # Netbird mesh VPN range — dashboard only accessible from mesh peers.
# m3-atlas Traefik proxies to this port over Netbird. # m3-atlas Traefik proxies to this port over Netbird.
netbirdRange = "100.64.0.0/16"; netbirdRange = "100.64.0.0/16";
apiPort = config.m3ta.ports.get "hermes-api";
dashboardPort = config.m3ta.ports.get "hermes-dashboard";
# Reference the hermes-agent package from the running service config # Reference the hermes-agent package from the running service config
hermesPkg = config.services.hermes-agent.package or (inputs.hermes-agent.packages.${pkgs.stdenv.hostPlatform.system}.default or pkgs.hermes-agent); hermesPkg = config.services.hermes-agent.package or (inputs.hermes-agent.packages.${pkgs.stdenv.hostPlatform.system}.default or pkgs.hermes-agent);
@@ -16,10 +14,10 @@ in {
# ── Hermes Dashboard systemd service ─────────────────────────────────── # ── Hermes Dashboard systemd service ───────────────────────────────────
# Web UI for managing Hermes Agent — sessions, config, kanban, cron, etc. # Web UI for managing Hermes Agent — sessions, config, kanban, cron, etc.
# #
# Flow: Browser → dash.m3ta.dev (TLS via m3-atlas Traefik) → Netbird → :${toString dashboardPort} # Flow: Browser → dash.m3ta.dev (TLS via m3-atlas Traefik) → Netbird → :9119
# #
# --insecure is required to bind 0.0.0.0 (hermes refuses non-localhost otherwise). # --insecure is required to bind 0.0.0.0 (hermes refuses non-localhost otherwise).
# Safe because firewall restricts the dashboard/API ports to Netbird mesh only. # Safe because firewall restricts port 9119 to Netbird mesh only.
systemd.services.hermes-dashboard = { systemd.services.hermes-dashboard = {
description = "Hermes Agent Web Dashboard"; description = "Hermes Agent Web Dashboard";
after = ["network.target" "hermes-agent.service"]; after = ["network.target" "hermes-agent.service"];
@@ -31,7 +29,7 @@ in {
User = "hermes"; User = "hermes";
Group = "hermes"; Group = "hermes";
ExecStart = "${hermesPkg}/bin/hermes dashboard --host 0.0.0.0 --port ${toString dashboardPort} --no-open --insecure"; ExecStart = "${hermesPkg}/bin/hermes dashboard --host 0.0.0.0 --port 9119 --no-open --insecure";
# Environment matching the hermes-agent service # Environment matching the hermes-agent service
Environment = [ Environment = [
@@ -53,17 +51,15 @@ in {
}; };
}; };
# ── Firewall: Hermes network endpoints only from Netbird mesh ────────── # ── Firewall: Dashboard only from Netbird mesh ─────────────────────────
networking.firewall = { networking.firewall = {
extraCommands = '' extraCommands = ''
# Allow Hermes Dashboard and OpenAI-compatible API only from Netbird mesh VPN # Allow Hermes Dashboard (9119/tcp) only from Netbird mesh VPN
ip46tables -A nixos-fw -p tcp --dport ${toString dashboardPort} -s ${netbirdRange} -j nixos-fw-accept ip46tables -A nixos-fw -p tcp --dport 9119 -s ${netbirdRange} -j nixos-fw-accept
ip46tables -A nixos-fw -p tcp --dport ${toString apiPort} -s ${netbirdRange} -j nixos-fw-accept
''; '';
extraStopCommands = '' extraStopCommands = ''
ip46tables -D nixos-fw -p tcp --dport ${toString dashboardPort} -s ${netbirdRange} -j nixos-fw-accept 2>/dev/null || true ip46tables -D nixos-fw -p tcp --dport 9119 -s ${netbirdRange} -j nixos-fw-accept 2>/dev/null || true
ip46tables -D nixos-fw -p tcp --dport ${toString apiPort} -s ${netbirdRange} -j nixos-fw-accept 2>/dev/null || true
''; '';
}; };
} }
+1 -2
View File
@@ -11,11 +11,10 @@
boot.supportedFilesystems = ["zfs"]; boot.supportedFilesystems = ["zfs"];
boot.zfs.package = pkgs.zfs_unstable; boot.zfs.package = pkgs.zfs_unstable;
boot.zfs.forceImportAll = false; boot.zfs.forceImportAll = false;
boot.zfs.forceImportRoot = false;
boot.loader.systemd-boot.enable = true; boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true; boot.loader.efi.canTouchEfiVariables = true;
boot.initrd.kernelModules = ["amdgpu"]; boot.initrd.kernelModules = ["amdgpu"];
boot.kernelPackages = pkgs.linuxPackages_7_0; boot.kernelPackages = pkgs.linuxPackages_6_18;
services.xserver.videoDrivers = ["amdgpu"]; services.xserver.videoDrivers = ["amdgpu"];
security.polkit.enable = true; security.polkit.enable = true;
security.pam.services.gdm.enableGnomeKeyring = true; security.pam.services.gdm.enableGnomeKeyring = true;
-1
View File
@@ -48,7 +48,6 @@
podman.enable = true; podman.enable = true;
virtualisation.enable = true; virtualisation.enable = true;
}; };
services.power-profiles-daemon.enable = true;
services.ollama = { services.ollama = {
environmentVariables = { environmentVariables = {
# HCC_AMDGPU_TARGET = "gfx1103"; # HCC_AMDGPU_TARGET = "gfx1103";
+19 -38
View File
@@ -8,14 +8,9 @@
... ...
}: }:
with lib; { with lib; {
imports = [
];
config = mkMerge [ config = mkMerge [
# ── XDG / MIME defaults ── # ── XDG / MIME defaults ──
{ {
qt.platformTheme.name = mkForce "qtct";
xdg = { xdg = {
enable = true; enable = true;
configFile."mimeapps.list".force = true; configFile."mimeapps.list".force = true;
@@ -41,46 +36,32 @@ with lib; {
}; };
} }
# ── Hyprland monitor layout ── # ── Hyprland monitor layout & host-specific rules ──
(mkIf config.desktop.wm.hyprland.enable { (mkIf config.desktop.wm.hyprland.enable {
wayland.windowManager.hyprland = { wayland.windowManager.hyprland = {
enable = true; enable = true;
settings = { settings = {
# Dual monitor: DP-1 left, DP-2 right
monitor = [ monitor = [
"DP-1,2560x1440@144,0x0,1" { output = "DP-1"; mode = "2560x1440@144"; position = "0x0"; scale = 1; }
"DP-2,2560x1440@144,2560x0,1" { output = "DP-2"; mode = "2560x1440@144"; position = "2560x0"; scale = 1; }
]; ];
workspace = [ workspace_rule = [
"1, monitor:DP-1, default:true" { workspace = 1; monitor = "DP-1"; default = true; }
"2, monitor:DP-1" { workspace = 2; monitor = "DP-1"; }
"3, monitor:DP-1" { workspace = 3; monitor = "DP-1"; }
"4, monitor:DP-2" { workspace = 4; monitor = "DP-2"; }
"5, monitor:DP-2" { workspace = 5; monitor = "DP-2"; }
"6, monitor:DP-2" { workspace = 6; monitor = "DP-2"; }
"7, monitor:DP-2" { workspace = 7; monitor = "DP-2"; }
]; ];
# m3ta-home sets QT_QPA_PLATFORMTHEME=gtk3 globally for Hyprland. window_rule = [
# ksnip crashes with duplicate GDK type registration under that Qt GTK { match = { class = "dev.zed.Zed" }; workspace = "1"; }
# platform theme, so use qtct for Qt apps on this host instead. { match = { class = "Msty" }; workspace = "1"; }
env = mkForce [ { match = { class = "^com.obsproject.Studio$" }; workspace = "2"; }
"XCURSOR_SIZE,32" { match = { class = "^(brave-browser)$" }; workspace = "4"; opacity = 1.0; }
"HYPRCURSOR_THEME,Bibata-Modern-Ice" { match = { class = "^(vivaldi-stable)$" }; workspace = "4"; opacity = 1.0; }
"WLR_NO_HARDWARE_CURSORS,1" { match = { class = "^steam_app_\\d+$" }; idle_inhibit = "focus"; }
"XDG_CURRENT_DESKTOP,Hyprland"
"XDG_SESSION_TYPE,wayland"
"XDG_SESSION_DESKTOP,Hyprland"
"XKB_DEFAULT_LAYOUT,de"
"NIXOS_OZONE_WL,1"
"QT_QPA_PLATFORM,wayland;xcb"
"QT_QPA_PLATFORMTHEME,qt5ct"
"QT_QPA_PLATFORMTHEME_QT6,qt6ct"
];
windowrule = [
"match:class dev.zed.Zed, workspace 1"
"match:class ^(com.obsproject.Studio)$, workspace 2"
"match:class ^(brave-browser)$, workspace 4, opacity 1.0"
"match:class ^(vivaldi-stable)$, workspace 4, opacity 1.0"
"match:class ^steam_app_\\d+$, idle_inhibit focus"
]; ];
}; };
}; };
+1 -2
View File
@@ -1,7 +1,6 @@
{pkgs, ...}: { {pkgs, ...}: {
imports = [ imports = [
./containers ./containers
./greetd.nix
./mem0.nix ./mem0.nix
# ./n8n.nix # ./n8n.nix
./netbird.nix ./netbird.nix
@@ -30,6 +29,6 @@
userServices = true; userServices = true;
}; };
}; };
# displayManager.gdm.enable = true; displayManager.gdm.enable = true;
}; };
} }
-38
View File
@@ -1,38 +0,0 @@
# 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 - -"
];
}
-3
View File
@@ -38,9 +38,6 @@ in {
"secrets/basecamp-client-id.age".publicKeys = systems ++ users; "secrets/basecamp-client-id.age".publicKeys = systems ++ users;
"secrets/basecamp-client-secret.age".publicKeys = systems ++ users; "secrets/basecamp-client-secret.age".publicKeys = systems ++ users;
"secrets/gitea-runner-token.age".publicKeys = systems ++ users; "secrets/gitea-runner-token.age".publicKeys = systems ++ users;
"secrets/honcho-selfhost-db-password.age".publicKeys = systems ++ users;
"secrets/honcho-selfhost-env.age".publicKeys = systems ++ users;
"secrets/honcho-selfhost-jwt-secret.age".publicKeys = systems ++ users;
"secrets/outline-key.age".publicKeys = systems ++ users; "secrets/outline-key.age".publicKeys = systems ++ users;
"secrets/restreamer-env.age".publicKeys = systems ++ users; "secrets/restreamer-env.age".publicKeys = systems ++ users;
"secrets/searx.age".publicKeys = systems ++ users; "secrets/searx.age".publicKeys = systems ++ users;
+23 -22
View File
@@ -1,25 +1,26 @@
age-encryption.org/v1 age-encryption.org/v1
-> ssh-ed25519 4NLKrw 42tBp6EbDJpC7EBt0++QxmF3N9rQJ/AP+7A/S174rCs -> ssh-ed25519 4NLKrw 2TwbZwX9SwWg4SVC0A2ICmyRjSfO+xtfBcBOK1lh3T4
bRzpQku0GLEBvANvCdeH3L4Kf06k6w2C4FfZCOp2QWI DSf4DrOAvW7L49lh6cq5IqrMM7gqXv2+67rR3ttn+CE
-> ssh-ed25519 5kwcsA YAYkQzsxfbHwrCPMW2eqLS9mRuuxr+EjHKl7MV3DDEo -> ssh-ed25519 5kwcsA K1hqFOAxq2T+oLp3bQjLYpXtlQVkA7RHCM/8ETMGbwU
dN3TitETbdPbXzBtIDBglienhY4oDsFGgfe0VYdsP1o xIE4xz50LB5vbDTTLKVcx9vC2iXIsRLThHYYxGjcJyY
-> ssh-ed25519 9d4YIQ 2vTWMSuLrgpgaTWeu0ARoUOukLBKupCfMdqJhLvTqwA -> ssh-ed25519 9d4YIQ bXYb62OM/N+EXpMOZZ6zEbpfaH10Vz62PuUdGODXolw
Lzk2Uo2U3tUJiq29on/a5zYfuUjgOZvCHhZYuFGSDG4 j64kKzOn8CmSnykEuWnXHZ0nfqwOfOxX4FPR4GSouR0
-> ssh-ed25519 3Bcr1w x689Z0/TsOLLk1JNPXg2jj6y5ucaH37zRt46d/Z1l2w -> ssh-ed25519 3Bcr1w C4alN6ud7q0K4I7NHuBgC77D6zeTfZVGjNS3EKpvL00
Bkzg3umkDYFBemmgev/M5LUFuobFugXe0u85mLmsDSo NpjOsg3eJ5LvX0lV7NYuVHLeqeYylHdmw60H+KeG1GY
-> ssh-ed25519 c4NQlA 5Dn6e8bILaYl9FVt+ZwuZ6rOC0k0Kg1+KOSP4JakyWI -> ssh-ed25519 c4NQlA In5wsg4+LTIEbP75B83GMXPCItSPGwKWUW8QO+QjXyY
AT6LeCo+P7RjgNhRex04kJ/7NHD2DAWRqs33uOJ7e5E oK1kikhr4RMq6QMv9kjNjiKrf5srlGh7hGbU2qns2rM
-> ssh-rsa DQlE7w -> ssh-rsa DQlE7w
M9pUnzZDa1v6X5UbQOE6HILaGU36VkQtnfXaJJdxJSRQ/sE9R3ZQoLjRZAw+UhUf tcP4yPgGWqHYeE1gw/KD6cswik+9WU2s2f7hg5mK78085sQ7npXRsBVAz2OCRn07
09JwLkS55477xaar3bpvvOxeP4MrtTHLJ7593eEkFT3i45FfVmxutq6EYckZrCJB foeAAmnY4YmKriBh421JOVNBDOXHR5dfaIKY9b663L+rYj99ic0rfW26C+dqKitF
WjrCG7Cbvc20o6s54PYiF4Xk8AuPxt+SElRxBtcOK+SPba84f+WWHqrBA1YRzTDK SnvveL3Zf16nqg6duSVA7LIcIFgkIlA+RXnHPVho+P4GwEH7W8nCf/4kUquuhB7B
fsM15eKWsJgzaz5y36grv4xSj4KbWMFtmEt5V5BEW32+zXBU5CPhonO59TxEQgh4 F4Hx1qOknmGyNBJBFi27D04ZDDk/ZVxioYsO6P6TUu7MuaGmQCoVKREDl5RRh4zO
hI2+gNmAzKQja7xbuxCyr3jcXWJz7IuXcrklr+2ZjF1wx3BDll1z+vxSX0C88MCc XD8/TFDRsJLqqcbCKIlU+6CN1+L0r4FN4K0UaTjwPNzGvn5EEjBKw9RpOhdvI28I
OLKDfnUiDa6BlgUfLK90dLIia8v0oIPXs4OWRfYs7SC/Z3QOPpSO62Ky9dKYRrod WlAQ+w6gdQiz9Ju4e5p7Doz2MbNb6894DimawHjzl968Xy5ifX2XA+FBdcW5hU9A
PHvCgxX28QvROE4TekL9PV81AfAbMVJrnkRiybg6id8CscldtDmgaKqoaIoJlAuF u+7VXKZmbfMyvRA7lmKRoi4SurJAyQd6iXBrVKfTwFc53V/tJi48bsKcE3yXxHH+
g5/LGd+FPfmlv2iNfGUn2Glhui8SkrBK1MzGJpeQw+l4CXLH33yQzHX0m6TdQBzr lKGuZFNGDDkqCruycjvz94WaIHy3fv5hhmBdgwoCZK1VGSLAnwdm1rG4B9m3t/K8
-> ssh-ed25519 CSMyhg 5YHqBNbkkUFVhDEfOM4P2tAxT2t1rDn5KItUcjUs4DY -> ssh-ed25519 CSMyhg FNYYdEIJYcxkjMuM5lnIs9gIilvgD44uazZE8CjNeho
oWEKUGiIVkRQvEkY33PpOUcoqsmacgHAaX58H6sRpP4 QHeghlsOOlYNMwhMHT4o7DeuyxGP/3wyqm94HUHjn44
--- KH+IYh4+bS3JMeEmFYakwIceMxOrlEZj0Fqt3VMgFRk --- zRG6aCTS+X18VpeN+tz38kaUoilk1kN5KrWTWYZ6pV4
96¨ºà·ènÅϬuk!ß±1ÝNItŽNŸ8EçwĹ]3µ”S*¡õ«0>!ý9zc‡(”2OI.^jC”&$ºÚ\ÛËWtÇÃNÿ#Õ€Å3¾ÜøÞÌÏcMuÈAߢ•<¾)¬´¼a¥rdí'pÄggPä5’ÆõOQòNfà”×1AZ|1v\š4F›‡Ò 6;„T<l£ ræX _qÔÁ’Ð껿H#p¯f™”}(žA(ã|»?ë0ªyJk¥SD‡\Jm&uõà &Ô9€ýÄ5Ù+çÊ…!v%Y˜ù~ãÁ$û“šZÇÓ° j„z–Â\ßá1,Vf˜
£’æ1zª»#Ó
-31
View File
@@ -1,31 +0,0 @@
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IDVrd2NzQSBJV1Fa
U2gzWjJKOHlsK202dXAzVENFdmZ5ZmJsc01STnRnTWMvdjFyZ21NCjU5Q0hwbVlV
dVlRTUZDb0JESHBaTzk5ancrTWNpLzYrMFk2QWU0Z0EvUmsKLT4gc3NoLWVkMjU1
MTkgM0JjcjF3IEdia0tSQTZWcEE3ODE4VjU2M0ZGay9rbXlzbFNqZWxpb1lGSExw
RW5keGsKWDlwRzcwT1BaTDByT0R6T3hqWTE1UWVyN2xhSzhSMlhBdW5FakNjZ1Ux
NAotPiBzc2gtZWQyNTUxOSA5ZDRZSVEgcnNJRnA2SVVkMHRXZTErc0VFbmduMDBz
ZkQzK3dMQ2xadkMyYllhOE1rOAprWmg4T3RiaTF5QXdmRGUxVDVpVnZWdWxRYTJv
WGVUQUhsT0VjKzl0NnJrCi0+IHNzaC1lZDI1NTE5IDROTEtydyAvc1ZTc0hRd3R4
V2U4dEpaeEZEZlVyc2ZHSlNiVSt3T1M3bndsVXpPNkhVCmt2WlplcVcza2YxV05T
bnRIOWpMYWpxck5jSzRtQVZ2L2ZQS3J1VjVTWDgKLT4gc3NoLWVkMjU1MTkgYzRO
UWxBIHZCKzR5RXN2RmYvc3ZPTGJRRG1peUN6bmNuVFpSd2NPdWJwTTJYdHllRW8K
TEo4QUI1a3ZzQ3NSQldSN1lTMlVwVGhTcTRPaHpYVHZVdHowYmZqR1czVQotPiBz
c2gtcnNhIERRbEU3dwpvRzJjS0h3b2p4RXJrSDI1VEszQ1BvckNUS2laV3hGVVlx
TXd0VkcvQ1hvWElNWnVNa1JoeHhMTDFUUWZnV0YxCjNHajRtZ0lmQzVXZmhyM1Nq
SWoyVTFFdmowNTdZV0J0WHE5cXdZb2U1OVJOb1hYb1lnSjdqN09Bd2IvOUFlckMK
d3hBc3pUK1J2Y3VnTjNSR0Q0cVJUVlluNllBazJ2L1dmOXVvZXI0WjVYbjI1RXhr
Rm1EMzkxcStrYWRJTFNJKwo3YjZ6MEZ3RmtLZytiWGdBWWZEYU9EQWxEZWtzV3dD
dGhJUUd0ajcvUFlxVGJqTVhlWmhSeHpkMHQyWWs5ZS9DClNEV0gzcWM2YlJTQ1lM
bE4zTFJKSFU1VjAybFJvY1FQR2tNSFo2NFVDczBTUW9UWjY4aDVNUzlYaVlkdzJm
dUsKckVaVld4YXpsdGYwWHVrWEtTbDJBU2cwcXlJSGY4ZEhlT2o4QlRBVGxWRHNL
OXlMTGp5b1ZWbnJYV2pzaFFIcApQVmJzVi9kWnRpeWcyaS9weVN3SncxQjhlanBV
dW5FU0VtL2lpS0NSTWtoOVI4akxXOUlhWW5xMkp2SUszSU9QCk1rZVdoNkI5TGRB
L2twWWpPYVlSWUxrRElRcjhHMFczM29lcWlWWlJhbk0vcGFEQmw3UGdXWXJBNXJV
VURYcFQKCi0+IHNzaC1lZDI1NTE5IENTTXloZyBpOEE3YUFzcytOYndXWWNreThp
ZEdveGFlbHlPa0FwZ2xBY0lCcmIycTM4CnI0ZE1DM2d1bzZPV3diL2lZQi9SUnB1
UWFjVVFueTFpMzRINm9Ob0pZMTQKLS0tIFJwZy9uRklOUEl0Z2hDNmx1YWsyNVov
aHZ5VVhtMnVyYTMwbCtkaFFtR28KZzkT96InPG4YYyVKq0ZOrIBPtCJBbTXUJu+8
9G03diIRwzYb6cSBRMtKKGl5NEfbJE7B2OnXeHPeCIPiGArKudKxFg9COHGOUP0h
hUDDL9RGVhd4sMs8zRkxNghRwQzD
-----END AGE ENCRYPTED FILE-----
-30
View File
@@ -1,30 +0,0 @@
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IDVrd2NzQSBKZllV
QytCeGYyVmtRMUxudm5iWmF3aWdITlRQVE53RnhFNUFabXkwbkg0CkE5Wk1mbXdm
TzRuQTdvNklORUdSdHFIZm1zSVdvMWVnWWpRTUxDSzFBMHcKLT4gc3NoLWVkMjU1
MTkgM0JjcjF3IDlISlRMdC9Sc2hic3JRV1o0WDNsbzByNmdFNnZDdjJSLytEa0xS
VzRzQ2sKYXJGSjJDa2xuYThLNnhIMFhwTTNUSHJWbnFFYzZSRFQxVlVWK2xjNWgv
dwotPiBzc2gtZWQyNTUxOSA5ZDRZSVEgaGUweXhJZmhTOGt3VFQ2R04rQjNOckxx
c3BCMTBWZ0lsQmU3NmlVRkRYWQp6QlMvQkZmemVjekdLVWlDTUlNRG04MTlzaHAz
cW9xd2JjNlE0eFF4NzNvCi0+IHNzaC1lZDI1NTE5IDROTEtydyBWQ2FGUCtFQ2FZ
Wk5uSzZyTDNBTHRPUTkvTlRWR0Q1bHdqRjBycmU0VFc0CkdpSUNZZGw1aGJwUVk0
VnlIR2IxVTJNaFVSNHJQSDR2YWxuN2ZiSERQSUEKLT4gc3NoLWVkMjU1MTkgYzRO
UWxBIHlDYUlJRGxabTZxSnNpTUNWUDRXWUtHa0h3Z3BYTlZkUEtkall1OUh1VzQK
ZEQzSW5tM0xFdWd1a3hibXlxa0N2N2RlemUyMkplRW5CNFZHUCtTSEhWdwotPiBz
c2gtcnNhIERRbEU3dwpoNE9FdlpHYXJFZVRYQnBZNFZPTUhLb3RDdWpNcWxtU2gv
MVF5Tm1tdHBuK3NLQjBnM2JudFFwQVZnb0VpUUljCjhKQk5HQnVrY05yeU53QWhK
MG9QUVN5M2NXUEw2eEhQajFmaHZ4cWpFaXcwd3ZrSzNRWFNRREl5bERac1Y2LzYK
c3BDVXlvQ20xaUt4KzZVRkhiNU5TYkpsbGQraFdvM3RvSkFZRU54SUV4ZWNnVG0y
VVR6Q2swV2RFeUZwNGZxdQoveVhYN3JXVXBpQzdxa1ZKVXROcXRDQXladml5c2ZD
ZjFVSVRaUkZiRVlNUHA3bDg2ek9mcXJnVG9YYStza3BFCktzVXlyZHNCZ1BxYTBO
TUt1T3ZYQmR2VjErdVpPWVBOVDlFZm14Ukp0dkdUamNxODZPNFNTMXNabzNWUzVT
MHIKUkJocFBhNitCMzFkWUhyV29FU3RMWjkvMWNxbzJFbTBHb2NpbW9Cb1dheHhn
Y0N6S2FIQ2NROWtqQm5ORWxtYwo1NnF2RVQybnY2Wm5CQlJZOXVTVVNLUGZIbWZh
V2laZWFQaXhEaGdma3pIWnlSaFpjSlRqNHlRZGNiaWhsWXlYCi9UM0Z2MldUMW01
ekZja1hLNXptaFVCVFN5UVVlRGh0L2wvekxEckNmNTJOTUdoUE9wRzFaNzhqQnpM
dThmWDUKCi0+IHNzaC1lZDI1NTE5IENTTXloZyB5ZzV4Q1pIOGpBOEtUcG1rNVZB
QjFPbEt0OThxNWl3V1VhRTV2RFVrUGhRCmpwSHpUTnhNdlRHMnRvYWk2emJlSlhJ
aWwweUg5dG1rYmRMMEVDTVdHdnMKLS0tIHp6alVpN1IvQ2E5UEV0Zm1nQk53cWJQ
WCtTN08wSXlCSktkaDlPRG9Wa0UKyLX9C4xDpcPIVFsimn4OmCWAKZ1IPxeSzgr0
W6Shg1EWCeMm3dQVJ8O9mji4JW/SJHKwqlJvFTMPhIwcdIo=
-----END AGE ENCRYPTED FILE-----
-31
View File
@@ -1,31 +0,0 @@
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IDVrd2NzQSBTNk5O
dmZTRnRnOU5JMHU4QitlWWMyaXhscm1aQWNQSWJmVjU3MHdvVmowClkrVTV6VVVV
Q0tiWXlkcGtHS3AwTUtqYmJwdnowMXFLcGNYb1dVZmpVTGcKLT4gc3NoLWVkMjU1
MTkgM0JjcjF3IGVpdFlFTUFZQS9IMzVaRkNHSHltcHV1QlFmYk1yRitvZjhZZzI2
ZDlTR3MKQ0RKT0tWZ0JGOUZCMTk2bitENmxnZzc1VDloNktvaVJFdGxyaVFaMjhJ
VQotPiBzc2gtZWQyNTUxOSA5ZDRZSVEgdmw1MTg1R3lLR1NFNGUzQXpnUS9TNjRq
czVtYlFzUmJnbmJ1c3JFRVZCQQoyYzBmQzdPbFVyU3diWlNuc1JoaHptOTFCUFBX
RFpDZk4rWHByOGp2bkJFCi0+IHNzaC1lZDI1NTE5IDROTEtydyBIZWdsejNPVkdZ
dkN3Z1FLTkNCVTdKSHNIOGU4UUUwdklhS0oxMlc5ekVjCnNZL0ZsV1VMU1p0eEFp
YzBOdndIRzB3KzZXNGRFaHBpVDQ4Wm1jb2E1Z1kKLT4gc3NoLWVkMjU1MTkgYzRO
UWxBIGFteVpubkxhcm80VEllY1llYkx4YXRTRHU4clNwK1F1SWlMTWxtMTYvRncK
YjhvRXBod2I5dUZaWGlyOHcwa3MvWjNPOCtKendiMk9zVE5mVlpTS1RVMAotPiBz
c2gtcnNhIERRbEU3dwpoenRXZkF2ck1LWDhxK25ldG1HVmRzVEh4TVNuaTdwQmVW
eW80TE5kYzd5YkFpZXZhdWVSdEh4VTA2TzhnMzNsCkZud1AvTUdWSHJtOWtEZzlj
ai9SdG1PRUtCN3VWQXlyamVpV1dWTEZkaFZpZHIxQ0c0eHIva1dzeDN6MlJla24K
dXcrNUxUWVdMTFpaRk1YbkszazNDdFhuSVdxbE9rVHNNWDBvbDF4WGlWT0d5RTJR
WmxCSFAzbDVVNWJneWhJZApPVnozRkRsS2J2VkFRVEpDVWJicmhhWXA0eVEvZFEz
eWtsY3ZaNEk1MzlDYVJDb09lZGRsVk9YMXhyOWJsNmJYCk5HRTRjb0RrdlRydjNs
T3prdXMvbnF0Y0FlL0VUbHNuVzZPNWJSK09TdEpuVU83dytiUDJRbmpFazludkkz
d3AKUUp6TkVWSWRHMkozR0lQR1JlNmZ5Nk05WkUwbWJaODUzNmJIVkNEazVRcFFO
ZnVZengyeFhQSlFsbUlta0tPRwo5OURlRTlPNERLWlF6aE9QaS91T0kyb2tIS0kz
L0FYNjhWVWp1Q2xqUVRialcyWkhXbTJwU0R5VmVkbE1GeGEyCmROTFErS2VsMEo1
VHdta2RObmtwMWtJTzRuaEhRbTFFbEE0V1RKbUk3SCtLWlE1cHR5c09ncThxbVY1
cnRETkUKCi0+IHNzaC1lZDI1NTE5IENTTXloZyBZbW1NSStwWGd5RE1kRkk5aTZX
alZtUWk5M3pnU1ZTSFU5UExnS0d4NkcwCjAvblBmUG5MUVVTOEpncjJjY0I1QzN1
eVVNMlVZenJ6MzVDRXFaMVgxREUKLS0tIFIwd1ZtanJuU0Q2Ym9kN3lSS1NtQlky
NC93UWJXa0tXTUVON3NmNVpUR0kKX71fenkAzKU3aIHFjLTpemNxsc5unQTy9f1O
jpfhFHRPG5HuUBtmi6Fuv2n8J8Gw70D0XKs6UgAYV5GY0Db1daJZRbgF9EExbadB
JQm3DLy8LG6KAM250ooGHKJoJSfQ
-----END AGE ENCRYPTED FILE-----
Binary file not shown.