Deterministic tmux relay/dedup daemons for AI-agent fleet monitoring
Project description
chitra
chitra is a set of systemd-supervised daemons that deterministically deliver text into tmux-hosted AI-agent sessions, record what happened, and watch their state. Its core dispatch, ledger, rate-limit, and routing paths are Python control loops, not an LLM deciding what to send or where to send it.
It was built to manage large parallel sessions with LLM coding agents, allowing the user to do more while chitra handles delivery and state tracking, applies optional LLM-backed judgment gates, and gates external harnesses such as Claude and Codex against clearly defined goals.
Scope
chitra delivers messages to LLM-driven sessions in tmux and observes their state. Delivery, queueing, routing, rate limiting, ledger writes, and state tracking are deterministic; routing is config lookup, not content judgment.
LLM judgment is layered on deliberately for specific gates: goal nudging, completion-claim review, and interview functionality. Chitra also dispatches to and gates external coding-agent harnesses such as Claude via claude-code and Codex; those agents do the content work, while chitra records, routes, reviews, and releases according to its own ledgers and policies.
Quickstart
pip install chitra-monitor # or: pip install git+https://github.com/ReticleWorks/chitra.git@<tag>
Replace <tag> with a released version from the tags page, or drop @<tag> to install from the default branch.
Requires Python 3.12+ and tmux on the host. See Install for local development setup, Configuration for environment variables, and Delivering into a tmux pane for what chitra actually does to a pane.
Why "chitra"
The name is a short form of Chitragupta, a figure from Hindu tradition described as the divine registrar and keeper of a complete, accurate ledger of deeds — one who records, verifies what is recorded, and reports to the decision-maker, but does not act on that decision-maker's behalf. That remains this package's contract: it observes, verifies against frozen goals and cited artifacts, gates release, and relays without doing an agent session's work for it. The name is used respectfully as a functional reference, not as religious imagery.
BrowserStack's chitragupta-node and chitragupta-rails are open-source SDKs that use the same name for structured JSON (JavaScript Object Notation) logging — attaching metadata to log lines rather than relaying or signing them. Different tool, same naming logic: the name attaches to something that records and structures what happened, not something that decides what should happen.
What's in this repo
chitra installs twelve command-line entrypoints backed by a set of small, single-purpose modules. dispatchd and triaged are the always-on daemons; the rest are periodic or ad-hoc tools.
Delivery
chitra.dispatch/chitra.dispatchd— drain a JSON order queue and deliver each message into a tmux session via bracketed paste, confirming delivery by grepping the session's own transcript. One writer per session (LaneLock); idempotent and crash-safe (see Delivery guarantees).chitra.ledger— an append-only, HMAC-signed log of every delivered message.
Monitoring
chitra.watchd— emits tmux pane-change and turn-end events and runs a completion audit on each finished turn.chitra.triaged/chitra.sweepd— deduplicated state-change events and a compact fleet-state feed for downstream monitors.chitra.draft_scanner— flags unsubmitted drafts left sitting in a tmux input box.
PR review
chitra.pr_review/chitra.pr_reviewd(chitra-pr-review) — deterministic blast-radius/diff-size pre-checks plus an isolated multi-reviewer security pass over one pull request's diff, logged to a signed ledger and reported as a plain PR comment. Never merges, approves, requests changes, or fails a required check by default; see thepr_reviewdmodule docstring andPRReviewPolicy.block_on_findings. Stock trigger:.github/workflows/pr-security-review.yml.
Goals and completion
chitra.goals— a per-lane goal store with a write-once enrolled done-condition, guarded byflock.chitra.goal_enforcement/chitra.completion_gate/chitra.close_gate— review a session's completion claim against its frozen goal and cited evidence; spend, credentials, and irreversible actions stay operator-gated.
Rate limiting
chitra.usage/chitra.rate_limit_guard/chitra.account_registry— read account usage and pause/resume lanes on provider limits or host load pressure, over a durable, crash-safe transaction. Seedocs/pause-recovery.md.
Rendering
chitra.board/chitra.convlog— a terminal roster of goals and open asks, and an append-only operator-brief conversation log.
Delivering into a tmux pane
Delivery into a live tmux session follows one path:
tmux display-message -p -t <target> '#{pane_in_mode}'— if1, the pane is in copy-mode (which silently swallows input); runtmux send-keys -X canceland wait briefly.printf '%s' "$text" | tmux load-buffer -b <name> -tmux paste-buffer -p -b <name> -t <target>— the-pflag is mandatory; without it, newlines act as Enter keypresses and the message can self-submit early.tmux send-keys -t <target> Enter- Confirm delivery by grepping the target session's transcript for the delivered text. "Looks sent" is not evidence.
For a remote target, each command is the same, ssh-wrapped to run on the actual target host. Checking the local tmux server's state, or grepping local transcripts, when the target is remote reports on the wrong host.
Delivery guarantees
- Single writer.
dispatchdholds aLaneLockper session id across each delivery, so two writers can't race to paste into the same session and corrupt its next turn. - Idempotent. Once a result file exists for an order, it is never redispatched, even across a restart. A crash between paste and result is reconciled with a send-nonce marker plus the same transcript-grep check, not a blind second paste.
- Authenticated. Every successful delivery appends an HMAC-SHA256-signed record to an append-only JSONL ledger; a reader with the signing key can prove a given message was delivered. This is a trusted-host model — anyone who can write to the ledger file can rewrite it — so treat "not in the ledger" as a strong signal, not tamper-proof evidence. See
chitra.ledger.verify_delivery.
Running the daemons
dispatchd and triaged run continuously. chitra-rate-limit-guard is a one-shot CLI meant to run on a timer. Example systemd units, with placeholder paths and service user, live under packaging/systemd/:
sudo cp packaging/systemd/chitra-rate-limit-guard.service.example /etc/systemd/system/chitra-rate-limit-guard.service
sudo cp packaging/systemd/chitra-rate-limit-guard.timer.example /etc/systemd/system/chitra-rate-limit-guard.timer
sudoedit /etc/systemd/system/chitra-rate-limit-guard.service # fill in placeholders
sudo systemctl daemon-reload
sudo systemctl enable --now chitra-rate-limit-guard.timer
Configuration
Each entrypoint is configured with CLI flags (--help on any command lists them) and a small set of environment variables. The most common:
| Env var | Default | Notes |
|---|---|---|
CHITRA_STATE_DIR |
/var/lib/chitra |
Base directory for the queue, ledger, and ledger key |
REMOTE_DISPATCH_HOSTS |
(empty) | Comma-separated allowlist of hosts dispatch may target over ssh |
CHITRA_CLAUDE_PROJECTS |
~/.claude/projects |
Root searched for transcript-grep delivery verification |
CHITRA_ROUTING_CONFIG |
(unset) | Optional task_type → routing-hint config; see docs/routing.yaml.example |
CHITRA_POLICY_CONFIG |
(unset) | Optional completion-gate and dispatch policy; see docs/policy.yaml.example |
The full set — ssh options, triage log paths, transcript globs — is documented per-command via --help.
Routing. A caller can tag a DispatchOrder with an opaque task_type. If a routing config is set, dispatchd maps that to a routing_hint (a model/harness preference the caller's system uses); an explicit routing_hint always wins, and chitra carries the hint through to the ledger but never acts on it.
Install
Requires Python 3.12+ and tmux (chitra shells out to the tmux binary; there is no Python tmux dependency).
pip install chitra-monitor # or: pip install git+https://github.com/ReticleWorks/chitra.git@<tag>
Not yet on PyPI — see docs/DESIGN.md for the packaging rationale.
For local development:
git clone https://github.com/ReticleWorks/chitra.git
cd chitra
pip install -e '.[test]'
pytest
Getting help
Questions and bug reports: open an issue. See CONTRIBUTING.md before opening a nontrivial PR; security reports go through SECURITY.md.
Authors
Trey (Reticle Works) with Claude and Codex.
License
MIT © 2026 Reticle Works. See LICENSE for the full text.
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 chitra_monitor-0.8.2.7.tar.gz.
File metadata
- Download URL: chitra_monitor-0.8.2.7.tar.gz
- Upload date:
- Size: 289.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5a79a4dfc5f598464d7e0bac0f55ca1d8e1d1006533ca03034a4935bd0866541
|
|
| MD5 |
d234dcde36f015f9a5eded634430717e
|
|
| BLAKE2b-256 |
89a253fd58fde59f8c0742bdfa2cdc3766036e7b40e1e14ea52cdf8650e93f9c
|
File details
Details for the file chitra_monitor-0.8.2.7-py3-none-any.whl.
File metadata
- Download URL: chitra_monitor-0.8.2.7-py3-none-any.whl
- Upload date:
- Size: 203.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8eaaf23f5daa9ca84c69354f5656c8e9dbb058300499d5a8c7ec69af4a5450d8
|
|
| MD5 |
352d6d70159f0b3d4e619fa28f58e026
|
|
| BLAKE2b-256 |
93e77a730aacee09c96feaac0485e7fcbfea445d20a79d0158cbb049a3de4c95
|