← All posts

Four systems and two cron jobs, or one install

A small team wants shared agents, shared memory, BYOK and an autonomy dial. Here is what assembling it costs, and what one install still lacks.

Vocion Teamvocion-v2.47.9

A self-hosted AI agent setup for a small engineering team needs five things: a runtime that runs the same agents for everyone, a shared knowledge base, a shared memory a human can vet before it becomes a rule, a dial for how autonomous each agent is allowed to be, and a place to talk to the agents where the team already works. The advice that circulates today assembles this from parts — an agent runtime, a chat app, a Markdown vault, a memory service, and the bespoke scripts a team writes to keep all four in sync. Vocion is one install that covers four of those five. It does not cover the fifth: Vocion has no Slack chat interface today. Slack is a read-only source connector — it ingests channel history so agents can search it. There is no bot, no mention handler, no slash command. If a Slack surface is the actual requirement, take Hermes.

This piece walks the honest trade row by row. The full matrix, with every source cited, is on the comparison page.

The setup everyone recommends

One route to shared agents for a small team assembles four systems. Hermes Agent (Nous Research) is the runtime: each named profile gets its own config, memory, sessions, and gateway PID, and a single gateway process routes messages to the right profile (Architecture, accessed 2026-09-09); secrets, including provider API keys, are read from that profile's own .env (Configuration, accessed 2026-09-09). That gateway reaches 20+ messaging platforms, Slack among them, from one background process (Messaging, accessed 2026-09-09). Hermes documents seven terminal/sandbox backends — local, ssh, docker, singularity, modal, daytona, and vercel_sandbox — in a security comparison table naming each backend's isolation level and best-fit use case (Security, accessed 2026-09-09). The installation docs don't state a minimum server size, so we won't repeat the "runs on a small VPS" line elsewhere without a source for it. Slack is the interface, wired through that same messaging gateway. Obsidian stores notes as local, plain-text Markdown with no proprietary lock-in (Obsidian, accessed 2026-09-09) — the property that makes a shared vault plausible, though we found no Obsidian doc that specifically advertises agent read/write access to it. A self-hosted alternative aimed at a different job is Open Notebook: MIT-licensed, with a REST API served at /docs on its own local port (Open Notebook README, accessed 2026-09-09) — it is a notebook-and-podcast research tool, not a drop-in Obsidian replacement. Mem0/OpenMemory, Zep, and Letta are the usual picks for shared memory across agents; the next section sources each one. Of the three, Letta's own docs go furthest into the rest of this list too: they describe Slack, Telegram, and Discord channels (Channels, accessed 2026-09-09), scheduled recurring prompts (Schedules, accessed 2026-09-09), and memory shared across agents (Shared memory, accessed 2026-09-09). By that set of criteria — chat channels, schedules, and shared memory documented under one product — Letta reads, in our judgment, as the closest single-vendor answer to this comparison, not just a memory layer; that ranking is our opinion, not a claim Letta's docs make about themselves.

None of this is a bad stack. It is a plausible stack, assembled from real, documented products. The cost shows up in the next section.

What the assembled stack costs to keep running

Four systems means four places state can drift, and nothing in that stack owns cleanup or accountability by default. Each piece also wants its own provider key: Hermes reads secrets from each profile's own .env (Configuration, accessed 2026-09-09), and Mem0's default setup needs its own OPENAI_API_KEY (Open source overview, accessed 2026-09-09) — separate from whatever key drives Obsidian's LLM plugins or Open Notebook's model providers. None of the four systems' own docs describe a shared place where "who approved this" gets recorded across all of them. That absence — not a specific script count — is the argument for keeping cleanup and spend reporting in the same install as the agents.

Vocion's answer to "a scheduled job" is an automation: a five-field UTC cron schedule or an event match — exactly one, not both — dispatched through dispatchDo to run one of workflow, checkMission, or job (docs/entities/automation.md). The cleanup half of the problem is also smaller than it looks, because the learning-candidate queue already proposes rules for a human instead of needing a script to go mine them for you — more on that below.

Shared context is a versioning problem, not a file-sync problem

An Obsidian vault answers "where do the notes live," not "which version of a note produced this output." Vocion's workspace is Git-backed YAML and Markdown, and every workspace:apply inserts a workspace_version row (packages/core/src/libs/workspace/applier.ts, the db.insert(workspaceVersionSchema) call around line 250). Every tool_call is stamped with that same workspace_sha (packages/core/src/models/Schema.ts, the tool_call table's workspace_sha column). Two agents reading "the same" file today is a file-sync guarantee. An output tracing back to the exact prompt version that produced it is a different guarantee, and it is the one a team debugging a bad agent reply actually needs.

Shared memory that a person signs off on

The assembled stack reaches for Mem0, Zep, or Letta here. Mem0's open-source path documents a memory layer — it needs pgvector (server mode) or a local Qdrant instance (library mode), a history database, an LLM key, and an embeddings provider, and that page does not describe agents, approvals, or scheduling (Open source overview, accessed 2026-09-09). Zep's concepts page describes a temporal Context Graph and an "agent tool" for retrieving from it, with no approval workflow on that page (Concepts, accessed 2026-09-09). Letta's shared-memory docs describe agents committing and pulling changes to a shared memory repository, also with no approval step described (Shared memory, accessed 2026-09-09). None of those three pages describes a step where a human decides whether a proposed memory becomes a standing rule — that is what those specific pages say, not a claim about every page either product publishes.

Vocion's learning steps do. New rules are deduplicated by trigram Jaccard similarity at a 0.72 threshold (packages/core/src/services/LearningsService.ts, DEDUP_THRESHOLD = 0.72), and classified feedback lands in a learning_candidate queue and becomes a real rule only when a person approves it, with the rejection reason kept (packages/core/src/services/LearningCandidateService.ts, packages/core/src/models/Schema.ts's learning_candidate table). That is the difference between a memory store and a queue of rules a human adopts: a memory store remembers what it was told; this is a review step that decides what gets remembered at all. See Teams: the org chart is the interface for how those same agents are organized into a one-level parent: hierarchy (docs/entities/team.md, docs/entities/agent.md). Hermes' own architecture docs describe each profile as a standalone instance with its own config, memory, and sessions (Architecture, accessed 2026-09-09); we found no parent/child relationship documented between profiles there, which is why we call that model flat by comparison.

The autonomy dial

Hermes has a documented command-approval and authorization model: it gates shell/terminal commands against a curated list of dangerous patterns and, depending on the configured mode, either escalates to a human or fails closed on timeout (Security, accessed 2026-09-09). The choice a reader is actually making is not "no HITL versus HITL" — it is a shell-command gate versus a review queue over authored actions.

Vocion's version is mission autonomy levels 1 through 5 (packages/core/src/services/missions/autonomy.ts, AutonomyLevel, AUTONOMY_LABELS), a review queue with list, assign, snooze, and decide (packages/core/src/services/ReviewService.ts), and trust rules that let a proposal auto-execute only when its confidence clears an explicit per-action threshold, refusing with no-rule, disabled, below-threshold, or no-confidence when it doesn't (packages/core/src/services/TrustService.ts). Per-agent harness.interrupts names the skills or tools that pause for approval regardless of level (docs/entities/agent.md). One agent can be set to draft-only; another can act inside its rules; the difference is a level, not a rewrite. The review queue itself is documented in The review queue, over HTTP.

BYOK and where the key actually lives

Hermes reads each profile's secrets, including provider API keys, from that profile's own .env (Configuration, accessed 2026-09-09). Vocion resolves per-organization vendor keys for openai, anthropic, vertex, azure-openai, aws, and a custom endpoint (packages/core/src/libs/platforms/registry.ts). The org's stored key is tried first, and a null falls through to the server's own environment key (packages/core/src/libs/llm/orgKey.ts, resolveOrgProviderKey). Stored credentials are encrypted with AES-256-GCM under a per-tenant data encryption key, wrapped by AWS KMS in production or a local master key in dev (packages/core/src/libs/crypto/credentialVault.ts). One distinction worth stating plainly, because two harness fields sound alike and are not: runsOnin-process, agentcore-container, or aws-managed-harness — is which machinery runs the turn, while harness.modelProvideranthropic, openai, or bedrock — is whose model answers (packages/core/src/libs/workspace/schemas.ts, the modelProvider field and the comment above it; docs/entities/agent.md). They are separate axes, and one default links them: an agent that sets modelProvider: bedrock and leaves runsOn unset gets agentcore-container, since choosing AWS as the model vendor is almost always choosing AWS as the place to run it; writing runsOn: in-process next to it opts back out (docs/entities/agent.md).

Spend limits that refuse the run

The assembled stack's answer to credit accountability, if it has one, is typically a bespoke report written after the fact — none of the four systems above document a built-in spend cap. Vocion's agent_budget rows carry soft caps that warn and hard caps that refuse, in both tokens and cents, per agent per daily or monthly period (packages/core/src/models/Schema.ts, the agent_budget table). preflightCheck refuses a run on hard_tokens_exceeded or hard_cents_exceeded before it starts; chargeUsage accrues; setLimits configures the caps (packages/core/src/services/BudgetService.ts).

Say the difference precisely, because it cuts both ways: the reader asked "who on the team is burning credits" — a per-person question. Vocion's budget is keyed on agent_slug, not on a person. That is a stronger control in one sense — it refuses the run instead of reporting on it afterward — but it is aimed at a different unit than the one the reader asked about. The agent_budget table has no user_id or equivalent column (packages/core/src/models/Schema.ts), so there is no per-user credit report today.

The part we do not have: Slack

Can you talk to a Vocion agent in Slack. No. The Slack connector (packages/core/src/libs/sources/slack.ts, 72 lines) calls Slack's conversations.history endpoint on a schedule and yields the messages as ingestible documents. That is the entire file. There is no bot user, no app_mention handler, no slash command, and no call to chat.postMessage — a repo-wide search for either turns up nothing but that one history call. @slack/* is not a dependency in packages/core/package.json. The connector reads channel history into an agent's context; it does not let an agent post back.

Hermes' messaging gateway, by contrast, reaches Slack, Telegram, Discord, and 20+ other platforms from one gateway process (Messaging, accessed 2026-09-09). If that is the requirement, the honest recommendation is Hermes.

What running it actually takes

Hermes' installation docs don't state a minimum server size, so we won't claim a specific hosting footprint for it here. Vocion wants Postgres and a compose stack — the repo's docker-compose.yml brings up a postgres service (pgvector-enabled) plus the Langfuse and Temporal platform services it includes, and the Next.js app itself runs alongside that with npm run dev:next (docker-compose.yml). Clone the repo (https://github.com/vocion/vocion-core); it is not published to npm — npm view @vocion/core against the public registry returns a 404 as of 2026-09-09, and the package's own package.json marks it "private": true (packages/core/package.json) — so there is no npm install @vocion/core. Bring up the compose stack, run the migrations, and apply a workspace. That is a heavier first hour than a single small server. What it buys, in the same install, is the versioning, the review queue, the per-agent budgets, and the learning-candidate queue described above.

The approval gate itself is a workflow step, not a special case: an approve step "pauses in the review queue for a human decision," with an optional reviews field naming the earlier step whose output is being judged (docs/entities/workflow.md). The support-reply demo (vocion-demos/demos/support-reply/) shows the shape — its context/support-demo/workflows/support-triage/workflow.yaml has a review step of type: approve sitting between draft and send. One caveat: the demo still ships its two skills under an operations/ directory, a layer the workspace loader has rejected since v2.20.0 (packages/core/src/libs/workspace/loader.ts), so this demo does not currently load on v2.47.9 — read the workflow file for the gate's shape rather than trying to run the demo as-is.

Which one to pick

If the team's non-negotiable is a chat surface in Slack today, pick Hermes — its docs don't advertise the versioning, budget enforcement, or learning-candidate queue described above, so budget time to build equivalents if you need them. If the non-negotiable is a single system that already tracks which workspace version produced an output, routes low-confidence actions to a human, and refuses a run before it overspends — and the team can live without a Slack surface for now — one install covers four of the five things being assembled. Either way, know the row you are trading away before you commit to a stack.

Where to find it

The full trade matrix, every claim sourced, is on the comparison page. Clone vocion-core, bring up the compose stack, and apply a workspace to see the review queue and the approval gate for yourself.