Skip to main content

Poindexter is the engine — agents are the employees

The mental model: Poindexter is a content-pipeline engine. Anything that talks to it — Claude Code, OpenClaw, a custom CLI, a Telegram bot, a future agent we haven’t built yet — is an employee. Employees come and go; the engine is what you own and tend.
This doc captures the architectural framing and the boundary between the engine and its consumers. Written 2026-05-08 to make explicit a design choice that had been implicit until then.

The boundary

The dotted line is the product surface: REST endpoints, MCP tools, OAuth /token. Anything below the line is an employee — a software persona that consumes Poindexter to do work.

Why this framing matters

Before today, OpenClaw and Poindexter were de-facto coupled. They shared the host, ran similar-sounding agents, even shared a Discord bot identity (until we untangled that 2026-05-07). It was unclear whether OpenClaw was “part of” Poindexter or a sibling system. After today’s decision, the answer is: OpenClaw is an employee that uses Poindexter. Specifically, it’s Matt’s current operator overlay — the way he chats with the system, approves posts, and runs ops tasks from his desktop or phone. It is private; it is not part of the open-source product; it can be replaced with a different operator overlay tomorrow without changing Poindexter at all. Same goes for Claude Code, which is Matt’s current development surface. When he wants to write code, he opens Claude Code and it talks to Poindexter through MCP and the local stdio bridge. If Anthropic shipped a different CLI tomorrow, or Matt switched to Aider, or a future agent like “Devin” matured enough — all of those would just be different employees consuming the same Poindexter contract. The engine doesn’t care who its employees are.

What “owns” means

Matt owns:
  • The architecture, contracts, and glue between OSS components
  • The PostgreSQL schema (the canonical state)
  • The plugin protocols (Taps, Probes, Jobs, Stages, Packs, LLMProviders)
  • The OAuth/auth model that gates access
  • The brand and the published content quality bar
Matt does not own:
  • LiteLLM (used as-is, replaceable)
  • LangGraph (used as-is, replaceable)
  • Langfuse (used as-is, replaceable)
  • Pyroscope, Grafana, Prometheus (used as-is, replaceable)
  • The host OS, Docker, Postgres itself
  • Any specific employee (Claude Code, OpenClaw, etc.)
Goal: less moving parts on Matt’s end. If a piece can be swapped for a comparable OSS or commercial offering without rewriting his code, swap-in shouldn’t require Matt’s involvement.

The contract employees consume

Any Poindexter employee should be able to operate using only:
  1. REST API at http://localhost:8002 (or its publicly-routable equivalent via Tailscale Funnel) — full CRUD on posts, settings, audit log, decisions.
  2. MCP server (HTTP at :8004/mcp or stdio via mcp-server/server.py) — the same surface, exposed as MCP tools for LLM-driven employees.
  3. OAuth 2.1 token endpoint at /token — mint short-lived JWTs scoped to what the employee needs (operator.read, operator.write, operator.approvals, etc.).
  4. PostgreSQL (advanced, optional) — direct DB access for employees that need it (the brain daemon does this; most employees should not).
Employees should NOT:
  • Hardcode OAuth secrets (they go through /token)
  • Bypass the REST/MCP surface to reach internal services
  • Modify Poindexter source code to add features (use the plugin framework instead)

How an employee gets onboarded

Same pattern, regardless of whether it’s a CLI, an agent, or a one-off script:
  1. Matt runs poindexter auth migrate-<consumer> to mint OAuth credentials. (Examples already shipping: migrate-cli, migrate-mcp, migrate-brain, migrate-scripts, migrate-mcp-gladlabs, migrate-openclaw, mint-grafana-token.)
  2. The employee stores those credentials wherever it stores secrets.
  3. The employee exchanges its client_id+secret for a JWT at /token, scoped appropriately.
  4. The employee uses that JWT to call REST or MCP endpoints.
  5. When Matt wants to retire an employee, he revokes its oauth_clients row. Done.
This is identical to how a human employee would be onboarded and offboarded: badge issued, badge revoked. No code changes required on either side.

Concrete examples (today’s lineup)

EmployeeWhat it doesTenureAuth
Claude Code (terminal)Matt’s primary dev interface; writes code, runs migrations, debugsLong-term while Anthropic ships itOAuth via stdio MCP
Claude Code (voice agent in Docker)Same, over LiveKit voice instead of typingExperimentalOAuth via bind-mounted host credentials
OpenClaw (operator gateway)Matt’s operator chat — Discord, approvals, ad-hoc commandsLong-term while Matt finds it usefulOAuth via migrate-openclaw
Brain daemonHealth probes, self-healing, knowledge graph maintenancePermanent (it’s part of the engine itself, but talks to the worker over REST like any employee)OAuth via migrate-brain
Discord ClaudeBotDirect Discord access to Claude Code sessionsExperimentalTelegram-style pairing via /discord:access
Telegram botCritical alerts + voice-note bridgePermanentBot token in bootstrap.toml
Scheduled Claude Sessions (alert-triage, dependency-review, etc.)Nightly autonomous Claude runs that touch the codebaseAs-neededInherits Matt’s terminal Claude credentials
Every entry above can be retired independently of every other. None of them is “Poindexter” — they all use Poindexter.

Anti-patterns to avoid

  • Don’t hardcode an employee’s name into Poindexter core. If content_router_service.py ever has if user == "openclaw":, that’s a bug.
  • Don’t let an employee write to app_settings without an OAuth-scoped token that covers it. This is what the is_secret=true decryption path enforces today.
  • Don’t ship employee-specific code in the public Poindexter repo. Glad Labs operator overlay (OpenClaw, the storefront, premium prompts) lives in glad-labs-stack and is stripped from the public mirror by scripts/sync-to-github.sh.
  • Don’t let an employee become load-bearing for non-employee work. If Poindexter’s content pipeline can’t run without OpenClaw alive, the boundary has been violated.

What this implies for development

  • New feature in Poindexter? Lives in the engine. Bound by the OAuth scopes; exposed via REST + MCP; configurable via app_settings.
  • New agent or operator UI? Lives outside the engine. Calls the engine through the contract. Has its own oauth_clients row.
  • Something between? Probably a plugin (Tap / Probe / Job / Stage / Pack / LLMProvider) — Poindexter’s extension framework lets external packages register without modifying core.
If you’re not sure which category a new piece falls into, the test is: if Matt fired this employee tomorrow, would the engine keep working? If yes → employee. If no → engine.
Last updated: 2026-05-08 Decision owner: Matt (Glad Labs LLC)