Scrape, analyze, and explore discussions from any Discourse forum.
- Scrape — Download all topics, posts, and metadata as structured JSON. Supports delta sync.
- Analyze — DuckDB analytics (tag distribution, top contributors, activity trends, keyword search, SQL REPL).
- Discover — Derive an entity-type vocabulary tailored to your forum by sampling topics with an LLM. Drives extraction quality in step 4.
- Query — Ask natural-language questions using a local GraphRAG knowledge graph (LightRAG + OpenAI/Ollama).
- Visualize — Interactive HTML graph explorer: entities, relationships, communities.
Everything runs locally — no cloud services required except LLM calls if you opt for OpenAI and the initial scrape.
Requires Python ≥ 3.10 and uv. For GraphRAG features, also install Ollama or bring an OpenAI key.
uv syncA committed fixture under sample/fixtures/seed42-tiny/ carries a full deterministic forum (33 topics / 116 posts, 404 KB of JSON) plus the ~10 MB of GraphRAG artefacts the offline tools need. Try the analyzer + visualizer end-to-end without scraping anything:
uv run discourse-explorer stats --path sample/fixtures/seed42-tiny categories
uv run discourse-explorer visualize sample/fixtures/seed42-tiny --openThe fixture comes from the synthetic-forum seeder under sample/ — see sample/README.md for the Docker-stack path that lets you regenerate it locally and test the live init / extend paths against a real Discourse instance.
A single checkout supports multiple forums: the project root has a 1-line selector, each forum has its own config directory.
# 1. Selector at project root (one line, points at whichever forum is "active")
echo 'DISCOURSE_DATA_DIR=./data/my-forum' > .env
# 2. Per-forum config (URL, auth, models, gleaning — all env vars for this corpus)
mkdir -p ./data/my-forum/config
cp discourse_explorer/config/env.example ./data/my-forum/config/.env
# edit ./data/my-forum/config/.envPriority when both dotenv files set the same key: data-dir wins. Shell exports override both. CLI flags override everything.
Full env-var reference and layering rules: docs/analysis/vocabulary-and-config.md.
Edit <data-dir>/config/.env and pick one:
| Method | Env vars | Notes |
|---|---|---|
| API key (preferred) | DISCOURSE_API_KEY + DISCOURSE_API_USERNAME |
Generate at Discourse Admin → API → New API Key. |
| Session cookie (fallback) | DISCOURSE_COOKIE |
F12 → Cookies → copy _t value. Expires in a few weeks. |
| OIDC / Keycloak | DISCOURSE_USERNAME + DISCOURSE_PASSWORD |
Automated SSO. May not work with all setups. |
Priority at runtime: API key ≻ cookie ≻ OIDC.
Also set DISCOURSE_URL=https://discourse.example.com in the same file for unflagged scraper runs.
| Tool | Purpose | Reference |
|---|---|---|
scrape |
Download topics + posts + metadata; delta sync | Manual §1 |
stats |
DuckDB analytics + SQL REPL | Manual §2 |
discover-types |
Distill an entity-type vocabulary from sampled topics | Manual §3 — Discover |
query |
Build the knowledge graph (--index) and ask questions |
Manual §3 — Build · Ask |
visualize |
Render the interactive HTML graph explorer | Manual §4 |
| Coding-agent skills | Guided end-to-end workflows for Claude Code and Codex | Manual — Guided workflows |
Indexing runs for minutes to hours — launch it with
scripts/index.sh, not the bare CLI.DISCOURSE_DATA_DIR=<data-dir> ./scripts/index.sh --resume # add new topics, replace edited ones (cheap) DISCOURSE_DATA_DIR=<data-dir> ./scripts/index.sh --full # DESTRUCTIVE full rebuildThe script detaches the run into its own session so it survives the shell, refuses to start a second indexer on the same data dir, and reports failure instead of printing a PID for a run that already died. A mode is required — there is no default, because the destructive one would be a poor thing to get by typo.
--resumeskips topics whose content is unchanged, and for a topic that did change it deletes the documents that topic produced last time before re-seeding it. Without that step an edit accretes: a renamed tag or a departed poster stays in the graph forever alongside its replacement. Watch theN stale doc(s) purgedfield in thePass 1 complete:line to see it happen.
Claude Code and Codex share one instruction and skill source. AGENTS.md links to CLAUDE.md, sample/AGENTS.md links to sample/CLAUDE.md, and .agents/skills links to the canonical .claude/skills directory. There are no copied skill files to synchronize.
All host-specific tool and model bindings are configured once in .claude/skills/HOST-COMPATIBILITY.md. Its ROUTER and EXECUTOR table is the single place to select a different executor model for Claude Code or Codex. Individual SKILL.md files deliberately use semantic operations such as “ask the user” and “delegate execution”; do not add host API names or model IDs to them.
To support another agent harness, add its bindings to the compatibility contract and expose the canonical .claude/skills directory through that harness's native discovery path or a symlink. If the harness cannot follow symlinks, configure it to read the canonical directory directly rather than maintaining a copied skill tree.
Executor availability is checked at the point each host can observe it. Claude Code checks its documented environment override but cannot detect every organization allowlist fallback from inside a session. Codex requests its configured executor model explicitly and reports a rejected spawn before any fallback. A host without a suitable subagent runs the resolved work in the main conversation and states that the executor-tier optimization was unavailable. The compatibility contract contains the exact behavior and current bindings.
docs/MANUAL.md— per-tool usage reference: CLI flags, env vars, examples, the end-to-end workflow.AGENTS.md/CLAUDE.md— shared maintainer-facing map of the codebase and invariants.docs/analysis/— deep-dives on indexing, canonicalization, visualization, configuration.docs/lightrag/— read before editingquery.pyordiscover_types.py.docs/discourse/— Discourse JSON shape + terminology.docs/ideas/— forward-looking proposals.
