Plan — Knowledge Manager Agent on oc.hyper.mediathe Claude Code plan for our agent

Context

Deploy autonomous Moderador de Redes (LAFH/GC-Red, per docs/research-lafh-knowledge-management.md) for a Seed Hypermedia community. Skill already authored under seed-knowledge-manager/.

Stack:

  • Local Seed daemon on oc.hyper.media (Go binary seed-daemon, default HTTP API on :55001, gRPC on :55002, P2P on :55000). seed-cli talks to it via -s http://127.0.0.1:55001. Means content is fetched from the network into a local store, then queried locally — no round-trip-per-call to remote hyper.media.

  • HKUDS/nanobot as agent runtime (Python pip, JSON config, MCP stdio, optional bwrap sandbox).

  • DeepSeek as LLM (OpenAI-compatible).

  • Custom stdio MCP wrapper around seed-cli for security envelope, rate limits, audit logging.

  • Telegram channel as secondary surface for ops chat with the operator (primary surface: Seed comments).

Headline goal: prove an agent can be governed by Seed documents, not local markdown.

Decisions (locked in)

  • Seed daemon: runs locally on oc.hyper.media (Docker image ghcr.io/seed-hypermedia/seed-daemon from backend/cmd/seed-daemon/Dockerfile, ports 55000/1/2).

  • Identity: agent has its own Ed25519 key (knowledge-manager, account KM_AID) stored in the same OS keyring the local daemon uses (libsecret on Linux). Owner grants WRITER on --path / of the site.

  • Write scope: agent may edit any document under the site. Rules doc still defines deny_write_paths (governance docs themselves, plus optional opt-out areas).

  • Telegram: secondary channel, operator-only (channels.telegram.allowFrom = [<OPS_TELEGRAM_ID>]). Used to chat about the agent and the server, not for community member queries.

  • All other prior decisions stand: HKUDS/nanobot, DeepSeek, mention-driven via Seed comments AND document mentions, 60s polling, en default locale, governance via Seed docs, bootstrap-on-first-run, full audit trail in km-logs/.

Why local daemon helps

  • Activity polling, citation lookups, document fetches all hit 127.0.0.1:55001 → low latency, no rate limit risk, works during hyper.media outages.

  • The daemon also pins / replicates the site content locally, giving us a real P2P peer rather than a thin client.

  • seed-cli keys live in the same OS keyring the daemon uses (per frontend/apps/cli/docs/KEYS.md), so signing operations don't require key duplication.

  • Cost: ~one Docker container, a few hundred MB on disk, plus the daemon's P2P traffic.

Architecture

oc.hyper.media (Ubuntu, ssh ubuntu@)
└── /home/km/                            # system user `km`, linger enabled
    ├── seed-daemon/                     # docker compose: data + config volumes
    │   ├── compose.yaml
    │   └── data/                        # persistent state (DB, IPFS blocks, etc.)
    ├── .nanobot/
    │   ├── config.json                  # providers, agents, tools.mcpServers, channels.telegram
    │   └── workspace/skill/             # rsynced from repo's seed-knowledge-manager/
    ├── km-agent/
    │   └── mcp/seed-cli-mcp/            # custom stdio MCP server wrapping seed-cli
    ├── km-state/                        # ephemeral runtime state
    │   ├── activity-cursor.json
    │   ├── inbox.jsonl
    │   ├── processed.jsonl
    │   ├── rate-counters.json
    │   └── rules.cache.json
    └── km-logs/                         # full audit log of every agent action
        ├── runs/2026-05-05T14-02Z__poll-mentions__<ulid>/
        │   ├── meta.json                # trigger, KM_AID, env hash, start, end, wall_ms
        │   ├── trace.jsonl              # ordered events with timestamps
        │   ├── llm.jsonl                # prompts, completions, reasoning, tokens
        │   ├── tools.jsonl              # MCP tool calls + latencies
        │   ├── seed-cli.jsonl           # argv + stdout + stderr + exit + ms
        │   ├── stdout.log               # raw nanobot stdout
        │   └── stderr.log               # raw nanobot stderr
        ├── current -> runs/<latest>     # symlink to most recent
        └── index.jsonl                  # one summary line per run

systemd --user units (linger keeps them alive after logout):
  seed-daemon.service       # `docker compose up` for the daemon
  nanobot-gateway.service   # `nanobot gateway`
  km-bootstrap.service      # one-shot, ensures governance docs exist
  km-poll.timer/.service    # `nanobot agent -m "/poll-mentions"` every 60s
  km-boletin.timer/.service # Mon 09:00 → /run-capability boletin --period last-7d
  km-health.timer/.service  # 1st of month 09:00 → /run-capability network-health
  km-gap.timer/.service     # Wed 10:00 → /run-capability gap-detection

External: api.deepseek.com, telegram bot API, the Seed P2P network (via daemon).

Governance via Seed docs (recap)

Same as previous version. Owner-editable Seed documents at fixed paths drive policy:

Rules YAML — now allow_write_paths defaults to [/] (whole site):

---
type: agent-rules
schema_version: 1
allow_write_paths: ["/"]
deny_write_paths:
  - /agents/knowledge-manager/charter
  - /agents/knowledge-manager/rules
  - /agents/knowledge-manager/runbook
  - /agents/knowledge-manager/allowlist
caps:
  max_documents_per_run: 1
  max_comments_per_run: 5
  max_comments_per_day: 30
  poll_interval_seconds: 60
mentions:
  trigger: "@knowledge-manager"
  invoker_source: "writer-capabilities"
moderation:
  blocked_authors: []
draft_only: false
language: en
---

Wrapper enforces: deny_paths > allow_paths > caps. Governance docs are always denied so the agent can't rewrite its own constraints.

Mentions, reactions, audit logging

Same as previous plan version (mention syntax @[Name](hm://aid) in comments and document blocks; replies always via comment, block-anchored where possible; full per-run logs under km-logs/runs/<id>/ with meta, trace, llm (incl. DeepSeek reasoning_content), tools, seed-cli, raw stdout/stderr; secrets redacted; km-log Bash helper for SSH browsing; logrotate 30d / 5GB).

Phased execution

Each phase ships a verifiable artifact. Run them one at a time. After each phase: smoke-test, commit code, then move on.

Phase 0 — Repo scaffolding (local, ~30min)

Outcome: directory seed-knowledge-manager/agent/ with empty placeholders + the operator README skeleton. No deploy yet.

Tasks:

Verify: tree seed-knowledge-manager/agent/ shows the layout; pnpm typecheck still passes (no real code yet).

Phase 1 — Server bootstrap (oc.hyper.media)

Outcome: server has all OS dependencies, the km user, and Docker-based Seed daemon running and synced.

Tasks:

  • SSH ubuntu@oc.hyper.media. Install: python3.12 python3.12-venv pipx libsecret-1-0 libsecret-tools dbus-user-session bubblewrap nodejs npm jq curl docker.io docker-compose-plugin logrotate rsync.

  • Create user km, loginctl enable-linger km. Add km to docker group.

  • As km: mkdir -p ~/seed-daemon/data && cd ~/seed-daemon. Add compose.yaml:

    services:
      seed-daemon:
        image: ghcr.io/seed-hypermedia/seed-daemon:latest
        restart: unless-stopped
        ports:
          - "127.0.0.1:55001:55001"   # HTTP
          - "127.0.0.1:55002:55002"   # gRPC
          - "55000:55000/tcp"         # P2P
          - "55000:55000/udp"
        volumes:
          - ./data:/data
        environment:
          - SEED_DAEMON_FLAGS=--data.dir=/data
  • docker compose up -d. Tail logs docker compose logs -f until daemon reports healthy on 127.0.0.1:55001.

  • Create user-systemd unit seed-daemon.service that runs docker compose up so it survives reboots.

Verify (must pass before phase 2):

Phase 2 — Agent identity + capability grant

Outcome: agent's Ed25519 key in keyring, owner has granted WRITER on the site, contact published.

Tasks (as km):

  • npx -y @seed-hypermedia/cli -s http://127.0.0.1:55001 key generate --name knowledge-manager --show-mnemonic. Capture mnemonic OFFLINE. Capture KM_AID.

  • On owner's machine (with owner key in their keyring):

    seed-cli capability create --delegate <KM_AID> --role WRITER --path / --label "knowledge-manager" --key <owner>
    seed-cli contact create --subject <KM_AID> --name "Knowledge Manager" --key <owner>
  • From km: seed-cli account capabilities <SITE_AID> confirms KM_AID is listed as WRITER on /.

Verify:

  • From km: create + delete a throwaway document under any path → succeeds.

  • Owner sees "Knowledge Manager" appear with avatar in the site UI.

Phase 3 — seed-cli MCP wrapper + tests (local repo)

Outcome: seed-knowledge-manager/agent/mcp/seed-cli-mcp/ is a working Node MCP server with unit tests, runnable on macOS without server access.

Tasks:

  • Set up Node package: TS + Vitest + @modelcontextprotocol/sdk. package.json name @km/seed-cli-mcp, bin: ./dist/index.js. Workspace member of pnpm root.

  • Implement modules:

    • src/governance.ts — fetches the four governance docs via seed_get_document, parses YAML frontmatter, caches 60s, returns Rules.

    • src/limits.ts — path allow/deny matcher (globstar), per-run / per-day counter persistence in KM_STATE_DIR/rate-counters.json.

    • src/seedcli.ts — typed wrapper: runSeedCli(args), captures argv/exit/stdout/stderr/ms, redacts env values, returns structured result. Force-injects --key knowledge-manager and -s ${SEED_SERVER} for write commands.

    • src/audit.ts — current-run dir resolution (created at process start), append-only writers for trace, llm, tools, seed-cli JSONL streams, secret redaction.

    • src/mentions.ts — parses Seed activity / citations responses, classifies into mention { kind: comment|doc-block, doc, blockId?, commentId?, author, text }.

    • src/state.ts — cursor + inbox + processed-set helpers.

    • src/index.ts — registers MCP tools (seed_search, seed_query_space, seed_get_document, seed_list_comments, seed_get_activity, seed_get_citations, seed_list_capabilities, seed_get_governance, seed_create_document, seed_update_document, seed_create_comment, seed_reply_comment, inbox_pop, inbox_mark_done, cursor_get, cursor_set).

  • Hardcoded denylist: never accept key *, capability *, account create, document delete <governance-path>.

  • Tests (*.test.ts, Vitest): governance YAML parsing, deny-path beats allow-path, rate caps, denylist refusal, draft-only mode forces comment-only, mention parsing for both surfaces, redaction of env values.

  • Build: pnpm --filter @km/seed-cli-mcp build produces dist/index.js.

Verify:

  • pnpm --filter @km/seed-cli-mcp test green.

  • Smoke run locally: MCP_DEBUG=1 node dist/index.js then send tools/list JSON-RPC over stdin → returns the expected toolset.

Phase 4 — nanobot install + governance bootstrap

Outcome: nanobot gateway running on oc.hyper.media; agent has created the four governance docs in the site on first run.

Tasks (as km):

  • pipx install nanobot-ai. nanobot --version.

  • nanobot onboard (creates ~/.nanobot/{config.json,workspace/}).

  • rsync seed-knowledge-manager/~/.nanobot/workspace/skill/ (read-only).

  • Deploy ~/.nanobot/secrets.env (mode 600):

    DEEPSEEK_API_KEY=...
    SEED_SERVER=http://127.0.0.1:55001
    SEED_SITE=hm://...
  • Write ~/.nanobot/config.json:

    {
      "providers": { "deepseek": { "apiKey": "${DEEPSEEK_API_KEY}" } },
      "agents": {
        "defaults": {
          "provider": "deepseek",
          "model": "deepseek-chat",
          "systemPrompt": "You are the Knowledge Manager for ${SEED_SITE}. Always call seed_get_governance first; obey it strictly. Methodology: workspace/skill/SKILL.md and templates under workspace/skill/templates/. Default language: en (override only if charter sets language).",
          "temperature": 0.2,
          "timezone": "UTC",
          "idleCompactAfterMinutes": 15
        }
      },
      "tools": {
        "restrictToWorkspace": true,
        "exec": { "enable": true, "sandbox": "bwrap", "pathAppend": "/usr/bin:/usr/local/bin" },
        "web": { "enable": true, "search": { "provider": "duckduckgo", "maxResults": 5 } },
        "mcpServers": {
          "seed": {
            "command": "node",
            "args": ["/home/km/km-agent/mcp/seed-cli-mcp/dist/index.js"],
            "toolTimeout": 60,
            "env": {
              "SEED_SERVER": "${SEED_SERVER}",
              "SEED_SITE": "${SEED_SITE}",
              "KM_KEY_NAME": "knowledge-manager",
              "KM_STATE_DIR": "/home/km/km-state",
              "KM_LOGS_DIR": "/home/km/km-logs"
            }
          }
        }
      },
      "channels": {}
    }
  • Deploy nanobot-gateway.service and km-bootstrap.service (oneshot, runs nanobot agent -m "/bootstrap-governance").

  • systemctl --user daemon-reload && systemctl --user enable --now nanobot-gateway.

  • systemctl --user start km-bootstrap.service.

Verify:

  • The four governance docs exist in the site at /agents/knowledge-manager/{charter,rules,runbook,allowlist}.

  • A run dir exists under km-logs/runs/...__bootstrap__<ulid>/ with meta.json, trace.jsonl, seed-cli.jsonl showing the four document create calls.

Phase 5 — Mention polling + reaction

Outcome: writer mentions in comments and documents trigger an agent reply within 2 minutes.

Tasks:

  • Deploy km-poll.timer/.service (60s cadence).

  • System prompt addendum (committed in repo): the /poll-mentions flow:

    1. seed_get_governance → rules.

    2. seed_list_capabilities ${SEED_SITE} → writer set.

    3. cursor_get → token.

    4. seed_get_activity --token <cursor> → events. Filter mentions to KM_AID.

    5. For each mention, if author ∈ writers and not in processed.jsonlinbox_pop queue.

    6. For each queued mention: classify against capabilities 1–7 in SKILL.md; respond via seed_reply_comment (comment-mention) or seed_create_comment <doc>#<blockId> (doc-mention).

    7. cursor_set newer.

  • Deploy km-log Bash helper to /home/km/.local/bin/km-log.

  • Deploy logrotate user config.

Verify:

  • From a writer account, comment @[Knowledge Manager](hm://<KM_AID>) what does this community know about X? → reply within 60s.

  • From the same account, edit a doc and add inline mention → block-anchored comment within 60s.

  • From a non-writer account, mention → ignored, processed.jsonl records reason: not-allowed.

  • km-log tail shows full event sequence with timestamps and DeepSeek reasoning_content.

Phase 6 — Scheduled LAFH cadences

Outcome: weekly bulletin, weekly gap report, monthly health report run on schedule.

Tasks:

  • Deploy km-boletin.{timer,service} (Mon 09:00 UTC, message /run-capability boletin --period last-7d).

  • Deploy km-gap.{timer,service} (Wed 10:00 UTC, message /run-capability gap-detection --period last-7d).

  • Deploy km-health.{timer,service} (1st of month 09:00 UTC, message /run-capability network-health --period last-30d).

  • Add system prompt support for /run-capability <name> --period <range> (maps capability name → SKILL section).

Verify:

  • systemctl --user start km-boletin.service produces a doc at /agents/knowledge-manager/state/boletin/<YYYY-WW>.

  • systemctl --user start km-health.service rewrites /agents/knowledge-manager/state/network-health.

  • Each run has its own dir under km-logs/runs/.

Phase 7 — Telegram secondary channel

Outcome: operator can chat with the agent over Telegram about agent status, force a poll, query logs, toggle kill-switch.

Tasks:

  • Create Telegram bot via @BotFather → token. Get operator's Telegram user ID.

  • Add to secrets.env: TELEGRAM_TOKEN=..., OPS_TELEGRAM_ID=<numeric>.

  • Edit ~/.nanobot/config.json:

    "channels": {
      "sendProgress": true,
      "telegram": {
        "enabled": true,
        "token": "${TELEGRAM_TOKEN}",
        "allowFrom": ["${OPS_TELEGRAM_ID}"]
      }
    }
    
  • systemctl --user restart nanobot-gateway.

  • Extend the agent's system prompt with operator-only verbs (/status, /poll-now, /show-rules, /last-runs, /draft-only on|off). These never write to Seed; they only read state and emit human-formatted Telegram replies.

  • Document in operator README that allowFrom is locked to ops only — community members must use Seed comments, not Telegram.

Verify:

  • /start on Telegram returns a welcome.

  • Send /status from operator's account → bot responds with daemon health, last run time, comments-today count.

  • Send /status from a different Telegram account → silently ignored (allowFrom enforced).

  • Send /draft-only on → check next mention reply is a draft comment.

Phase 8 — Audit-log polish + verification suite

Outcome: full smoke test of every flow; logs make sense from SSH.

Tasks:

  • Run all 19 verification steps end-to-end (combined from earlier plan plus Telegram).

  • Capture screenshots / log excerpts in seed-knowledge-manager/agent/README.md.

Phase 9 — Future / deferred (not in v1)

  • Broader "members can invoke" mode (mentions.invoker_source: allowlist-doc).

  • Seed-native skill files (currently on disk).

  • Heartbeat-style autonomous to-dos in Seed (no checkbox primitive yet).

  • Multi-site moderation.

  • Web UI for browsing logs.

Files to create / modify (in this repo, across phases)

  • seed-knowledge-manager/ — already exists; no changes.

  • seed-knowledge-manager/agent/config/config.json — nanobot config template (Phase 4).

  • seed-knowledge-manager/agent/seed-daemon/compose.yaml — local daemon compose file (Phase 1).

  • seed-knowledge-manager/agent/mcp/seed-cli-mcp/ — Node MCP server + tests (Phase 3).

  • seed-knowledge-manager/agent/systemd/{seed-daemon,nanobot-gateway,km-bootstrap,km-poll{.service,.timer},km-boletin{.service,.timer},km-health{.service,.timer},km-gap{.service,.timer}} — all systemd units.

  • seed-knowledge-manager/agent/scripts/install.sh — idempotent provisioning script.

  • seed-knowledge-manager/agent/scripts/km-log — Bash log helper.

  • seed-knowledge-manager/agent/templates/{agent-charter,agent-rules,agent-runbook,agent-allowlist}.md — bootstrap seeds.

  • seed-knowledge-manager/agent/logrotate/km-logs.conf — user logrotate (30d / 5GB).

  • seed-knowledge-manager/agent/README.md — operator runbook (key gen, capability grant, env vars, deploy, kill-switch, log paths, Telegram setup, daemon volume backup).

  • Reuses (no changes):

Verification (full v1 suite)

  • 1–2. Daemon up locally; seed-cli account list works against http://127.0.0.1:55001.

  • 3. Agent key generated; capability granted; contact published; throwaway doc round-trip.

  • 4. MCP wrapper unit tests green; manual tools/list smoke run works.

  • 5. Bootstrap creates four governance docs; run dir under km-logs/.

  • 6. Comment-mention from writer → reply ≤60s, block-anchored where applicable.

  • 7. Document-mention from writer → block-anchored comment reply ≤60s.

  • 8. Mention from non-writer → ignored, recorded.

  • 9. draft_only: true → reply is draft comment, no document writes.

  • 10. deny_write_paths add → write attempt declines with explanatory comment.

  • 11. Weekly bulletin manual trigger creates state/boletin/<YYYY-W

  • 12. Health report manual trigger rewrites state/network-health.

  • 13. Capability revoke → next-tick writes start failing with logged errors.

  • 14. Wrapper unit tests green in CI.

  • 15. km-log tail works; meta.json.wall_ms, per-tool latency_ms, DeepSeek reasoning_content all present.

  • 16. Grep km-logs/ for DEEPSEEK_API_KEY value → zero matches.

  • 17. logrotate -d dry-run on user config exits clean.

  • 18. Telegram /status from ops → returns; from non-ops → ignored.

  • 19. /draft-only on from ops → next mention reply is a draft.

Sources:

Do you like what you are reading?. Subscribe to receive updates.

Unsubscribe anytime