Compare commits
2 Commits
master
..
f5ad78c6c2
| Author | SHA1 | Date | |
|---|---|---|---|
| f5ad78c6c2 | |||
| 1a17b852a1 |
@@ -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"
|
||||
}
|
||||
@@ -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`.
|
||||
@@ -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."
|
||||
}
|
||||
Generated
-4570
File diff suppressed because it is too large
Load Diff
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"name": "nixos-config-a5c",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@a5c-ai/babysitter-sdk": "latest"
|
||||
}
|
||||
}
|
||||
@@ -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'],
|
||||
}));
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
@@ -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."
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,3 +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-ycz","title":"Build hermes-agent skills using mkSkills:w","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.mkSkills 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}
|
||||
|
||||
@@ -46,10 +46,3 @@ CLAUDE.md
|
||||
.dolt/
|
||||
*.db
|
||||
.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
File diff suppressed because it is too large
Load Diff
@@ -15,7 +15,7 @@
|
||||
url = "github:nix-community/home-manager";
|
||||
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-45570c2.url = "github:nixos/nixpkgs/45570c299dc2b63c8c574c4cd77f0b92f7e2766e";
|
||||
nixpkgs-locked.url = "github:nixos/nixpkgs/2744d988fa116fc6d46cdfa3d1c936d0abd7d121";
|
||||
@@ -25,6 +25,7 @@
|
||||
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";
|
||||
@@ -38,16 +39,41 @@
|
||||
|
||||
nixos-generators = {url = "github:nix-community/nixos-generators";};
|
||||
|
||||
hyprpanel.url = "github:Jas-SinghFSU/HyprPanel";
|
||||
rose-pine-hyprcursor.url = "github:ndom91/rose-pine-hyprcursor";
|
||||
nix-colors.url = "github:misterio77/nix-colors";
|
||||
|
||||
m3ta-home = {
|
||||
url = "git+ssh://gitea@code.m3ta.dev/m3tam3re/m3ta-home";
|
||||
# url = "path:/home/m3tam3re/p/NIX/m3ta-home";
|
||||
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 = {
|
||||
url = "github:rustfs/rustfs-flake";
|
||||
@@ -62,6 +88,7 @@
|
||||
nixpkgs,
|
||||
m3ta-nixpkgs,
|
||||
nur,
|
||||
agents,
|
||||
...
|
||||
} @ inputs: let
|
||||
inherit (self) outputs;
|
||||
@@ -164,6 +191,11 @@
|
||||
inherit system;
|
||||
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 {
|
||||
default = pkgs.mkShell {
|
||||
buildInputs = with pkgs; [
|
||||
@@ -174,6 +206,7 @@
|
||||
statix
|
||||
deadnix
|
||||
];
|
||||
inherit (rules) instructions shellHook;
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
@@ -21,8 +21,7 @@
|
||||
useGlobalPkgs = true;
|
||||
useUserPackages = true;
|
||||
extraSpecialArgs = {
|
||||
inputs = inputs // {agents = null;};
|
||||
inherit outputs system;
|
||||
inherit inputs outputs system;
|
||||
videoDrivers = config.services.xserver.videoDrivers or [];
|
||||
};
|
||||
};
|
||||
|
||||
@@ -39,11 +39,6 @@
|
||||
outline = 3019;
|
||||
authentik = 3023;
|
||||
tuwunel = 3024;
|
||||
honcho = 3025;
|
||||
|
||||
# Agent infrastructure
|
||||
hermes-api = 8642;
|
||||
hermes-dashboard = 9119;
|
||||
|
||||
# Home automation
|
||||
homarr = 7575;
|
||||
|
||||
@@ -90,7 +90,6 @@
|
||||
hyprland.enable = true;
|
||||
rofi.enable = true;
|
||||
wayland.enable = true;
|
||||
dms.enable = true;
|
||||
};
|
||||
apps = {
|
||||
crypto.enable = true;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
{pkgs, ...}: {
|
||||
imports = [
|
||||
./containers
|
||||
./greetd.nix
|
||||
./hermes-agent.nix
|
||||
./netbird.nix
|
||||
#./n8n.nix
|
||||
|
||||
@@ -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 - -"
|
||||
];
|
||||
}
|
||||
@@ -17,21 +17,10 @@ in {
|
||||
settings = {
|
||||
# ── Model ──────────────────────────────────────────────────────────
|
||||
model = {
|
||||
default = "gpt-5.5";
|
||||
provider = "openai-codex";
|
||||
default = "glm-5.1";
|
||||
provider = "zai";
|
||||
};
|
||||
|
||||
fallback_providers = [
|
||||
{
|
||||
provider = "zai";
|
||||
model = "glm-5.1";
|
||||
}
|
||||
{
|
||||
provider = "minimax";
|
||||
model = "MiniMax-M2.7";
|
||||
}
|
||||
];
|
||||
|
||||
credential_pool_strategies = {
|
||||
zai = "fill_first";
|
||||
};
|
||||
|
||||
@@ -3,14 +3,6 @@
|
||||
secrets = {
|
||||
baserow-env = {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";
|
||||
};
|
||||
honcho-selfhost-env = {file = ../../secrets/honcho-selfhost-env.age;};
|
||||
honcho-selfhost-jwt-secret = {file = ../../secrets/honcho-selfhost-jwt-secret.age;};
|
||||
kestra-config = {
|
||||
file = ../../secrets/kestra-config.age;
|
||||
mode = "644";
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
imports = [
|
||||
./baserow.nix
|
||||
./ghost.nix
|
||||
./honcho.nix
|
||||
./kestra.nix
|
||||
./littlelink.nix
|
||||
./matomo.nix
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{config, ...}: {
|
||||
virtualisation.oci-containers.containers."ghost" = {
|
||||
image = "docker.io/ghost:6-alpine";
|
||||
image = "docker.io/ghost:latest";
|
||||
environmentFiles = [config.age.secrets.ghost-env.path];
|
||||
ports = ["127.0.0.1:3002:2368"];
|
||||
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
|
||||
'';
|
||||
}
|
||||
@@ -0,0 +1,422 @@
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
with lib; let
|
||||
cfg = config.services.honcho;
|
||||
in {
|
||||
options.services.honcho = {
|
||||
enable = mkEnableOption "Honcho memory server for AI agents";
|
||||
|
||||
package = mkOption {
|
||||
type = types.package;
|
||||
default = pkgs.callPackage ../../../pkgs/honcho {};
|
||||
description = "The Honcho package to use.";
|
||||
};
|
||||
|
||||
user = mkOption {
|
||||
type = types.str;
|
||||
default = "honcho";
|
||||
description = "User under which Honcho runs.";
|
||||
};
|
||||
|
||||
group = mkOption {
|
||||
type = types.str;
|
||||
default = "honcho";
|
||||
description = "Group under which Honcho runs.";
|
||||
};
|
||||
|
||||
dataDir = mkOption {
|
||||
type = types.str;
|
||||
default = "/var/lib/honcho";
|
||||
description = "Data directory for Honcho.";
|
||||
};
|
||||
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
default = 8000;
|
||||
description = "Port for the Honcho API server.";
|
||||
};
|
||||
|
||||
# ── Core Settings ──
|
||||
settings = mkOption {
|
||||
type = types.submodule {
|
||||
freeformType = types.attrsOf types.anything;
|
||||
options = {
|
||||
LOG_LEVEL = mkOption {
|
||||
type = types.str;
|
||||
default = "INFO";
|
||||
description = "Log level.";
|
||||
};
|
||||
|
||||
NAMESPACE = mkOption {
|
||||
type = types.str;
|
||||
default = "honcho";
|
||||
description = "Namespace prefix for vector stores and cache.";
|
||||
};
|
||||
|
||||
AUTH_USE_AUTH = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = "Enable JWT auth (not needed behind Traefik BasicAuth).";
|
||||
};
|
||||
|
||||
CACHE_ENABLED = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = "Enable Redis caching.";
|
||||
};
|
||||
|
||||
VECTOR_STORE_TYPE = mkOption {
|
||||
type = types.str;
|
||||
default = "pgvector";
|
||||
description = "Vector store backend.";
|
||||
};
|
||||
|
||||
SENTRY_ENABLED = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = "Enable Sentry.";
|
||||
};
|
||||
|
||||
METRICS_ENABLED = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = "Enable Prometheus metrics.";
|
||||
};
|
||||
|
||||
TELEMETRY_ENABLED = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = "Enable CloudEvents telemetry.";
|
||||
};
|
||||
};
|
||||
};
|
||||
default = {};
|
||||
description = "Honcho environment variables (env vars).";
|
||||
};
|
||||
|
||||
# ── Database ──
|
||||
database = mkOption {
|
||||
type = types.submodule {
|
||||
options = {
|
||||
createLocally = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = "Create local PostgreSQL DB and user.";
|
||||
};
|
||||
|
||||
host = mkOption {
|
||||
type = types.str;
|
||||
default = "localhost";
|
||||
};
|
||||
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
default = 5432;
|
||||
};
|
||||
|
||||
name = mkOption {
|
||||
type = types.str;
|
||||
default = "honcho";
|
||||
};
|
||||
|
||||
user = mkOption {
|
||||
type = types.str;
|
||||
default = "honcho";
|
||||
};
|
||||
|
||||
passwordFile = mkOption {
|
||||
type = types.nullOr types.path;
|
||||
default = null;
|
||||
description = "File containing DB password (exported as DB_CONNECTION_URI).";
|
||||
};
|
||||
};
|
||||
};
|
||||
default = {};
|
||||
};
|
||||
|
||||
# ── Redis ──
|
||||
redis = mkOption {
|
||||
type = types.submodule {
|
||||
options = {
|
||||
url = mkOption {
|
||||
type = types.str;
|
||||
default = "redis://localhost:6380/0";
|
||||
description = "Redis URL for Honcho caching.";
|
||||
};
|
||||
};
|
||||
};
|
||||
default = {};
|
||||
};
|
||||
|
||||
# ── LLM Provider ──
|
||||
llm = mkOption {
|
||||
type = types.submodule {
|
||||
options = {
|
||||
openaiApiKeyFile = mkOption {
|
||||
type = types.nullOr types.path;
|
||||
default = null;
|
||||
description = "File exporting LLM_OPENAI_API_KEY.";
|
||||
};
|
||||
|
||||
anthropicApiKeyFile = mkOption {
|
||||
type = types.nullOr types.path;
|
||||
default = null;
|
||||
description = "File exporting LLM_ANTHROPIC_API_KEY.";
|
||||
};
|
||||
|
||||
geminiApiKeyFile = mkOption {
|
||||
type = types.nullOr types.path;
|
||||
default = null;
|
||||
description = "File exporting LLM_GEMINI_API_KEY.";
|
||||
};
|
||||
};
|
||||
};
|
||||
default = {};
|
||||
};
|
||||
|
||||
# ── Deriver (Background Reasoning) ──
|
||||
deriver = mkOption {
|
||||
type = types.submodule {
|
||||
options = {
|
||||
enable = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
};
|
||||
|
||||
workers = mkOption {
|
||||
type = types.ints.positive;
|
||||
default = 1;
|
||||
};
|
||||
|
||||
model = mkOption {
|
||||
type = types.str;
|
||||
default = "gpt-4.1-mini";
|
||||
description = "Model for deriver reasoning.";
|
||||
};
|
||||
|
||||
transport = mkOption {
|
||||
type = types.str;
|
||||
default = "openai";
|
||||
description = "LLM transport (openai, anthropic, gemini).";
|
||||
};
|
||||
|
||||
baseUrl = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
description = "OpenAI-compatible base URL for self-hosted.";
|
||||
};
|
||||
|
||||
apiKeyFile = mkOption {
|
||||
type = types.nullOr types.path;
|
||||
default = null;
|
||||
description = "File exporting DERIVER_MODEL_CONFIG__OVERRIDES__API_KEY.";
|
||||
};
|
||||
};
|
||||
};
|
||||
default = {};
|
||||
};
|
||||
|
||||
# ── Dialectic ──
|
||||
dialectic = mkOption {
|
||||
type = types.submodule {
|
||||
options = {
|
||||
model = mkOption {
|
||||
type = types.str;
|
||||
default = "gpt-4.1-mini";
|
||||
};
|
||||
|
||||
transport = mkOption {
|
||||
type = types.str;
|
||||
default = "openai";
|
||||
};
|
||||
|
||||
baseUrl = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
};
|
||||
};
|
||||
};
|
||||
default = {};
|
||||
};
|
||||
|
||||
# ── Embedding ──
|
||||
embedding = mkOption {
|
||||
type = types.submodule {
|
||||
options = {
|
||||
model = mkOption {
|
||||
type = types.str;
|
||||
default = "text-embedding-3-small";
|
||||
};
|
||||
|
||||
transport = mkOption {
|
||||
type = types.str;
|
||||
default = "openai";
|
||||
};
|
||||
|
||||
baseUrl = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
};
|
||||
};
|
||||
};
|
||||
default = {};
|
||||
};
|
||||
};
|
||||
|
||||
config = let
|
||||
# Build the DB connection URI
|
||||
dbUri =
|
||||
if cfg.database.passwordFile != null
|
||||
then "postgresql+psycopg://${cfg.database.user}@${cfg.database.host}:${toString cfg.database.port}/${cfg.database.name}"
|
||||
else "postgresql+psycopg://${cfg.database.user}@${cfg.database.host}:${toString cfg.database.port}/${cfg.database.name}";
|
||||
|
||||
# Common env vars for both API and Deriver
|
||||
commonEnv =
|
||||
cfg.settings
|
||||
// {
|
||||
DB_CONNECTION_URI = dbUri;
|
||||
CACHE_URL = cfg.redis.url;
|
||||
PYTHON_DOTENV_DISABLED = "true";
|
||||
HONCHO_CONFIG_TOML_DISABLED = "true";
|
||||
};
|
||||
|
||||
# Shared EnvironmentFile list
|
||||
envFiles =
|
||||
(optional (cfg.llm.openaiApiKeyFile != null) cfg.llm.openaiApiKeyFile)
|
||||
++ (optional (cfg.llm.anthropicApiKeyFile != null) cfg.llm.anthropicApiKeyFile)
|
||||
++ (optional (cfg.llm.geminiApiKeyFile != null) cfg.llm.geminiApiKeyFile)
|
||||
++ (optional (cfg.deriver.apiKeyFile != null) cfg.deriver.apiKeyFile);
|
||||
in
|
||||
mkIf cfg.enable {
|
||||
# ── User & Group ──
|
||||
users.users.${cfg.user} = mkIf (cfg.user == "honcho") {
|
||||
isSystemUser = true;
|
||||
group = cfg.group;
|
||||
home = cfg.dataDir;
|
||||
createHome = true;
|
||||
};
|
||||
users.groups.${cfg.group} = mkIf (cfg.group == "honcho") {};
|
||||
|
||||
# ── API Server ──
|
||||
systemd.services.honcho-api = {
|
||||
description = "Honcho Memory API Server";
|
||||
after = ["network.target" "postgresql.service"];
|
||||
requires = ["postgresql.service"];
|
||||
wantedBy = ["multi-user.target"];
|
||||
|
||||
environment = commonEnv;
|
||||
|
||||
serviceConfig = {
|
||||
Type = "simple";
|
||||
User = cfg.user;
|
||||
Group = cfg.group;
|
||||
WorkingDirectory = cfg.package;
|
||||
|
||||
ExecStartPre = "${cfg.package}/bin/honcho-migrate";
|
||||
ExecStart = "${cfg.package}/bin/honcho-api --port ${toString cfg.port}";
|
||||
|
||||
Restart = "on-failure";
|
||||
RestartSec = "5";
|
||||
|
||||
EnvironmentFile = envFiles;
|
||||
|
||||
# Security
|
||||
NoNewPrivileges = true;
|
||||
ProtectSystem = "strict";
|
||||
ProtectHome = true;
|
||||
ReadWritePaths = [cfg.dataDir];
|
||||
PrivateTmp = true;
|
||||
};
|
||||
};
|
||||
|
||||
# ── Deriver Worker ──
|
||||
systemd.services.honcho-deriver = mkIf cfg.deriver.enable {
|
||||
description = "Honcho Deriver (Background Reasoning)";
|
||||
after = ["network.target" "postgresql.service" "honcho-api.service"];
|
||||
requires = ["postgresql.service"];
|
||||
wants = ["honcho-api.service"];
|
||||
wantedBy = ["multi-user.target"];
|
||||
|
||||
environment =
|
||||
commonEnv
|
||||
// {
|
||||
DERIVER_ENABLED = "true";
|
||||
DERIVER_WORKERS = toString cfg.deriver.workers;
|
||||
DERIVER_MODEL_CONFIG__TRANSPORT = cfg.deriver.transport;
|
||||
DERIVER_MODEL_CONFIG__MODEL = cfg.deriver.model;
|
||||
}
|
||||
// optionalAttrs (cfg.deriver.baseUrl != null) {
|
||||
DERIVER_MODEL_CONFIG__OVERRIDES__BASE_URL = cfg.deriver.baseUrl;
|
||||
}
|
||||
// (
|
||||
builtins.listToAttrs (
|
||||
map (level: {
|
||||
name = "DIALECTIC_LEVELS__${level}__MODEL_CONFIG__MODEL";
|
||||
value = cfg.dialectic.model;
|
||||
})
|
||||
["minimal" "low" "medium" "high" "max"]
|
||||
)
|
||||
)
|
||||
// (
|
||||
builtins.listToAttrs (
|
||||
map (level: {
|
||||
name = "DIALECTIC_LEVELS__${level}__MODEL_CONFIG__TRANSPORT";
|
||||
value = cfg.dialectic.transport;
|
||||
})
|
||||
["minimal" "low" "medium" "high" "max"]
|
||||
)
|
||||
)
|
||||
// optionalAttrs (cfg.dialectic.baseUrl != null) (
|
||||
builtins.listToAttrs (
|
||||
map (level: {
|
||||
name = "DIALECTIC_LEVELS__${level}__MODEL_CONFIG__OVERRIDES__BASE_URL";
|
||||
value = cfg.dialectic.baseUrl;
|
||||
})
|
||||
["minimal" "low" "medium" "high" "max"]
|
||||
)
|
||||
)
|
||||
// {
|
||||
EMBEDDING_MODEL_CONFIG__MODEL = cfg.embedding.model;
|
||||
EMBEDDING_MODEL_CONFIG__TRANSPORT = cfg.embedding.transport;
|
||||
}
|
||||
// optionalAttrs (cfg.embedding.baseUrl != null) {
|
||||
EMBEDDING_MODEL_CONFIG__OVERRIDES__BASE_URL = cfg.embedding.baseUrl;
|
||||
};
|
||||
|
||||
serviceConfig = {
|
||||
Type = "simple";
|
||||
User = cfg.user;
|
||||
Group = cfg.group;
|
||||
WorkingDirectory = cfg.package;
|
||||
|
||||
ExecStart = "${cfg.package}/bin/honcho-deriver";
|
||||
|
||||
Restart = "on-failure";
|
||||
RestartSec = "10";
|
||||
|
||||
EnvironmentFile = envFiles;
|
||||
|
||||
NoNewPrivileges = true;
|
||||
ProtectSystem = "strict";
|
||||
ProtectHome = true;
|
||||
ReadWritePaths = [cfg.dataDir];
|
||||
PrivateTmp = true;
|
||||
};
|
||||
};
|
||||
|
||||
# ── Local PostgreSQL ──
|
||||
services.postgresql = mkIf cfg.database.createLocally {
|
||||
ensureDatabases = [cfg.database.name];
|
||||
ensureUsers = [
|
||||
{
|
||||
name = cfg.database.user;
|
||||
ensureDBOwnership = true;
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -28,7 +28,6 @@
|
||||
host kestra kestra 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 honcho honcho 10.89.0.0/24 scram-sha-256
|
||||
|
||||
# Deny all other connections
|
||||
local all all reject
|
||||
@@ -39,7 +38,7 @@
|
||||
services.postgresqlBackup = {
|
||||
enable = true;
|
||||
startAt = "03:10:00";
|
||||
databases = ["baserow" "paperless" "kestra" "authentik" "netbird" "honcho"];
|
||||
databases = ["baserow" "paperless" "kestra" "authentik" "netbird"];
|
||||
};
|
||||
networking.firewall = {
|
||||
extraCommands = ''
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
{config, ...}: {
|
||||
services.redis.servers.honcho = {
|
||||
enable = true;
|
||||
port = 6380; # Separate from default Redis (6379) to avoid conflicts
|
||||
bind = "127.0.0.1";
|
||||
save = [
|
||||
[900 1]
|
||||
[300 10]
|
||||
[60 10000]
|
||||
];
|
||||
};
|
||||
|
||||
networking.firewall.extraCommands = ''
|
||||
iptables -A INPUT -p tcp -s 127.0.0.1 --dport 6380 -j ACCEPT
|
||||
'';
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
inputs,
|
||||
...
|
||||
@@ -8,41 +7,40 @@
|
||||
# Edge TTS: Seraphina — friendly, multilingual German female voice (free, no API key)
|
||||
edgeVoice = "de-DE-SeraphinaMultilingualNeural";
|
||||
|
||||
agentSkillExclusions = {
|
||||
m3ta-agents = [];
|
||||
anthropic = ["pdf" "skill-creator" "xlsx"];
|
||||
basecamp = [];
|
||||
kestra = [];
|
||||
mattpocock = ["grill-me" "caveman"];
|
||||
superpowers = ["brainstorming" "systematic-debugging"];
|
||||
vercel = [];
|
||||
};
|
||||
# Extra Python packages from the container's writable venv layer.
|
||||
# matrix-nio is installed via pip in /home/hermes/.venv but the hermes
|
||||
# process uses the read-only Nix store Python, so we inject the venv's
|
||||
# site-packages via PYTHONPATH and provide libstdc++ for libolm (e2e).
|
||||
# NOTE: v0.13.0 upgraded to Python 3.12 — path updated accordingly.
|
||||
venvSitePackages = "/home/hermes/.venv/lib/python3.12/site-packages";
|
||||
gccLibPath = "${pkgs.stdenv.cc.cc.lib}/lib";
|
||||
|
||||
agentLibSourceSelections =
|
||||
lib.mapAttrs (_sourceName: exclude: {
|
||||
skills = {
|
||||
all = true;
|
||||
inherit exclude;
|
||||
};
|
||||
})
|
||||
agentSkillExclusions;
|
||||
|
||||
# Deterministic store renderer consumed directly by Hermes. m3ta-home
|
||||
# re-exports the focused helper so nixos-config does not need a direct
|
||||
# agent-lib flake input.
|
||||
hermesSkills = inputs.m3ta-home.lib.mkHermesSkillsDir {
|
||||
system = pkgs.stdenv.hostPlatform.system;
|
||||
name = "hermes-agent-lib-skills";
|
||||
lockFile = ../../../agent-sources.lock.json;
|
||||
sources = agentLibSourceSelections;
|
||||
# Build skills using agents flake lib for hermes user
|
||||
hermesSkills = inputs.agents.lib.mkSkills {
|
||||
inherit pkgs;
|
||||
customSkills = "${inputs.agents}/skills";
|
||||
externalSkills = [
|
||||
{
|
||||
src = inputs.skills-basecamp;
|
||||
skillsDir = "skills";
|
||||
}
|
||||
{
|
||||
src = inputs.skills-anthropic;
|
||||
skillsDir = "skills";
|
||||
}
|
||||
{
|
||||
src = inputs.skills-kestra;
|
||||
skillsDir = "skills";
|
||||
}
|
||||
];
|
||||
};
|
||||
in {
|
||||
virtualisation.docker.enable = true;
|
||||
|
||||
systemd.tmpfiles.rules = [
|
||||
"d /var/lib/hermes/.config 0755 hermes hermes -"
|
||||
"d /var/lib/hermes/.config/tea 0755 hermes hermes -"
|
||||
"L+ /var/lib/hermes/.config/tea/yml - - - - ${pkgs.writeText "tea-yml" ''
|
||||
"d /home/hermes/.config 0755 hermes hermes -"
|
||||
"d /home/hermes/.config/tea 0755 hermes hermes -"
|
||||
"L+ /home/hermes/.config/tea/yml - - - - ${pkgs.writeText "tea-yml" ''
|
||||
logins:
|
||||
- name: m3ta
|
||||
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 = {
|
||||
enable = 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; [
|
||||
basecamp
|
||||
docker
|
||||
git
|
||||
curl
|
||||
jq
|
||||
tea
|
||||
nix
|
||||
python3Minimal
|
||||
uv
|
||||
zellij
|
||||
];
|
||||
|
||||
@@ -108,7 +105,7 @@ in {
|
||||
# Bind to 0.0.0.0 so the Netbird interface can reach it.
|
||||
API_SERVER_ENABLED = "true";
|
||||
API_SERVER_HOST = "0.0.0.0";
|
||||
API_SERVER_PORT = toString (config.m3ta.ports.get "hermes-api");
|
||||
API_SERVER_PORT = "8642";
|
||||
};
|
||||
|
||||
# ── Container mode (podman) ──────────────────────────────────────────
|
||||
@@ -116,27 +113,21 @@ in {
|
||||
enable = false;
|
||||
backend = "podman";
|
||||
extraVolumes = ["/home/m3tam3re/p:/projects:rw"];
|
||||
extraOptions = [];
|
||||
extraOptions = [
|
||||
"--env"
|
||||
"PYTHONPATH=${venvSitePackages}"
|
||||
"--env"
|
||||
"LD_LIBRARY_PATH=${gccLibPath}"
|
||||
];
|
||||
};
|
||||
|
||||
settings = {
|
||||
# ── Model ──────────────────────────────────────────────────────────
|
||||
model = {
|
||||
default = "gpt-5.5";
|
||||
provider = "openai-codex";
|
||||
default = "glm-5.1";
|
||||
provider = "zai";
|
||||
};
|
||||
|
||||
fallback_providers = [
|
||||
{
|
||||
provider = "zai";
|
||||
model = "glm-5.1";
|
||||
}
|
||||
{
|
||||
provider = "minimax";
|
||||
model = "MiniMax-M2.7";
|
||||
}
|
||||
];
|
||||
|
||||
credential_pool_strategies = {
|
||||
zai = "fill_first";
|
||||
};
|
||||
@@ -148,14 +139,13 @@ in {
|
||||
max_turns = 90;
|
||||
gateway_timeout = 1800;
|
||||
tool_use_enforcement = "auto";
|
||||
reasoning_effort = "high";
|
||||
};
|
||||
|
||||
# ── Skills ─────────────────────────────────────────────────────────
|
||||
|
||||
skills = {
|
||||
external_dirs = [
|
||||
hermesSkills
|
||||
"/var/lib/hermes/.agents/skills"
|
||||
];
|
||||
};
|
||||
|
||||
|
||||
@@ -7,8 +7,6 @@
|
||||
# Netbird mesh VPN range — dashboard only accessible from mesh peers.
|
||||
# m3-atlas Traefik proxies to this port over Netbird.
|
||||
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
|
||||
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 ───────────────────────────────────
|
||||
# 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).
|
||||
# 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 = {
|
||||
description = "Hermes Agent Web Dashboard";
|
||||
after = ["network.target" "hermes-agent.service"];
|
||||
@@ -31,7 +29,7 @@ in {
|
||||
User = "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 = [
|
||||
@@ -53,17 +51,15 @@ in {
|
||||
};
|
||||
};
|
||||
|
||||
# ── Firewall: Hermes network endpoints only from Netbird mesh ──────────
|
||||
# ── Firewall: Dashboard only from Netbird mesh ─────────────────────────
|
||||
networking.firewall = {
|
||||
extraCommands = ''
|
||||
# Allow Hermes Dashboard and OpenAI-compatible API 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 ${toString apiPort} -s ${netbirdRange} -j nixos-fw-accept
|
||||
# Allow Hermes Dashboard (9119/tcp) only from Netbird mesh VPN
|
||||
ip46tables -A nixos-fw -p tcp --dport 9119 -s ${netbirdRange} -j nixos-fw-accept
|
||||
'';
|
||||
|
||||
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 ${toString apiPort} -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
|
||||
'';
|
||||
};
|
||||
}
|
||||
|
||||
@@ -11,11 +11,10 @@
|
||||
boot.supportedFilesystems = ["zfs"];
|
||||
boot.zfs.package = pkgs.zfs_unstable;
|
||||
boot.zfs.forceImportAll = false;
|
||||
boot.zfs.forceImportRoot = false;
|
||||
boot.loader.systemd-boot.enable = true;
|
||||
boot.loader.efi.canTouchEfiVariables = true;
|
||||
boot.initrd.kernelModules = ["amdgpu"];
|
||||
boot.kernelPackages = pkgs.linuxPackages_7_0;
|
||||
boot.kernelPackages = pkgs.linuxPackages_6_18;
|
||||
services.xserver.videoDrivers = ["amdgpu"];
|
||||
security.polkit.enable = true;
|
||||
security.pam.services.gdm.enableGnomeKeyring = true;
|
||||
|
||||
@@ -48,7 +48,6 @@
|
||||
podman.enable = true;
|
||||
virtualisation.enable = true;
|
||||
};
|
||||
services.power-profiles-daemon.enable = true;
|
||||
services.ollama = {
|
||||
environmentVariables = {
|
||||
# HCC_AMDGPU_TARGET = "gfx1103";
|
||||
|
||||
@@ -8,14 +8,9 @@
|
||||
...
|
||||
}:
|
||||
with lib; {
|
||||
imports = [
|
||||
];
|
||||
|
||||
config = mkMerge [
|
||||
# ── XDG / MIME defaults ──
|
||||
{
|
||||
qt.platformTheme.name = mkForce "qtct";
|
||||
|
||||
xdg = {
|
||||
enable = true;
|
||||
configFile."mimeapps.list".force = true;
|
||||
@@ -59,24 +54,9 @@ with lib; {
|
||||
"6, monitor:DP-2"
|
||||
"7, monitor:DP-2"
|
||||
];
|
||||
# m3ta-home sets QT_QPA_PLATFORMTHEME=gtk3 globally for Hyprland.
|
||||
# ksnip crashes with duplicate GDK type registration under that Qt GTK
|
||||
# platform theme, so use qtct for Qt apps on this host instead.
|
||||
env = mkForce [
|
||||
"XCURSOR_SIZE,32"
|
||||
"HYPRCURSOR_THEME,Bibata-Modern-Ice"
|
||||
"WLR_NO_HARDWARE_CURSORS,1"
|
||||
"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 Msty, 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"
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
{pkgs, ...}: {
|
||||
imports = [
|
||||
./containers
|
||||
./greetd.nix
|
||||
./mem0.nix
|
||||
# ./n8n.nix
|
||||
./netbird.nix
|
||||
@@ -30,6 +29,6 @@
|
||||
userServices = true;
|
||||
};
|
||||
};
|
||||
# displayManager.gdm.enable = true;
|
||||
displayManager.gdm.enable = true;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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 - -"
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
{
|
||||
lib,
|
||||
stdenv,
|
||||
fetchFromGitHub,
|
||||
python3,
|
||||
}:
|
||||
# NOTE: First build will fail with a hash mismatch error.
|
||||
# Copy the "got: sha256-XXX..." from the error and replace fakeHash below.
|
||||
let
|
||||
version = "3.0.6";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "plastic-labs";
|
||||
repo = "honcho";
|
||||
tag = "v${version}";
|
||||
hash = lib.fakeHash;
|
||||
};
|
||||
|
||||
pythonEnv = python3.withPackages (ps:
|
||||
with ps; [
|
||||
# Core web framework
|
||||
fastapi
|
||||
uvicorn
|
||||
httptools
|
||||
python-dotenv
|
||||
sqlalchemy
|
||||
fastapi-pagination
|
||||
|
||||
# Database & vector
|
||||
pgvector
|
||||
psycopg
|
||||
greenlet
|
||||
|
||||
# LLM providers
|
||||
openai
|
||||
google-genai
|
||||
|
||||
# Utilities
|
||||
httpx
|
||||
rich
|
||||
nanoid
|
||||
alembic
|
||||
pyjwt
|
||||
tenacity
|
||||
tiktoken
|
||||
langfuse
|
||||
pydantic
|
||||
pydantic-settings
|
||||
pdfplumber
|
||||
typing-extensions
|
||||
json-repair
|
||||
scikit-learn
|
||||
prometheus-client
|
||||
cloudevents
|
||||
|
||||
# Vector store backends
|
||||
turbopuffer
|
||||
lancedb
|
||||
pyarrow
|
||||
|
||||
# Cache
|
||||
redis
|
||||
cashews
|
||||
|
||||
# Observability
|
||||
sentry-sdk
|
||||
]);
|
||||
in
|
||||
stdenv.mkDerivation {
|
||||
pname = "honcho";
|
||||
inherit version src;
|
||||
|
||||
buildInputs = [pythonEnv];
|
||||
|
||||
buildPhase = ''
|
||||
rm -rf sdks honcho-cli
|
||||
'';
|
||||
|
||||
installPhase = ''
|
||||
mkdir -p $out/{src,migrations,scripts,bin}
|
||||
|
||||
cp -r src/* $out/src/
|
||||
cp -r migrations/* $out/migrations/
|
||||
cp scripts/provision_db.py $out/scripts/
|
||||
cp alembic.ini $out/
|
||||
|
||||
# API wrapper
|
||||
cat > $out/bin/honcho-api << 'WRAPPER'
|
||||
#!/bin/sh
|
||||
exec ${pythonEnv}/bin/python -c "
|
||||
import sys, os
|
||||
app_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
os.chdir(app_dir)
|
||||
sys.path.insert(0, app_dir)
|
||||
os.environ.setdefault('PYTHON_DOTENV_DISABLED', 'true')
|
||||
os.environ.setdefault('HONCHO_CONFIG_TOML_DISABLED', 'true')
|
||||
import uvicorn
|
||||
port = int(os.environ.get('PORT', '8000'))
|
||||
for i, arg in enumerate(sys.argv[1:]):
|
||||
if arg.startswith('--port='):
|
||||
port = int(arg.split('=',1)[1])
|
||||
elif arg == '--port':
|
||||
nxt = sys.argv[i+2:i+3]
|
||||
if nxt: port = int(nxt[0])
|
||||
uvicorn.run('src.main:app', host='0.0.0.0', port=port)
|
||||
" "$@"
|
||||
WRAPPER
|
||||
chmod +x $out/bin/honcho-api
|
||||
|
||||
# Deriver wrapper
|
||||
cat > $out/bin/honcho-deriver << 'WRAPPER'
|
||||
#!/bin/sh
|
||||
exec ${pythonEnv}/bin/python -c "
|
||||
import sys, os
|
||||
app_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
os.chdir(app_dir)
|
||||
sys.path.insert(0, app_dir)
|
||||
os.environ.setdefault('PYTHON_DOTENV_DISABLED', 'true')
|
||||
os.environ.setdefault('HONCHO_CONFIG_TOML_DISABLED', 'true')
|
||||
from src.deriver.__main__ import *
|
||||
" "$@"
|
||||
WRAPPER
|
||||
chmod +x $out/bin/honcho-deriver
|
||||
|
||||
# Migration wrapper
|
||||
cat > $out/bin/honcho-migrate << 'WRAPPER'
|
||||
#!/bin/sh
|
||||
exec ${pythonEnv}/bin/python -c "
|
||||
import sys, os, asyncio
|
||||
app_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
os.chdir(app_dir)
|
||||
sys.path.insert(0, app_dir)
|
||||
os.environ.setdefault('PYTHON_DOTENV_DISABLED', 'true')
|
||||
os.environ.setdefault('HONCHO_CONFIG_TOML_DISABLED', 'true')
|
||||
from src.db import init_db
|
||||
asyncio.run(init_db())
|
||||
" "$@"
|
||||
WRAPPER
|
||||
chmod +x $out/bin/honcho-migrate
|
||||
'';
|
||||
|
||||
passthru = {
|
||||
inherit pythonEnv;
|
||||
python = pythonEnv;
|
||||
};
|
||||
|
||||
meta = {
|
||||
description = "Honcho — Memory system for stateful AI agents";
|
||||
homepage = "https://honcho.dev";
|
||||
license = lib.licenses.agpl3Only;
|
||||
platforms = lib.platforms.linux;
|
||||
};
|
||||
}
|
||||
@@ -38,9 +38,6 @@ in {
|
||||
"secrets/basecamp-client-id.age".publicKeys = systems ++ users;
|
||||
"secrets/basecamp-client-secret.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/restreamer-env.age".publicKeys = systems ++ users;
|
||||
"secrets/searx.age".publicKeys = systems ++ users;
|
||||
|
||||
@@ -1,25 +1,26 @@
|
||||
age-encryption.org/v1
|
||||
-> ssh-ed25519 4NLKrw 42tBp6EbDJpC7EBt0++QxmF3N9rQJ/AP+7A/S174rCs
|
||||
bRzpQku0GLEBvANvCdeH3L4Kf06k6w2C4FfZCOp2QWI
|
||||
-> ssh-ed25519 5kwcsA YAYkQzsxfbHwrCPMW2eqLS9mRuuxr+EjHKl7MV3DDEo
|
||||
dN3TitETbdPbXzBtIDBglienhY4oDsFGgfe0VYdsP1o
|
||||
-> ssh-ed25519 9d4YIQ 2vTWMSuLrgpgaTWeu0ARoUOukLBKupCfMdqJhLvTqwA
|
||||
Lzk2Uo2U3tUJiq29on/a5zYfuUjgOZvCHhZYuFGSDG4
|
||||
-> ssh-ed25519 3Bcr1w x689Z0/TsOLLk1JNPXg2jj6y5ucaH37zRt46d/Z1l2w
|
||||
Bkzg3umkDYFBemmgev/M5LUFuobFugXe0u85mLmsDSo
|
||||
-> ssh-ed25519 c4NQlA 5Dn6e8bILaYl9FVt+ZwuZ6rOC0k0Kg1+KOSP4JakyWI
|
||||
AT6LeCo+P7RjgNhRex04kJ/7NHD2DAWRqs33uOJ7e5E
|
||||
-> ssh-ed25519 4NLKrw 2TwbZwX9SwWg4SVC0A2ICmyRjSfO+xtfBcBOK1lh3T4
|
||||
DSf4DrOAvW7L49lh6cq5IqrMM7gqXv2+67rR3ttn+CE
|
||||
-> ssh-ed25519 5kwcsA K1hqFOAxq2T+oLp3bQjLYpXtlQVkA7RHCM/8ETMGbwU
|
||||
xIE4xz50LB5vbDTTLKVcx9vC2iXIsRLThHYYxGjcJyY
|
||||
-> ssh-ed25519 9d4YIQ bXYb62OM/N+EXpMOZZ6zEbpfaH10Vz62PuUdGODXolw
|
||||
j64kKzOn8CmSnykEuWnXHZ0nfqwOfOxX4FPR4GSouR0
|
||||
-> ssh-ed25519 3Bcr1w C4alN6ud7q0K4I7NHuBgC77D6zeTfZVGjNS3EKpvL00
|
||||
NpjOsg3eJ5LvX0lV7NYuVHLeqeYylHdmw60H+KeG1GY
|
||||
-> ssh-ed25519 c4NQlA In5wsg4+LTIEbP75B83GMXPCItSPGwKWUW8QO+QjXyY
|
||||
oK1kikhr4RMq6QMv9kjNjiKrf5srlGh7hGbU2qns2rM
|
||||
-> ssh-rsa DQlE7w
|
||||
M9pUnzZDa1v6X5UbQOE6HILaGU36VkQtnfXaJJdxJSRQ/sE9R3ZQoLjRZAw+UhUf
|
||||
09JwLkS55477xaar3bpvvOxeP4MrtTHLJ7593eEkFT3i45FfVmxutq6EYckZrCJB
|
||||
WjrCG7Cbvc20o6s54PYiF4Xk8AuPxt+SElRxBtcOK+SPba84f+WWHqrBA1YRzTDK
|
||||
fsM15eKWsJgzaz5y36grv4xSj4KbWMFtmEt5V5BEW32+zXBU5CPhonO59TxEQgh4
|
||||
hI2+gNmAzKQja7xbuxCyr3jcXWJz7IuXcrklr+2ZjF1wx3BDll1z+vxSX0C88MCc
|
||||
OLKDfnUiDa6BlgUfLK90dLIia8v0oIPXs4OWRfYs7SC/Z3QOPpSO62Ky9dKYRrod
|
||||
PHvCgxX28QvROE4TekL9PV81AfAbMVJrnkRiybg6id8CscldtDmgaKqoaIoJlAuF
|
||||
g5/LGd+FPfmlv2iNfGUn2Glhui8SkrBK1MzGJpeQw+l4CXLH33yQzHX0m6TdQBzr
|
||||
tcP4yPgGWqHYeE1gw/KD6cswik+9WU2s2f7hg5mK78085sQ7npXRsBVAz2OCRn07
|
||||
foeAAmnY4YmKriBh421JOVNBDOXHR5dfaIKY9b663L+rYj99ic0rfW26C+dqKitF
|
||||
SnvveL3Zf16nqg6duSVA7LIcIFgkIlA+RXnHPVho+P4GwEH7W8nCf/4kUquuhB7B
|
||||
F4Hx1qOknmGyNBJBFi27D04ZDDk/ZVxioYsO6P6TUu7MuaGmQCoVKREDl5RRh4zO
|
||||
XD8/TFDRsJLqqcbCKIlU+6CN1+L0r4FN4K0UaTjwPNzGvn5EEjBKw9RpOhdvI28I
|
||||
WlAQ+w6gdQiz9Ju4e5p7Doz2MbNb6894DimawHjzl968Xy5ifX2XA+FBdcW5hU9A
|
||||
u+7VXKZmbfMyvRA7lmKRoi4SurJAyQd6iXBrVKfTwFc53V/tJi48bsKcE3yXxHH+
|
||||
lKGuZFNGDDkqCruycjvz94WaIHy3fv5hhmBdgwoCZK1VGSLAnwdm1rG4B9m3t/K8
|
||||
|
||||
-> ssh-ed25519 CSMyhg 5YHqBNbkkUFVhDEfOM4P2tAxT2t1rDn5KItUcjUs4DY
|
||||
oWEKUGiIVkRQvEkY33PpOUcoqsmacgHAaX58H6sRpP4
|
||||
--- KH+IYh4+bS3JMeEmFYakwIceMxOrlEZj0Fqt3VMgFRk
|
||||
�96¨ºà·ènÅϬuk!ß±1ÝNItŽNŸ8E�çwĹ]3µ”S*¡õ«0>!ý9zc‡(”2O;åI.^jC”&$ºÚ\ÛËWtÇÃNÿ#Õ€Å3¾ÜøÞÌÏcMuÈAߢ•<¾)¬MÄ´¼a¥rdí'p’ÄggPä5’ÆõOQòNfà”×1AZ|1v\š4F›‡�Ò6;„T<l£
|
||||
-> ssh-ed25519 CSMyhg FNYYdEIJYcxkjMuM5lnIs9gIilvgD44uazZE8CjNeho
|
||||
QHeghlsOOlYNMwhMHT4o7DeuyxGP/3wyqm94HUHjn44
|
||||
--- zRG6aCTS+X18VpeN+tz38kaUoilk1kN5KrWTWYZ6pV4
|
||||
ræX_qÔÁ’Ð껿H#p¯f™”}(žA(ã|»?ë0ªyJk¥SD‡\Jm&uõÃ&Ô9€ýÄ5Ù+çÊ…!v%Y˜ù~ãÁ$û“šZÇÓ°j„z–Â\ßá1,Vf˜
|
||||
£’æ1zª»#Ó
|
||||
Binary file not shown.
@@ -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-----
|
||||
@@ -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-----
|
||||
@@ -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.
Reference in New Issue
Block a user