The open-source AI content factory that runs on your PC — discovers topics, researches, writes, reviews with multi-model QA, and publishes. Local-first via Ollama, $0 API costs.
5
stars
10,376
commits
Python
primary language
Sep 8, 2026
updated
One engine that discovers topics, researches them, writes long-form posts, tears them apart in review, and publishes the survivors — on your GPU, with zero API costs.
Quick start · What it does · The QA gauntlet · Architecture · Docs · Pro
This is not a demo repo. Poindexter is the production system behind gladlabs.io — 200+ live posts and counting, every one generated, reviewed, and published by this pipeline on a single PC. Here is the operator's actual view of it running:
The operator console (ships with the repo, served by the worker at /console/), recorded live: a post mid-generation, the approval inbox with per-task QA verdicts, pipeline traces, and the system map.
The Pipeline dashboard (ships with the repo): drafts arrive scored, weak ones are auto-rejected, survivors wait for your one-click approval.
From clean machine to a running pipeline in about 30 minutes — most of it one-time model downloads.
Prereqs: Docker · Ollama · Python 3.13+ · NVIDIA GPU with 8 GB+ VRAM (CPU works, just slowly) · Node.js 22+ only if you want the optional Next.js frontend. Windows: run from Git Bash or WSL2.
# 1. Clone
git clone https://github.com/Glad-Labs/poindexter.git && cd poindexter
# 2. Bootstrap — generates secrets, spins up Postgres, runs migrations, mints your OAuth client
pip install -e src/cofounder_agent
poindexter setup --auto
# 3. Pull the core models (one-time, ~30 GB total)
ollama pull gemma3:27b && ollama pull phi4:14b && ollama pull qwen3:8b && ollama pull nomic-embed-text
# 4. Start everything
bash scripts/start-stack.sh up -d
# 5. Queue your first post
poindexter tasks create "Why Docker changed everything"
Then watch it work:
poindexter doctor for a full health report, poindexter tasks list for the queueA few minutes later the draft lands in your approval queue with its QA scores attached (poindexter tasks list --status awaiting_approval). You approve; it publishes. That's the loop.
| Model | Size | Role |
|---|---|---|
gemma3:27b | 16 GB | Writer, fallback, structured + media-script tasks |
phi4:14b | 9 GB | Adversarial QA critic — the hard quality gate |
qwen3:8b | 5 GB | Fast tasks — SEO, image decisions, summaries, routing |
nomic-embed-text | 274 MB | Embeddings for semantic search + memory retrieval |
These four run the core blog pipeline (research → write → QA → publish) on any 8 GB+ GPU. The critic is a different model family from the writer on purpose — cross-model QA means their biases don't cancel. Feature roles pull additional public models on demand: image QA + captioning use qwen3-vl:30b (~20 GB — needs headroom past the 8 GB minimum), and the optional voice agent loads its own STT/TTS models. The core pipeline runs without them.
The writer is the one model worth upgrading. Set pipeline_writer_model to any Ollama model you have:
ollama pull qwen3:30b # 18 GB — best speed/quality balance publicly available
ollama pull qwen3.5:35b # 23 GB — stronger prose, slower
ollama pull llama3.3:70b # 42 GB — highest quality, needs 48 GB+ VRAM or CPU offload
ollama pull glm-4.7:9b # 6 GB — lighter fallback for <16 GB VRAM
Every model routing decision (writer / critic / research / summarizer / embedder) lives in app_settings and can be swapped at runtime — no restart, no redeploy. Cloud models (Anthropic, OpenAI, Groq, OpenRouter) are an opt-in plugin gated by a spend guard.
One engine, eight jobs:
Run it on your machine. Own your data. No cloud lock-in.
Most AI content tools optimize for output volume. Poindexter optimizes for curation: it generates candidates, then makes each one survive 15 QA rails — cross-model LLM critics, DeepEval and Ragas evaluations, deterministic anti-hallucination validators, citation verification against the research corpus, and vision QA on every generated image. Roughly half of all drafts don't make it.
Real 30-day per-reviewer averages from the QA Rails dashboard — more reviewers than rails, because the DeepEval rail scores three and the citation rail two. The strict rails at the bottom are why the output doesn't read like AI slop — speed comes from generating more candidates and filtering hard, not from lowering the bar.
Every rail is DB-configurable: advisory or blocking per rail, thresholds tunable at runtime, and a bounded rescue cycle gives near-miss drafts one revision pass before the hard reject.
The stack treats itself like production infrastructure. A standalone watchdog daemon (the "brainstem") checks every service on a 5-minute cycle, restarts what it can, and pages you on Telegram/Discord for what it can't. The doctor command aggregates every health probe into one score:
Real output. Note the FAIL: the system caught its own throughput drop and said so — you find out from the tool, not from silence.
And the output side is a real website, not a JSON blob in a bucket somewhere:
gladlabs.io — built on the static JSON export. Bring any frontend: Next.js, Hugo, Astro, or a single HTML file.
It is not for marketing teams who want a one-click web app (use Jasper / Copy.ai), or anyone unwilling to run Docker.
Poindexter is a kernel + modules + capabilities stack. The kernel is the substrate everything rents — plugin registry, DI container, pipeline engine, settings. Business modules (content, finance, yours) compose capability plugins across 18 entry-point groups. Components never import each other — everything communicates through PostgreSQL (the "spinal cord"), and a standalone watchdog daemon (the "brainstem") can crash and restart without taking down the API.
flowchart LR
subgraph pc["Your PC — the whole factory"]
direction TB
brain["Brainstem — watchdog daemon<br>(standalone, self-healing)"]
modules["Modules<br>content · finance · yours"]
kernel["Kernel<br>plugin registry · DI · pipeline engine · settings"]
caps["Capabilities — 18 plugin groups<br>llm · image · video · audio · tts · taps · …"]
pg[("PostgreSQL + pgvector<br>the shared bus — no cross-imports")]
ollama["Ollama<br>local inference"]
end
store["Any S3-compatible storage<br>static JSON + RSS"]
fe["Any frontend<br>Next.js · Hugo · Astro · plain HTML"]
modules --> kernel
modules --> caps
caps --> ollama
kernel <--> pg
brain -.->|monitors + restarts| pg
kernel --> store --> fe
The content pipeline itself is a declarative LangGraph DAG stored in the database — 47 nodes covering research, writing, image generation, the 15 QA rails, SEO, and publish. Swap any piece as long as it writes the same tables. Full diagrams and design rationale in docs/architecture/.
| Feature | Description |
|---|---|
| Local AI by default | Ollama for inference. Your GPU, your data, zero API costs. |
| Cloud opt-in | LiteLLM provider plugin routes to Anthropic, OpenAI, Groq, OpenRouter — gated by cost guard |
| Anti-hallucination | 3 independent layers: prompts, multi-model QA, deterministic validator |
| DB-as-config | 1,700+ settings in PostgreSQL. Change with SQL, REST, or CLI. No deploys, no .env sprawl. |
| LangGraph pipelines | Declarative DAGs with Postgres checkpointing — resumable mid-run |
| Multi-modal output | Markdown posts, AI images, podcast audio, text-to-video (alpha) |
| Push-only output | Static JSON + RSS + JSON Feed 1.1 to any S3-compatible storage |
| Multi-site | One daemon manages N sites. Each site = config row + storage bucket. |
| Self-healing | Watchdog daemon monitors all services, restarts failures, alerts via Telegram/Discord |
| Production observability | Grafana, Prometheus, Loki, Pyroscope (CPU profiling), Sentry-compatible (GlitchTip) |
| OAuth 2.1 throughout | Every consumer (CLI, MCP, scripts) mints scoped JWTs. No static API keys. |
| 17,000+ tests | Unit coverage across all services, smoke tests on migrations, link-rot CI |
cost_guard)nomic-embed-text via Ollama → pgvectorEverything tunable lives in the app_settings database table — not environment variables. The only file on disk is ~/.poindexter/bootstrap.toml, created by poindexter setup: the database URL plus the few pre-DB secrets (Postgres password, OAuth signing key, optional Telegram/Discord alert channels).
poindexter settings list # view all settings
poindexter settings set auto_publish_threshold 80 # change at runtime — no restart
Every knob is also reachable over REST with an OAuth-minted JWT. See environment-variables.
Eighteen plugin entry-point groups let you extend the system without touching core code: Taps (pull data in — RSS, Slack, social), TopicSources (discover candidate topics), LLMProviders / ImageProviders / TTS / video / audio / captions, PublishAdapters (where finished posts go), Probes, Jobs, Stages, Reviewers, and full Modules that bundle plugins + migrations + routes into a versioned business function. The canonical list lives in plugins/registry.py::ENTRY_POINT_GROUPS.
Each plugin is a pip package registering a setuptools entry point:
pip install poindexter-tap-slack
poindexter settings set plugin.tap.slack '{"enabled": true, "config": {"workspace": "myteam"}}'
# my_package/slack_tap.py
from poindexter.plugins import Tap, Document
class SlackTap:
name = "slack"
interval_seconds = 3600
async def extract(self, pool, config):
async for msg in fetch_slack_messages(config):
yield Document(
source_id=f"slack/{msg.ts}",
source_table="slack",
text=msg.text,
metadata={"channel": msg.channel, "user": msg.user},
writer="poindexter-tap-slack",
)
# pyproject.toml
[project.entry-points."poindexter.taps"]
slack = "my_package.slack_tap:SlackTap"
Shipping samples (HelloTap, DatabaseProbe, NoopJob) live in src/cofounder_agent/plugins/samples/. The first production plugin is the LiteLLMProvider — Glad Labs eats its own dog food. Full design in plugin-architecture.
Poindexter is in alpha. Honest snapshot:
What works today
poindexter setup takes a fresh clone to a healthy local stack — no .env file, no manual secret wrangling.Known rough edges
If any of those would block your use case, that's worth knowing before you start. PRs welcome — see CONTRIBUTING.md.
The engine is free and open-source under Apache 2.0. Pro is for operators who want production-grade output without months of tuning.
| Tier | Price | What you get |
|---|---|---|
| Free | $0 | Full pipeline engine, nothing feature-gated — baseline prompts, all Grafana dashboards this repo ships, GitHub issues support |
| Pro | See gladlabs.ai | Production-tuned prompt packs exported from the live system, curated dashboard refreshes, prompt updates as the system is tuned, private VIP Discord, the Poindexter book (perk) |
Pro exists for the obvious case: you've installed the OSS, you've seen output that's almost there, and you want the version that's actually shipping content on gladlabs.io daily.
See Poindexter Pro → gladlabs.ai
Full technical docs at gladlabs.mintlify.app. Recommended path:
poindexter subcommandIssues and PRs welcome — see CONTRIBUTING.md. The screenshots above re-bake with node scripts/capture-readme-screenshots.mjs against a running stack.
main runs gitleaks (secrets), Trivy (CVEs), and syft+grype (SBOM + CVE scan)Apache License 2.0 — Copyright 2025-2026 Glad Labs LLC. Relicensed from AGPL-3.0 on 2026-04-29 — see CHANGELOG.
Built in the open by Glad Labs. If Poindexter is interesting, a ⭐ helps other operators find it.
8,551 commits
1,039 commits
356 commits
205 commits
Python
97.5%
The open-source AI content factory that runs on your PC — discovers topics, researches, writes, reviews with multi-model QA, and publishes. Local-first via Ollama, $0 API costs.
5
stars
10,376
commits
Python
primary language
Sep 8, 2026
updated
One engine that discovers topics, researches them, writes long-form posts, tears them apart in review, and publishes the survivors — on your GPU, with zero API costs.
Quick start · What it does · The QA gauntlet · Architecture · Docs · Pro
This is not a demo repo. Poindexter is the production system behind gladlabs.io — 200+ live posts and counting, every one generated, reviewed, and published by this pipeline on a single PC. Here is the operator's actual view of it running:
The operator console (ships with the repo, served by the worker at /console/), recorded live: a post mid-generation, the approval inbox with per-task QA verdicts, pipeline traces, and the system map.
The Pipeline dashboard (ships with the repo): drafts arrive scored, weak ones are auto-rejected, survivors wait for your one-click approval.
From clean machine to a running pipeline in about 30 minutes — most of it one-time model downloads.
Prereqs: Docker · Ollama · Python 3.13+ · NVIDIA GPU with 8 GB+ VRAM (CPU works, just slowly) · Node.js 22+ only if you want the optional Next.js frontend. Windows: run from Git Bash or WSL2.
# 1. Clone
git clone https://github.com/Glad-Labs/poindexter.git && cd poindexter
# 2. Bootstrap — generates secrets, spins up Postgres, runs migrations, mints your OAuth client
pip install -e src/cofounder_agent
poindexter setup --auto
# 3. Pull the core models (one-time, ~30 GB total)
ollama pull gemma3:27b && ollama pull phi4:14b && ollama pull qwen3:8b && ollama pull nomic-embed-text
# 4. Start everything
bash scripts/start-stack.sh up -d
# 5. Queue your first post
poindexter tasks create "Why Docker changed everything"
Then watch it work:
poindexter doctor for a full health report, poindexter tasks list for the queueA few minutes later the draft lands in your approval queue with its QA scores attached (poindexter tasks list --status awaiting_approval). You approve; it publishes. That's the loop.
| Model | Size | Role |
|---|---|---|
gemma3:27b | 16 GB | Writer, fallback, structured + media-script tasks |
phi4:14b | 9 GB | Adversarial QA critic — the hard quality gate |
qwen3:8b | 5 GB | Fast tasks — SEO, image decisions, summaries, routing |
nomic-embed-text | 274 MB | Embeddings for semantic search + memory retrieval |
These four run the core blog pipeline (research → write → QA → publish) on any 8 GB+ GPU. The critic is a different model family from the writer on purpose — cross-model QA means their biases don't cancel. Feature roles pull additional public models on demand: image QA + captioning use qwen3-vl:30b (~20 GB — needs headroom past the 8 GB minimum), and the optional voice agent loads its own STT/TTS models. The core pipeline runs without them.
The writer is the one model worth upgrading. Set pipeline_writer_model to any Ollama model you have:
ollama pull qwen3:30b # 18 GB — best speed/quality balance publicly available
ollama pull qwen3.5:35b # 23 GB — stronger prose, slower
ollama pull llama3.3:70b # 42 GB — highest quality, needs 48 GB+ VRAM or CPU offload
ollama pull glm-4.7:9b # 6 GB — lighter fallback for <16 GB VRAM
Every model routing decision (writer / critic / research / summarizer / embedder) lives in app_settings and can be swapped at runtime — no restart, no redeploy. Cloud models (Anthropic, OpenAI, Groq, OpenRouter) are an opt-in plugin gated by a spend guard.
One engine, eight jobs:
Run it on your machine. Own your data. No cloud lock-in.
Most AI content tools optimize for output volume. Poindexter optimizes for curation: it generates candidates, then makes each one survive 15 QA rails — cross-model LLM critics, DeepEval and Ragas evaluations, deterministic anti-hallucination validators, citation verification against the research corpus, and vision QA on every generated image. Roughly half of all drafts don't make it.
Real 30-day per-reviewer averages from the QA Rails dashboard — more reviewers than rails, because the DeepEval rail scores three and the citation rail two. The strict rails at the bottom are why the output doesn't read like AI slop — speed comes from generating more candidates and filtering hard, not from lowering the bar.
Every rail is DB-configurable: advisory or blocking per rail, thresholds tunable at runtime, and a bounded rescue cycle gives near-miss drafts one revision pass before the hard reject.
The stack treats itself like production infrastructure. A standalone watchdog daemon (the "brainstem") checks every service on a 5-minute cycle, restarts what it can, and pages you on Telegram/Discord for what it can't. The doctor command aggregates every health probe into one score:
Real output. Note the FAIL: the system caught its own throughput drop and said so — you find out from the tool, not from silence.
And the output side is a real website, not a JSON blob in a bucket somewhere:
gladlabs.io — built on the static JSON export. Bring any frontend: Next.js, Hugo, Astro, or a single HTML file.
It is not for marketing teams who want a one-click web app (use Jasper / Copy.ai), or anyone unwilling to run Docker.
Poindexter is a kernel + modules + capabilities stack. The kernel is the substrate everything rents — plugin registry, DI container, pipeline engine, settings. Business modules (content, finance, yours) compose capability plugins across 18 entry-point groups. Components never import each other — everything communicates through PostgreSQL (the "spinal cord"), and a standalone watchdog daemon (the "brainstem") can crash and restart without taking down the API.
flowchart LR
subgraph pc["Your PC — the whole factory"]
direction TB
brain["Brainstem — watchdog daemon<br>(standalone, self-healing)"]
modules["Modules<br>content · finance · yours"]
kernel["Kernel<br>plugin registry · DI · pipeline engine · settings"]
caps["Capabilities — 18 plugin groups<br>llm · image · video · audio · tts · taps · …"]
pg[("PostgreSQL + pgvector<br>the shared bus — no cross-imports")]
ollama["Ollama<br>local inference"]
end
store["Any S3-compatible storage<br>static JSON + RSS"]
fe["Any frontend<br>Next.js · Hugo · Astro · plain HTML"]
modules --> kernel
modules --> caps
caps --> ollama
kernel <--> pg
brain -.->|monitors + restarts| pg
kernel --> store --> fe
The content pipeline itself is a declarative LangGraph DAG stored in the database — 47 nodes covering research, writing, image generation, the 15 QA rails, SEO, and publish. Swap any piece as long as it writes the same tables. Full diagrams and design rationale in docs/architecture/.
| Feature | Description |
|---|---|
| Local AI by default | Ollama for inference. Your GPU, your data, zero API costs. |
| Cloud opt-in | LiteLLM provider plugin routes to Anthropic, OpenAI, Groq, OpenRouter — gated by cost guard |
| Anti-hallucination | 3 independent layers: prompts, multi-model QA, deterministic validator |
| DB-as-config | 1,700+ settings in PostgreSQL. Change with SQL, REST, or CLI. No deploys, no .env sprawl. |
| LangGraph pipelines | Declarative DAGs with Postgres checkpointing — resumable mid-run |
| Multi-modal output | Markdown posts, AI images, podcast audio, text-to-video (alpha) |
| Push-only output | Static JSON + RSS + JSON Feed 1.1 to any S3-compatible storage |
| Multi-site | One daemon manages N sites. Each site = config row + storage bucket. |
| Self-healing | Watchdog daemon monitors all services, restarts failures, alerts via Telegram/Discord |
| Production observability | Grafana, Prometheus, Loki, Pyroscope (CPU profiling), Sentry-compatible (GlitchTip) |
| OAuth 2.1 throughout | Every consumer (CLI, MCP, scripts) mints scoped JWTs. No static API keys. |
| 17,000+ tests | Unit coverage across all services, smoke tests on migrations, link-rot CI |
cost_guard)nomic-embed-text via Ollama → pgvectorEverything tunable lives in the app_settings database table — not environment variables. The only file on disk is ~/.poindexter/bootstrap.toml, created by poindexter setup: the database URL plus the few pre-DB secrets (Postgres password, OAuth signing key, optional Telegram/Discord alert channels).
poindexter settings list # view all settings
poindexter settings set auto_publish_threshold 80 # change at runtime — no restart
Every knob is also reachable over REST with an OAuth-minted JWT. See environment-variables.
Eighteen plugin entry-point groups let you extend the system without touching core code: Taps (pull data in — RSS, Slack, social), TopicSources (discover candidate topics), LLMProviders / ImageProviders / TTS / video / audio / captions, PublishAdapters (where finished posts go), Probes, Jobs, Stages, Reviewers, and full Modules that bundle plugins + migrations + routes into a versioned business function. The canonical list lives in plugins/registry.py::ENTRY_POINT_GROUPS.
Each plugin is a pip package registering a setuptools entry point:
pip install poindexter-tap-slack
poindexter settings set plugin.tap.slack '{"enabled": true, "config": {"workspace": "myteam"}}'
# my_package/slack_tap.py
from poindexter.plugins import Tap, Document
class SlackTap:
name = "slack"
interval_seconds = 3600
async def extract(self, pool, config):
async for msg in fetch_slack_messages(config):
yield Document(
source_id=f"slack/{msg.ts}",
source_table="slack",
text=msg.text,
metadata={"channel": msg.channel, "user": msg.user},
writer="poindexter-tap-slack",
)
# pyproject.toml
[project.entry-points."poindexter.taps"]
slack = "my_package.slack_tap:SlackTap"
Shipping samples (HelloTap, DatabaseProbe, NoopJob) live in src/cofounder_agent/plugins/samples/. The first production plugin is the LiteLLMProvider — Glad Labs eats its own dog food. Full design in plugin-architecture.
Poindexter is in alpha. Honest snapshot:
What works today
poindexter setup takes a fresh clone to a healthy local stack — no .env file, no manual secret wrangling.Known rough edges
If any of those would block your use case, that's worth knowing before you start. PRs welcome — see CONTRIBUTING.md.
The engine is free and open-source under Apache 2.0. Pro is for operators who want production-grade output without months of tuning.
| Tier | Price | What you get |
|---|---|---|
| Free | $0 | Full pipeline engine, nothing feature-gated — baseline prompts, all Grafana dashboards this repo ships, GitHub issues support |
| Pro | See gladlabs.ai | Production-tuned prompt packs exported from the live system, curated dashboard refreshes, prompt updates as the system is tuned, private VIP Discord, the Poindexter book (perk) |
Pro exists for the obvious case: you've installed the OSS, you've seen output that's almost there, and you want the version that's actually shipping content on gladlabs.io daily.
See Poindexter Pro → gladlabs.ai
Full technical docs at gladlabs.mintlify.app. Recommended path:
poindexter subcommandIssues and PRs welcome — see CONTRIBUTING.md. The screenshots above re-bake with node scripts/capture-readme-screenshots.mjs against a running stack.
main runs gitleaks (secrets), Trivy (CVEs), and syft+grype (SBOM + CVE scan)Apache License 2.0 — Copyright 2025-2026 Glad Labs LLC. Relicensed from AGPL-3.0 on 2026-04-29 — see CHANGELOG.
Built in the open by Glad Labs. If Poindexter is interesting, a ⭐ helps other operators find it.
8,551 commits
1,039 commits
356 commits
205 commits
Python
97.5%