# Formalization Audit Report **Date:** 2026-04-06 **Auditor:** Allegro (subagent) **Scope:** All homebrew components on VPS 167.99.126.228 --- ## Executive Summary This system runs a fleet of 5 Hermes AI agents (allegro, adagio, ezra, bezalel, bilbobagginshire) alongside supporting infrastructure (Gitea, Nostr relay, Evennia MUD, Ollama). The deployment is functional but heavily ad-hoc — characterized by one-off systemd units, scattered scripts, bare `docker run` containers with no compose file, and custom glue code where standard tooling exists. **Priority recommendations:** 1. **Consolidate fleet deployment** into docker-compose (HIGH impact, MEDIUM effort) 2. **Clean up burn scripts** — archive or delete (HIGH impact, LOW effort) 3. **Add docker-compose for Gitea + strfry** (MEDIUM impact, LOW effort) 4. **Formalize the webhook receiver** into the hermes-agent repo (MEDIUM impact, LOW effort) 5. **Recover or rewrite GOFAI source files** — only .pyc remain (HIGH urgency) --- ## 1. Gitea Webhook Receiver **File:** `/root/wizards/allegro/gitea_webhook_receiver.py` (327 lines) **Service:** `allegro-gitea-webhook.service` ### Current State Custom aiohttp server that: - Listens on port 8670 for Gitea webhook events - Verifies HMAC-SHA256 signatures - Filters for @allegro mentions and issue assignments - Forwards to Hermes API (OpenAI-compatible endpoint) - Posts response back as Gitea comment - Includes health check, event logging, async fire-and-forget processing Quality: **Solid.** Clean async code, proper signature verification, sensible error handling, daily log rotation. Well-structured for a single-file service. ### OSS Alternatives - **Adnanh/webhook** (Go, 10k+ stars) — generic webhook receiver, but would need custom scripting anyway - **Flask/FastAPI webhook blueprints** — would be roughly equivalent effort - **Gitea built-in webhooks + Woodpecker CI** — different architecture (push-based CI vs. agent interaction) ### Recommendation: **KEEP, but formalize** The webhook logic is Allegro-specific (mention detection, Hermes API forwarding, comment posting). No off-the-shelf tool replaces this without equal or more glue code. However: - Move into the hermes-agent repo as a plugin/skill - Make it configurable for any wizard name (not just "allegro") - Add to docker-compose instead of standalone systemd unit **Effort:** 2-4 hours --- ## 2. Nostr Relay + Bridge ### Relay (strfry + custom timmy-relay) **Running:** Two relay implementations in parallel 1. **strfry** Docker container (port 7777) — standard relay, healthy, community-maintained 2. **timmy-relay** Go binary (port 2929) — custom NIP-29 relay built on `relay29`/`khatru29` The custom relay (`main.go`, 108 lines) is a thin wrapper around `fiatjaf/relay29` with: - NIP-29 group support (admin/mod roles) - LMDB persistent storage - Allowlisted event kinds - Anti-spam policies (tag limits, timestamp guards) ### Bridge (dm_bridge_mvp) **Service:** `nostr-bridge.service` **Status:** Running but **source file deleted** — only `.pyc` cache remains at `/root/nostr-relay/__pycache__/dm_bridge_mvp.cpython-312.pyc` From decompiled structure, the bridge: - Reads DMs from Nostr relay - Parses commands from DMs - Creates Gitea issues/comments via API - Polls for new DMs in a loop - Uses keystore.json for identity management **CRITICAL:** Source code is gone. If the service restarts on a Python update (new .pyc format), this component dies. ### OSS Alternatives - **strfry:** Already using it. Good choice, well-maintained. - **relay29:** Already using it. Correct choice for NIP-29 groups. - **nostr-tools / rust-nostr SDKs** for bridge — but bridge logic is custom regardless ### Recommendation: **KEEP relay, RECOVER bridge** - The relay setup (relay29 custom binary + strfry) is appropriate - **URGENT:** Decompile dm_bridge_mvp.pyc and reconstruct source before it's lost - Consider whether strfry (port 7777) is still needed alongside timmy-relay (port 2929) — possible to consolidate - Move bridge into its own git repo on Gitea **Effort:** 4-6 hours (bridge recovery), 1 hour (strfry consolidation assessment) --- ## 3. Evennia / Timmy Academy **Path:** `/root/workspace/timmy-academy/` **Components:** | Component | File | Custom? | Lines | |-----------|------|---------|-------| | AuditedCharacter | typeclasses/audited_character.py | Yes | 110 | | Custom Commands | commands/command.py | Yes | 368 | | Audit Dashboard | web/audit/ (views, api, templates) | Yes | ~250 | | Object typeclass | typeclasses/objects.py | Stock (untouched) | 218 | | Room typeclass | typeclasses/rooms.py | Minimal | ~15 | | Exit typeclass | typeclasses/exits.py | Minimal | ~15 | | Account typeclass | typeclasses/accounts.py | Custom (157 lines) | 157 | | Channel typeclass | typeclasses/channels.py | Custom | ~160 | | Scripts | typeclasses/scripts.py | Custom | ~130 | | World builder | world/ | Custom | Unknown | ### Custom vs Stock Analysis - **objects.py** — Stock Evennia template with no modifications. Safe to delete and use defaults. - **audited_character.py** — Fully custom. Tracks movement, commands, session time, generates audit summaries. Clean code. - **commands/command.py** — 7 custom commands (examine, rooms, status, map, academy, smell, listen). All game-specific. Quality is good — uses Evennia patterns correctly. - **web/audit/** — Custom Django views and templates for an audit dashboard (character detail, command logs, movement logs, session logs). Functional but simple. - **accounts.py, channels.py, scripts.py** — Custom but follow Evennia patterns. Mainly enhanced with audit hooks. ### OSS Alternatives Evennia IS the OSS framework. The customizations are all game-specific content, which is exactly how Evennia is designed to be used. ### Recommendation: **KEEP as-is** This is a well-structured Evennia game. The customizations are appropriate and follow Evennia best practices. No formalization needed — it's already a proper project in a git repo. Minor improvements: - Remove the `{e})` empty file in root (appears to be a typo artifact) - The audit dashboard could use authentication guards **Effort:** 0 (already formalized) --- ## 4. Burn Scripts (`/root/burn_*.py`) **Count:** 39 scripts **Total lines:** 2,898 **Date range:** All from April 5, 2026 (one day) ### Current State These are one-off Gitea API query scripts. Examples: - `burn_sitrep.py` — fetch issue details from Gitea - `burn_comments.py` — fetch issue comments - `burn_fetch_issues.py` — list open issues - `burn_execute.py` — perform actions on issues - `burn_mode_query.py` — query specific issue data All follow the same pattern: 1. Load token from `/root/.gitea_token` 2. Define `api_get(path)` helper 3. Hit specific Gitea API endpoints 4. Print JSON results They share ~80% identical boilerplate. Most appear to be iterative debugging scripts (burn_discover.py, burn_discover2.py; burn_fetch_issues.py, burn_fetch_issues2.py). ### OSS Alternatives - **Gitea CLI (`tea`)** — official Gitea CLI tool, does everything these scripts do - **python-gitea** — Python SDK for Gitea API - **httpie / curl** — for one-off queries ### Recommendation: **DELETE or ARCHIVE** These are debugging artifacts, not production code. They: - Duplicate functionality already in the webhook receiver and hermes-agent tools - Contain hardcoded issue numbers and old API URLs (`143.198.27.163:3000` vs current `forge.alexanderwhitestone.com`) - Have numbered variants showing iterative debugging (not versioned) Action: 1. `mkdir /root/archive && mv /root/burn_*.py /root/archive/` 2. If any utility is still needed, extract it into the hermes-agent's `tools/gitea_client.py` which already exists 3. Install `tea` CLI for ad-hoc Gitea queries **Effort:** 30 minutes --- ## 5. Heartbeat Daemon **Files:** - `/root/wizards/allegro/home/skills/devops/hybrid-autonomous-production/templates/heartbeat_daemon.py` (321 lines) - `/root/wizards/allegro/household-snapshots/scripts/template_checkpoint_heartbeat.py` (155 lines) - Various per-wizard heartbeat scripts ### Current State Two distinct heartbeat patterns: **A) Production Heartbeat Daemon (321 lines)** Full autonomous operations script: - Health checks (Gitea, Nostr relay, Hermes services) - Dynamic repo discovery - Automated triage (comments on unlabeled issues) - PR merge automation - Logged to `/root/allegro/heartbeat_logs/` - Designed to run every 15 minutes via cron Quality: **Good for a prototype.** Well-structured phases, logging, error handling. But runs as root, uses urllib directly, has hardcoded org name. **B) Checkpoint Heartbeat Template (155 lines)** State backup script: - Syncs wizard home dirs to git repos - Auto-commits and pushes to Gitea - Template pattern (copy and customize per wizard) ### OSS Alternatives - **For health checks:** Uptime Kuma, Healthchecks.io, Monit - **For PR automation:** Renovate, Dependabot, Mergify (but these are SaaS/different scope) - **For backups:** restic, borgbackup, git-backup tools - **For scheduling:** systemd timers (already used), or cron ### Recommendation: **FORMALIZE into proper systemd timer + package** - Create a proper `timmy-heartbeat` Python package with: - `heartbeat.health` — infrastructure health checks - `heartbeat.triage` — issue triage automation - `heartbeat.checkpoint` — state backup - Install as a systemd timer (not cron) with proper unit files - Use the existing `tools/gitea_client.py` from hermes-agent instead of duplicating urllib code - Add alerting (webhook to Telegram/Nostr on failures) **Effort:** 4-6 hours --- ## 6. GOFAI System **Path:** `/root/wizards/allegro/gofai/` ### Current State: CRITICAL — SOURCE FILES MISSING The `gofai/` directory contains: - `tests/test_gofai.py` (790 lines, 20+ test cases) — **exists** - `tests/test_knowledge_graph.py` (14k chars) — **exists** - `__pycache__/*.cpython-312.pyc` — cached bytecode for 4 modules - **NO .py source files** for the actual modules The `.pyc` files reveal the following modules were deleted but cached: | Module | Classes/Functions | Purpose | |--------|------------------|---------| | `schema.py` | FleetSchema, Wizard, Task, TaskStatus, EntityType, Relationship, Principle, Entity, get_fleet_schema | Pydantic/dataclass models for fleet knowledge | | `rule_engine.py` | RuleEngine, Rule, RuleContext, ActionType, create_child_rule_engine | Forward-chaining rule engine with SOUL.md integration | | `knowledge_graph.py` | KnowledgeGraph, FleetKnowledgeBase, Node, Edge, JsonGraphStore, SQLiteGraphStore | Property graph with JSON and SQLite persistence | | `child_assistant.py` | ChildAssistant, Decision | Decision support for child wizards (can_i_do_this, who_is_my_family, etc.) | Git history shows: `feat(gofai): add SQLite persistence layer to KnowledgeGraph` — so this was an active development. ### Maturity Assessment (from .pyc + tests) - **Rule Engine:** Basic forward-chaining with keyword matching. Has predefined child safety and fleet coordination rules. ~15 rules. Functional but simple. - **Knowledge Graph:** Property graph with CRUD, path finding, lineage tracking, GraphViz export. JSON + SQLite backends. Reasonably mature. - **Schema:** Pydantic/dataclass models. Standard data modeling. - **Child Assistant:** Interactive decision helper. Novel concept for wizard hierarchy. - **Tests:** Comprehensive (790 lines). This was being actively developed and tested. ### OSS Alternatives - **Rule engines:** Durable Rules, PyKnow/Experta, business-rules - **Knowledge graphs:** NetworkX (simpler), Neo4j (overkill), RDFlib - **Schema:** Pydantic (already used) ### Recommendation: **RECOVER and FORMALIZE** 1. **URGENT:** Recover source from git history: `git show :gofai/schema.py` etc. 2. Package as `timmy-gofai` with proper `pyproject.toml` 3. The concept is novel enough to keep — fleet coordination via deterministic rules + knowledge graph is genuinely useful 4. Consider using NetworkX for graph backend instead of custom implementation 5. Push to its own Gitea repo **Effort:** 2-4 hours (recovery from git), 4-6 hours (formalization) --- ## 7. Hermes Agent (Claude Code / Hermes) **Path:** `/root/wizards/allegro/hermes-agent/` **Origin:** `https://github.com/NousResearch/hermes-agent.git` (MIT license) **Version:** 0.5.0 **Size:** ~26,000 lines of Python (top-level only), massive codebase ### Current State This is an upstream open-source project (NousResearch/hermes-agent) with local modifications. Key components: - `run_agent.py` — 8,548 lines (!) — main agent loop - `cli.py` — 7,691 lines — interactive CLI - `hermes_state.py` — 1,623 lines — state management - `gateway/` — HTTP API gateway for each wizard - `tools/` — 15+ tool modules (gitea_client, memory, image_generation, MCP, etc.) - `skills/` — 29 skill directories - `prose/` — document generation engine - Custom profiles per wizard ### OSS Duplication Analysis | Component | Duplicates | Alternative | |-----------|-----------|-------------| | `tools/gitea_client.py` | Custom Gitea API wrapper | python-gitea, PyGitea | | `tools/web_research_env.py` | Custom web search | Already uses exa-py, firecrawl | | `tools/memory_tool.py` | Custom memory/RAG | Honcho (already optional dep) | | `tools/code_execution_tool.py` | Custom code sandbox | E2B, Modal (already optional dep) | | `gateway/` | Custom HTTP API | FastAPI app (reasonable) | | `trajectory_compressor.py` | Custom context compression | LangChain summarizers, LlamaIndex | ### Recommendation: **KEEP — it IS the OSS project** Hermes-agent is itself an open-source project. The right approach is: - Keep upstream sync working (both `origin` and `gitea` remotes configured) - Don't duplicate the gitea_client into burn scripts or heartbeat daemons — use the one in tools/ - Monitor for upstream improvements to tools that are currently custom - The 8.5k-line run_agent.py is a concern for maintainability — but that's an upstream issue **Effort:** 0 (ongoing maintenance) --- ## 8. Fleet Deployment ### Current State Each wizard runs as a separate systemd service: - `hermes-allegro.service` — WorkingDir at allegro's hermes-agent - `hermes-adagio.service` — WorkingDir at adagio's hermes-agent - `hermes-ezra.service` — WorkingDir at ezra's (uses allegro's hermes-agent origin) - `hermes-bezalel.service` — WorkingDir at bezalel's Each has its own: - Copy of hermes-agent (or symlink/clone) - .venv (separate Python virtual environment) - home/ directory with SOUL.md, .env, memories, skills - EnvironmentFile pointing to per-wizard .env Docker containers (not managed by compose): - `gitea` — bare `docker run` - `strfry` — bare `docker run` ### Issues 1. **No docker-compose.yml** — containers were created with `docker run` and survive via restart policy 2. **Duplicate venvs** — each wizard has its own .venv (~500MB each = 2.5GB+) 3. **Inconsistent origins** — ezra's hermes-agent origin points to allegro's local copy, not git 4. **No fleet-wide deployment tool** — updates require manual per-wizard action 5. **All run as root** ### OSS Alternatives | Tool | Fit | Complexity | |------|-----|-----------| | docker-compose | Good — defines Gitea, strfry, and could define agents | Low | | k3s | Overkill for 5 agents on 1 VPS | High | | Podman pods | Similar to compose, rootless possible | Medium | | Ansible | Good for fleet management across VPSes | Medium | | systemd-nspawn | Lightweight containers | Medium | ### Recommendation: **ADD docker-compose for infrastructure, KEEP systemd for agents** 1. Create `/root/docker-compose.yml` for Gitea + strfry + Ollama(optional) 2. Keep wizard agents as systemd services (they need filesystem access, tool execution, etc.) 3. Create a fleet management script: `fleet.sh {start|stop|restart|status|update} [wizard]` 4. Share a single hermes-agent checkout with per-wizard config (not 5 copies) 5. Long term: consider running agents in containers too (requires volume mounts for home/) **Effort:** 4-6 hours (docker-compose + fleet script) --- ## 9. Nostr Key Management **File:** `/root/nostr-relay/keystore.json` ### Current State Plain JSON file containing nsec (private keys), npub (public keys), and hex equivalents for: - relay - allegro - ezra - alexander (with placeholder "ALEXANDER_CONTROLS_HIS_OWN" for secret) The keystore is: - World-readable (`-rw-r--r--`) - Contains private keys in cleartext - No encryption - No rotation mechanism - Used by bridge and relay scripts via direct JSON loading ### OSS Alternatives - **SOPS (Mozilla)** — encrypted secrets in version control - **age encryption** — simple file encryption - **Vault (HashiCorp)** — overkill for this scale - **systemd credentials** — built into systemd 250+ - **NIP-49 encrypted nsec** — Nostr-native key encryption - **Pass / gopass** — Unix password manager ### Recommendation: **FORMALIZE with minimal encryption** 1. `chmod 600 /root/nostr-relay/keystore.json` — **immediate** (5 seconds) 2. Move secrets to per-service EnvironmentFiles (already pattern used for .env) 3. Consider NIP-49 (password-encrypted nsec) for the keystore 4. Remove the relay private key from the systemd unit file (currently in plaintext in the `[Service]` section!) 5. Never commit keystore.json to git (check .gitignore) **Effort:** 1-2 hours --- ## 10. Ollama Setup and Model Management ### Current State - **Service:** `ollama.service` — standard systemd unit, running as `ollama` user - **Binary:** `/usr/local/bin/ollama` — standard install - **Models:** Only `qwen3:4b` (2.5GB) currently loaded - **Guard:** `/root/wizards/scripts/ollama_guard.py` — custom 55-line script that blocks models >5GB - **Port:** 11434 (default, localhost only) ### Assessment The Ollama setup is essentially stock. The only custom component is `ollama_guard.py`, which is a clever but fragile size guard that: - Checks local model size before pulling - Blocks downloads >5GB to protect the VPS - Designed to be symlinked ahead of real `ollama` in PATH However: it's not actually deployed as a PATH override (real `ollama` is at `/usr/local/bin/ollama`, guard is in `/root/wizards/scripts/`). ### OSS Alternatives - **Ollama itself** is the standard. No alternative needed. - **For model management:** LiteLLM proxy, OpenRouter (for offloading large models) - **For guards:** Ollama has `OLLAMA_MAX_MODEL_SIZE` env var (check if available in current version) ### Recommendation: **KEEP, minor improvements** 1. Actually deploy the guard if you want it (symlink or wrapper) 2. Or just set `OLLAMA_MAX_LOADED_MODELS=1` and use Ollama's native controls 3. Document which models are approved for local use vs. RunPod offload 4. Consider adding Ollama to docker-compose for consistency **Effort:** 30 minutes --- ## Priority Matrix | # | Component | Action | Priority | Effort | Impact | |---|-----------|--------|----------|--------|--------| | 1 | GOFAI source recovery | Recover from git | CRITICAL | 2h | Source code loss | | 2 | Nostr bridge source | Decompile/recover .pyc | CRITICAL | 4h | Service loss risk | | 3 | Keystore permissions | chmod 600 | CRITICAL | 5min | Security | | 4 | Burn scripts | Archive to /root/archive/ | HIGH | 30min | Cleanliness | | 5 | Docker-compose | Create for Gitea+strfry | HIGH | 2h | Reproducibility | | 6 | Fleet script | Create fleet.sh management | HIGH | 3h | Operations | | 7 | Webhook receiver | Move into hermes-agent repo | MEDIUM | 3h | Maintainability | | 8 | Heartbeat daemon | Package as timmy-heartbeat | MEDIUM | 5h | Reliability | | 9 | Ollama guard | Deploy or remove | LOW | 30min | Consistency | | 10 | Evennia | No action needed | LOW | 0h | Already good | --- ## Appendix: Files Examined ``` /etc/systemd/system/allegro-gitea-webhook.service /etc/systemd/system/nostr-bridge.service /etc/systemd/system/nostr-relay.service /etc/systemd/system/hermes-allegro.service /etc/systemd/system/hermes-adagio.service /etc/systemd/system/hermes-ezra.service /etc/systemd/system/hermes-bezalel.service /etc/systemd/system/ollama.service /root/wizards/allegro/gitea_webhook_receiver.py /root/nostr-relay/main.go /root/nostr-relay/keystore.json /root/nostr-relay/__pycache__/dm_bridge_mvp.cpython-312.pyc /root/wizards/allegro/gofai/ (all files) /root/wizards/allegro/hermes-agent/pyproject.toml /root/workspace/timmy-academy/ (typeclasses, commands, web) /root/burn_*.py (39 files) /root/wizards/allegro/home/skills/devops/.../heartbeat_daemon.py /root/wizards/allegro/household-snapshots/scripts/template_checkpoint_heartbeat.py /root/wizards/scripts/ollama_guard.py ```