Dialectical knowledge base construction via the Elenchus protocol
Project description
Elenchus
A standalone system for dialectical knowledge base construction, implementing the Elenchus protocol (Allen 2026) with a DuckDB material base backend.
The respondent develops a bilateral position [C : D] through natural language dialogue with an LLM opponent. Accepted tensions become material implications in a NMMS material base satisfying Containment.
Requirements
- Python 3.11+
- An LLM API key (Anthropic, OpenRouter, or any OpenAI-compatible provider)
Installation
pip install elenchus
export ELENCHUS_API_KEY=sk-ant-... # or ANTHROPIC_API_KEY
For development:
git clone https://github.com/bradleypallen/elenchus-server.git
cd elenchus-server
pip install -e ".[dev]"
User Guide — full documentation covering concepts, the web interface, CLI, configuration, and a worked example.
Usage
First-time setup (multi-user mode)
Elenchus is a multi-user platform. Before the first sign-in you create an admin actor; everyone else joins by accepting an invite that admin issues.
# 1. Bootstrap the admin.
elenchus admin create --email admin@local --name "Admin"
# (prompts for a password, or reads ELENCHUS_ADMIN_PASSWORD)
# 2. Start the server.
elenchus
# 3. Open the browser and sign in as the admin.
# 4. In the admin dashboard, issue an invite to each user.
# The dashboard renders the full ?token=... URL — share that link.
# 5. Users click the link, choose a display name and password, and land
# in the app.
Upgrading from a single-user install? Run elenchus migrate-legacy once
to register every existing ./dialectics/*.duckdb under the admin's
account and relocate it into the per-actor directory layout. Idempotent.
Web interface
elenchus
Options: --port, --model, --api-key, --base-url, --protocol, --data-dir (see elenchus --help).
Open the URL shown in the terminal (default http://localhost:8741). The web interface provides:
- Creating and resuming dialectics scoped to the current user
- Natural language dialogue with the LLM opponent
- Live bilateral state display [C : D]
- Tension resolution (accept/contest)
- Material implications accumulating in I
- Derivability queries against the material base
- An admin-only dashboard for issuing invites and managing users
Command line
# Interactive session (in-memory)
elenchus-cli --name "My Inquiry"
# Persistent session (saved to DuckDB file)
elenchus-cli --db my_inquiry.duckdb --name "My Inquiry"
# Resume a saved session
elenchus-cli --db my_inquiry.duckdb
API
All /api/sessions/* and /api/admin/* routes require an authenticated
session. The flow is: POST to /api/auth/login to get a session cookie,
then send subsequent requests with that cookie. A dialectic is addressed
by the numeric session_id returned on create.
# 1. Log in (saves the cookie to ./cookies.txt).
curl -c cookies.txt -X POST http://localhost:8741/api/auth/login \
-H "Content-Type: application/json" \
-d '{"email": "you@example.com", "password": "..."}'
# 2. Create a dialectic — the response includes "session_id" (e.g. 1).
curl -b cookies.txt -X POST http://localhost:8741/api/sessions \
-H "Content-Type: application/json" \
-d '{"name": "prov-o", "topic": "PROV-O Starting Point Terms"}'
# 3. Send a message.
curl -b cookies.txt -X POST http://localhost:8741/api/sessions/1/message \
-H "Content-Type: application/json" \
-d '{"message": "Entity is a thing with fixed aspects."}'
# 4. Get state.
curl -b cookies.txt http://localhost:8741/api/sessions/1
# 5. Accept a tension.
curl -b cookies.txt -X POST http://localhost:8741/api/sessions/1/tensions/1 \
-H "Content-Type: application/json" \
-d '{"action": "accept"}'
# 6. Check derivability.
curl -b cookies.txt -X POST http://localhost:8741/api/sessions/1/derive \
-H "Content-Type: application/json" \
-d '{"gamma": ["entity_fixed_aspects"], "delta": ["individuation"]}'
# 7. List your sessions (admins see every base in the platform).
curl -b cookies.txt http://localhost:8741/api/sessions
The legacy name-keyed routes (
/api/dialectics/{name}/...) are retained as a compatibility alias — the study-participant flow still uses them — but new clients should use/api/sessions/{id}.
Admin-only routes: /api/admin/invites, /api/admin/users,
/api/admin/users/{id}/{deactivate,reactivate}, /api/admin/backup,
/api/admin/audit. Non-admins get a 403.
Architecture
src/elenchus/
├── server.py ──→ opponent.py ──→ LLM API (Anthropic / OpenAI-compatible)
│ ↓
│ dialectical_state.py
│ ↓
│ material_base.py
│ ↓
│ dialectics/*.duckdb
├── static/index.html
├── cli.py
└── pdf_report.py
- server.py: FastAPI app serving the API and static frontend
- opponent.py: LLM oracle — sends state to LLM API (Anthropic or OpenAI-compatible), parses structured responses, applies state transitions
- dialectical_state.py: Definition 4 — S = ⟨[C : D], T, I⟩ backed by DuckDB
- material_base.py: Definition 5 — B = ⟨L_B, |∼_B⟩ with pyNMMS-based derivability
- dialectics/*.duckdb: Persistent state files (one per dialectic)
Persistence
The data directory ($ELENCHUS_DATA, default ./dialectics/) holds:
platform.duckdb— actors, auth sessions, invites, magic links, base ownership, platform settings. Held open by the server for its lifetime.bases/{actor_id}/{base_name}.duckdb— one file per dialectic, owned byactor_id. Each file contains the atomic language L_B, all assessments (the base consequence relation |∼_B), the bilateral position [C : D], open and resolved tensions, and conversation history.backups/elenchus-*.tar.gz— output ofscripts/backup.py/POST /api/admin/backup(see Operations below).
Schema versions live in each file's meta.schema_version. The
migration runner brings every file forward at open time; see
src/elenchus/migrations/README.md
for how to add a new migration.
Operations
Full deployment guide — systemd unit, Nginx + TLS, backup cron, log
rotation, and a pre-pilot checklist — is in
docs/OPERATIONS.md. See also the task-oriented
guides on the documentation site:
Administration (roles, dashboard, invites,
users, cost, audit, backups, alerting), Running a Study
(conditions, participant flow, questionnaires, blinded judging, export),
and Deployment (local / production VM / cloud).
Quick reference:
# Liveness + readiness probe (unauthenticated; for uptime monitors).
curl -sf http://localhost:8741/healthz
# {"status":"ok","schema_version":7,"phase_b_enabled":false,
# "llm_configured":true,"checks":{"platform_db":"ok","data_dir":"ok"}}
# Audit drift between platform.duckdb and the filesystem.
elenchus audit
# Run a one-shot backup (everything → backups/elenchus-YYYYmmdd-HHMMSS.tar.gz).
# Server must be running; this hits POST /api/admin/backup.
python scripts/backup.py --email admin@local --password ...
# Cron entry (3 AM daily, keep latest 14 archives):
# 0 3 * * * ELENCHUS_BACKUP_EMAIL=admin@local ELENCHUS_BACKUP_PASSWORD=... \
# python /opt/elenchus/scripts/backup.py >> /var/log/elenchus-backup.log 2>&1
# Agent-driven pilot simulation — robustness check before a live study.
# Drives researcher + participants (both conditions) + judges + export
# end-to-end against the real API. 'scripted' is free + deterministic
# (CI gate); 'llm' uses real personas for a pre-pilot dress rehearsal.
elenchus sim # 4 participants, 2 judges, scripted
elenchus sim --driver llm # real LLM personas (needs an API key)
The simulation validates the platform (every role, every transition, blinding mechanics, cost, error handling under the real request stack) — not the science. LLM participants prove the machinery is robust; whether the two conditions produce different-quality outputs is what the human pilot measures.
# Per-persona MP4 walkthroughs — watch the system being used through
# the real UI, one video per perspective (participant in each
# condition, judge, researcher). Drives the served frontend in a
# headless browser; needs the [e2e] extra + a one-time chromium install.
pip install -e ".[e2e]" && python -m playwright install chromium
python scripts/record_demo.py --out demo-videos # scripted (free)
python scripts/record_demo.py --driver llm # real dialogue
# Watch a dialectic unfold between two LLMs from a positum: a respondent
# (a domain expert defending a posited paragraph) vs the real Elenchus
# opponent. Prints the transcript + the resulting bilateral position
# [C : D], accepted material implications, and open tensions.
python scripts/run_dialectic.py # default sci-ontology positum
python scripts/run_dialectic.py --positum @paragraph.txt \
--domain "the Gene Ontology" --turns 6 \
--respondent-model claude-sonnet-4-6 --out dialectic.json
DuckDB is a single-writer-per-file store, so the production server
runs as one process; horizontal scaling means migrating the
platform DB to Postgres (the db/registry.py boundary is shaped to
absorb that swap without touching route handlers). See
design-notes/architecture-vision.md
for the larger framing.
Configuration
Environment variables:
ELENCHUS_API_KEY: Required. Your LLM API key (also acceptsANTHROPIC_API_KEY).ELENCHUS_MODEL: LLM model for the oracle (default:claude-opus-4-6)ELENCHUS_BASE_URL: API base URL for OpenAI-compatible providers (e.g.https://openrouter.ai/api/v1)ELENCHUS_PROTOCOL: API protocol —anthropicoropenai(auto-detected from base URL)ELENCHUS_DATA: Directory for.duckdbfiles (default:./dialectics)PORT: Server port (default:8741)SESSION_COOKIE_SECURE: Set totruein production behind HTTPS so the auth cookie carries theSecureflag.BCRYPT_ROUNDS: bcrypt cost factor for password hashing (default 12). Tests override this to 4 to keep the suite fast — do not lower in production.ELENCHUS_ENABLE_PHASE_B: opt-in flag for the theory-articulation speech acts (ASSERT_IMPLICATION,INTRODUCE_BEARER,RETRACT_IMPLICATION). Off by default: the live message route exposes only{COMMIT, DENY, ACCEPT_TENSION, CONTEST_TENSION, RETRACT, REFINE}to match the Sloan study's Elenchus-condition vocabulary. Set to1/trueoutside study contexts.
Using OpenRouter
export ELENCHUS_API_KEY=sk-or-...
export ELENCHUS_BASE_URL=https://openrouter.ai/api/v1
export ELENCHUS_MODEL=anthropic/claude-opus-4-6
elenchus
Any OpenAI-compatible endpoint works the same way (Together, Groq, etc.).
License
MIT — Copyright © 2026 University of Amsterdam. Author: Bradley P. Allen.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file elenchus-0.3.3.tar.gz.
File metadata
- Download URL: elenchus-0.3.3.tar.gz
- Upload date:
- Size: 364.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.11.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6b975197b0c3e4afeb2d381a25fe3c482f8c3cad1b0c7de345eeb73721ab4ed3
|
|
| MD5 |
c3d646e7419dfd60bf02de81e7f2aafb
|
|
| BLAKE2b-256 |
84dcc27e1840bc64eca3bb0b19c81065a066f621e1be1c467ee5a75a0bf6300a
|
File details
Details for the file elenchus-0.3.3-py3-none-any.whl.
File metadata
- Download URL: elenchus-0.3.3-py3-none-any.whl
- Upload date:
- Size: 198.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.11.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f7333d377712715d1d346175c2f547869972fde932ffb42ac4deff68df1a37ff
|
|
| MD5 |
c3ae4d09f4712046fc59f6b62bc64b5c
|
|
| BLAKE2b-256 |
b3fdbfcff7984b929a7cb10d1243d8fa1c7d62783095aef89420fc61e200b0b4
|