Patrick Buckley fac2393967 fix(#894): re-derive the render gate on DOM-live signals; drop the busy conflation
Review round 5 step-back (fix-era critical): the r4 gate's busy term
conflated 'a turn is executing' with 'this DOM holds live turn state'.
_editAndResend flips busy BEFORE its POST and /rewind emits only
clear_ui (no state_change), so the busy term skipped the truncation
render the rewind exists to produce and appended the resent bubble onto
the PRE-rewind transcript; /retry's regenerated turn likewise raced its
own clear_ui refetch.  The seam was re-derived once against the caller
x state matrix; the gate reads DOM-live signals only, split by scope:

- UNIVERSAL: dispatch seq (refetchSeq — overlapping fetches resolve
  last-DISPATCH-wins; an older snapshot landing late can neither
  double-render nor clear the latch over newer truth) and the content
  refs (skipping always beats stranding a ref; seeded callers null
  theirs before fetching, so it never blocks them).
- SEEDLESS-ONLY (keyed on the seedCursor arg): the
  .conv-batch--running DOM marker for the tool phase (NOT activeBatch —
  that is the pending-APPROVAL tracker, set only for opts.pending
  batches; ruled at-site), coordSend's busySource === 'optimistic'
  flavor (the one busy that marks un-committed DOM), and
  stream-OPENness (CONNECTING keeps the handle with a frozen cursor
  and a pending replay; handle-existence was not liveness — also
  applied to the retry's fire guard).  Seedless-only because the
  SEEDED resync renders over these deliberately: after a node dies
  mid-batch the --running class is dead residue no result will ever
  strip, and the resync's render IS the recovery — a universal term
  wedged the coord-restart scenario outright (family-run find; the
  r5 finders missed the seeded-path interaction).

Backstop comment corrected (r5): fresh/truncated SSE replays DO carry
a synthetic state_change (replay_ok does not) — a latched pane pays one
refetch per reconnect, bounded by reconnect jitter/backoff and #884's
server single-flight; heal-caused triggers remain structurally
impossible.  Caller fire-time ref guards demoted to the efficiency
layer at-site.  Contract test re-pins the gate: term presence in
comment-stripped CODE, universal-vs-seedless placement, wipe between
failure guard and latch-clear, no plain busy.  The edit-resend commit
gap under a second actor's clear_ui is accepted at-site (re-appears at
settle heal).  G4's honesty note names the tool-phase branch it
behaviorally detects; G5 wording replay_ok-precise.  Full coord family
(C + G1-G5) green; 136 pins green; 5 static mutants + the G4
behavioral control caught.
2026-07-24 15:04:31 -07:00
2026-07-05 01:57:54 -07:00
2026-07-21 15:30:35 -07:00
2026-07-21 15:30:35 -07:00

Turnstone

CI PyPI Python License Discord Sponsor

Self-hosted, local-first orchestration for tool-using AI agents. Give LLMs real tools — shell, files, search, web — and run them across your own cluster with direct HTTP routing and interactive interfaces. Your code, your models, your data stay on hardware you control: no telemetry, no phone-home.

Turnstone coordinator — parallel tool batches with judge-graded approval and child workstream tracking

Named after the Ruddy Turnstone (Arenaria interpres) — a shorebird that flips stones to discover what's hiding underneath.

What is a harness?

 :  s_{n+1} ~ T(s_n)   for n < τ*,    T = ρ ∘ (M_W ∘ π, E)

the primer →

Release Tracks

Track Install Docker Description
Stable pip install turnstone ghcr.io/turnstonelabs/turnstone:stable Production-grade. Bugfixes only.
Experimental pip install turnstone --pre ghcr.io/turnstonelabs/turnstone:experimental New features. May have rough edges.

See docs/releasing.md for the full release process.

What it does

Turnstone gives LLMs tools — shell, files, search, web, planning — and orchestrates multi-turn conversations where the model investigates, acts, and reports.

  • Local-first & private — runs entirely on hardware you control, with no telemetry and no phone-home. Point it at local models (vLLM, llama.cpp) or commercial APIs you hold the keys to — your prompts and data never transit a third party you didn't choose.
  • Bring your own models — OpenAI-compatible APIs (vLLM, llama.cpp, NIM), the Anthropic Messages API, and Google Gemini, mixed freely per role
  • Interactive sessions — terminal CLI or browser UI with parallel workstreams
  • Cluster dashboard — real-time view of every node and workstream, with a rendezvous routing proxy
  • Intent validation — an LLM judge (your model) grades every tool call with a risk assessment and evidence before it runs
  • MCP support — external tool servers with native deferred loading (Anthropic/OpenAI) or BM25 fallback
  • Team controls when you need them — optional RBAC, SSO, tool policies, and audit logs, all stored in your own database

Turnstone system architecture

Quickstart

pip install turnstone

# Terminal REPL
turnstone --base-url http://localhost:8000/v1

# Browser UI
turnstone-server --port 8080 --base-url http://localhost:8000/v1

# Cluster dashboard
turnstone-console --port 8090

For PostgreSQL (recommended for production):

export TURNSTONE_DB_BACKEND=postgresql
export TURNSTONE_DB_URL="postgresql+psycopg://user:pass@localhost:5432/turnstone"
turnstone-server --port 8080 --base-url http://localhost:8000/v1

Docker

One-line install — autodetects Ubuntu/Debian, Fedora/RHEL, Arch, and WSL, installs git + Docker if missing, generates secrets, and starts the stack:

curl -fsSL https://raw.githubusercontent.com/turnstonelabs/turnstone/main/run.sh | bash

Or, if you already have Docker, clone the repo and run it yourself:

docker compose up

That builds one image and brings up a full local cluster — PostgreSQL, console, Caddy, channel gateway, and 10 server nodes — with no .env required (it ships with insecure dev defaults). Open the dashboard at https://localhost:8443 (Caddy serves it over TLS with its own local CA — trust it once). Nodes boot without an LLM; add model backends from the console UI.

For production (released images from ghcr.io, real secrets required), use the bundled stack: docker compose -f turnstone/deploy/compose.yaml up.

See QUICKSTART.md for the install + troubleshooting walkthrough and docs/docker.md for Docker configuration.

Programmatic (SDK)

from turnstone.sdk import TurnstoneServer

with TurnstoneServer("http://localhost:8080", token="tok_xxx") as client:
    ws = client.create_workstream(name="demo")
    result = client.send_and_wait("Analyze the error logs", ws.ws_id, auto_approve=True)
    print(result.content)

Tools

Built-in tools for shell, files, search, web, memory, notifications, and autonomous sub-agents — plus external tools via MCP with native deferred loading. See docs/tools.md for the full reference and docs/mcp-registry.md for MCP configuration.

Architecture

Single-node: Client → Server (direct HTTP + SSE). No external dependencies beyond the database.

Multi-node: Client → Console (rendezvous routing proxy) → Server nodes. The console picks the target node for each workstream via rendezvous (HRW) hashing over the live service registry — pure function of (ws_id, live_nodes), no stored bucket state, deterministic across readers. A node join or drop only re-routes the keys that score highest on the affected node.

Component Purpose
turnstone Terminal CLI (REPL)
turnstone-server Web UI + REST API + SSE events
turnstone-console Cluster dashboard + routing proxy + admin panel
turnstone-channel Channel gateway (Discord and Slack adapters)
turnstone-admin User/token management CLI
turnstone-eval Headless measurement — scores tool-use against expected actions
turnstone-optimizer Prompt/tool optimizer (UCB self-modify loop over the eval substrate)
turnstone-doctor LLM-backed cluster diagnostics

Diagrams

UML diagrams in docs/diagrams/:

Diagram Description
System Context Components and external dependencies
Package Structure Python modules and dependency graph
Core Engine SessionUI, ChatSession, LLMProvider
Conversation Turn Message lifecycle through the engine
Tool Pipeline Prepare / approve / execute
Workstream States State machine transitions
Console Data Flow Dashboard data collection
Deployment Docker Compose topology
Auth JWT, scopes, login flows
Channels Discord / Slack adapters + routing
Judge Intent validation pipeline
OIDC SSO authorization code flow

Documentation

Topic Link
Configuration reference docs/settings.md
API reference docs/api-reference.md
Docker deployment docs/docker.md
Intent validation (judge) docs/judge.md
Governance & RBAC docs/governance.md
OIDC SSO docs/oidc.md
TLS / mTLS docs/tls.md
Channel integrations docs/channels.md
Console dashboard docs/console.md
Eval harness docs/eval.md
Tools reference docs/tools.md
MCP integration docs/mcp-registry.md

Requirements

  • Python 3.11+
  • An OpenAI-compatible API endpoint, Anthropic API key, or Google Gemini API key
  • Optional: Discord / Slack channel integrations (pip install turnstone[discord,slack])
  • Git LFS for cloning (diagram PNGs)

Support

Turnstone is free, Apache-2.0, and self-hosted — no paid tier, no telemetry, no upsell. If it saves you time or you'd like to help keep development moving, you can sponsor the project:

❤ Sponsor Turnstone → · one-off via PayPal

Sponsorship is entirely optional and funds maintenance, new features, and infrastructure. Prefer to contribute in other ways? Filing issues, improving docs, and pull requests help just as much.

Community

Questions, ideas, or want to show what you're building? Join us on Discord: discord.gg/Nh3bWMacaq.

License

Apache License 2.0, as of version 1.6.0. Versions 1.5.x and earlier remain under the Business Source License 1.1 they shipped with.

S
Description
No description provided
Readme Apache-2.0 114 MiB
Languages
Python 87.6%
JavaScript 8.7%
CSS 1.8%
HTML 1%
TypeScript 0.5%
Other 0.3%