Plural
Multiplayer sessions for one AI coding agent.
One agent. One host. Everyone in the room.
Quick start · How it works · Security · Architecture · Contributing
Plural turns a solo AI agent session into a shared one. The agent runs on the host's machine with the host's API key. Teammates join from a terminal or a browser, watch the run live, and propose prompts. The host stays in control: every proposal and every risky tool call goes through the host's approval.
┌────────────┐ ┌──────────────┐ ┌─────────────┐
│ guest TUI │◀──ws──▶│ │◀──ws──▶│ host │
├────────────┤ │ relay │ │ ├ agent │──▶ LLM API
│ guest web │◀──ws──▶│ (optional, │ │ ├ policy │
└────────────┘ │ dumb pipe) │ │ ├ tools │──▶ workspace
└──────────────┘ │ └ event log│
└─────────────┘
Features
- Shared live session. Everyone sees the same transcript, tool calls and diffs as they stream.
- Proposals, not prompts. Guests suggest prompts. The host approves, edits or rejects them.
- Host-only approvals. Write and exec actions need host consent. Guest-originated turns are tainted and always ask. Rejected actions are not re-asked: after repeated rejections of the same kind the agent is told to ask the user instead, and "Block (session)" denies a whole command class.
- Attribution. Every turn records who wrote it and who approved it.
- Policy engine. Allow, ask and deny rules with last-match-wins. Base deny rules cannot be overridden.
- Event-sourced. An append-only SQLite log is the source of truth. Reconnect, replay and audit come for free.
- Isolation. Tools are confined to the workspace. Secret paths are denied and output is redacted.
- Direct or relayed. Connect over LAN directly, or through a relay that never sees permissions and never stores content.
- Terminal clients. A Textual TUI and a plain line mode. A browser client is in progress.
- Bring your own key. API keys only. Plural never touches subscription credentials.
Requirements
- Python 3.12 or newer
- uv 0.5+
- macOS or Linux (Windows through WSL)
- Node.js 22+ (only to work on the in-progress web client)
Installation
curl -fsSL https://raw.githubusercontent.com/Plural-Organization/Plural/main/install.sh | sh
The script installs uv if it is missing, then Plural. Or install it yourself:
uv tool install plural-agent
The package is plural-agent; it installs the plural command. Other ways:
pipx install plural-agent
uvx --from plural-agent plural host
From source, clone the repository and run from its root:
uv sync
Check your environment:
plural doctor
Quick start
Start a session in your project:
uv run plural host --workspace .
The host prints a one-time invite link and opens the TUI. Send the link to a teammate:
uv run plural join "ws://127.0.0.1:7777/r_…?invite=…" --name alia
The default provider is fake, a scripted demo that needs no API key. Plain text lists the workspace. $ cmd asks to run cmd through bash and waits for host approval.
Through a relay
uv run plural relay --bind 0.0.0.0 --port 8787
uv run plural host --relay wss://relay.example.com
A ready-made deployment with Caddy and TLS lives in deploy/.
Usage
Commands
| Command | Purpose |
|---|---|
plural host |
Start a host session, direct mode or --relay URL |
plural join <link> |
Join as a guest, --plain for a line-mode stream |
plural relay |
Run the relay router |
plural doctor |
Check the environment and workspace |
plural export <room> |
Dump a session's event log to JSONL |
Run plural <command> --help for all options.
TUI keys
| Key | Action |
|---|---|
ctrl+r |
Review pending proposals and approvals |
ctrl+o |
Create an invite link |
ctrl+p |
View and edit session policy rules (host) |
esc |
Abort the current run |
ctrl+q |
Quit |
Prompt-line commands work in the TUI and in plural join --plain: /abort, /compact, /policy (list rules), /policy allow|ask|deny <tool> <pattern>, /policy rm <rule id>.
Configuration
Every setting can be passed as an environment variable with the PLURAL_ prefix.
| Variable | Default | Description |
|---|---|---|
PLURAL_PROVIDER |
fake |
fake, anthropic or openai |
PLURAL_MODEL |
claude-opus-5-5 for anthropic |
Model id, required for openai |
PLURAL_API_KEY |
ANTHROPIC_API_KEY / OPENAI_API_KEY |
Provider API key |
PLURAL_BASE_URL |
provider default | Custom endpoint, e.g. a local OpenAI-compatible server |
PLURAL_EFFORT |
provider default | low, medium, high, xhigh or max |
PLURAL_MAX_OUTPUT_TOKENS |
provider default | Output token cap per model response |
PLURAL_WORKSPACE |
current directory | Directory the agent works in |
PLURAL_DATA_DIR |
~/.plural |
Event logs, host key, attachments |
PLURAL_BIND_HOST |
127.0.0.1 |
Bind address for direct mode |
PLURAL_PORT |
7777 |
Port for direct mode |
PLURAL_RELAY_URL |
none | Relay URL |
PLURAL_PUBLIC_URL |
none | Public URL used in invite links |
PLURAL_FIRST_TOKEN_TIMEOUT_S |
120 |
Seconds to wait for the model's first output before the run fails |
PLURAL_IDLE_TIMEOUT_S |
60 |
Seconds of silence mid-response before the run fails |
PLURAL_CONTEXT_WINDOW |
128000 |
Model context size in tokens; set it to the server's real window for local models |
PLURAL_COMPACT_AT |
0.75 |
Fraction of the window at which the conversation is summarized |
PLURAL_APPROVAL_TIMEOUT_S |
120 |
Seconds before an approval expires |
PLURAL_REJECTION_LIMIT |
2 |
Rejections of one kind of action per run before the agent stops asking the host |
PLURAL_TOKEN_TTL_S |
86400 |
Capability token lifetime, at most 24h |
PLURAL_INVITE_TTL_S |
3600 |
Invite link lifetime |
Providers
Two adapters cover most models: anthropic uses the official Anthropic SDK, and openai speaks the OpenAI Chat Completions format, which also covers OpenRouter, Groq, Gemini, DeepSeek and local servers such as Ollama.
PLURAL_PROVIDER=anthropic ANTHROPIC_API_KEY=sk-ant-... plural host
PLURAL_PROVIDER=openai PLURAL_MODEL=gpt-5 OPENAI_API_KEY=sk-... plural host
PLURAL_PROVIDER=openai PLURAL_MODEL=qwen2.5-coder PLURAL_BASE_URL=http://localhost:11434/v1 plural host
How it works
- The host starts a session. A
SessionActorowns the room and processes every command in order: append to the log, reduce state, publish. - A guest joins with an invite and receives a signed Ed25519 capability token bound to the room and connection.
- The guest proposes a prompt. The host approves it, and it enters the agent's queue attributed as
[alia, proposal approved by host]. - The agent loop streams the model's reply. Each tool call is prepared, evaluated by the
PolicyGate, and either runs, waits for the host, or is denied. - Every event goes to the event log first and is then fanned out to clients. A client that reconnects asks for events
sinceits lastseq.
Security model
- The agent and the API key live only on the host. Guests never see the key.
- Permission decisions are made only by the host. The relay is a dumb pipe.
- No tool runs without passing the
PolicyGate. There are no bypasses. - Tools are confined to the workspace with
realpathchecks and a deny-list for secret files such as.env, keys and credentials. bashruns with a clean environment, in its own process group, with a timeout.- Secrets are redacted before anything is written to the log or sent to clients.
See SECURITY.md to report a vulnerability and docs/threat-model.md for the full threat model.
Architecture
Plural is a hexagonal, event-sourced Python app. Layers are enforced with import-linter.
src/plural/
├── protocol/ wire models, envelope, codec (pure)
├── core/ state reducer, roles, policy engine (pure)
├── security/ tokens, host keys, redaction
├── agent/ harness: loop, tools, sandbox, providers
├── storage/ SQLite event store, migrations, export
├── host/ session actor, gateway, transports
├── relay/ dumb relay router
├── client/ connection, reconnect, projection
├── cli/ Typer entry point
└── tui/ Textual app
web/ browser client (in progress)
coreandprotocoldo no I/O.agentknows nothing about transports or storage.relayknows nothing about sessions, policy or content.clientknows nothing about the host internals.
Design notes: docs/adr/ · Wire protocol: docs/protocol.md
Development
uv sync
uv run pytest
Full check, the same one CI runs:
uv run ruff check . && uv run ruff format --check . && uv run pyright && uv run lint-imports && uv run pytest
Regenerate the protocol JSON schema for the web client:
uv run python scripts/gen_schema.py
Web client (in progress):
cd web
npm ci
npm run dev
Roadmap
- Protocol, event log and session actor
- Policy engine and host approvals
- Workspace tools and local sandbox
- Direct and relay transports
- TUI and plain clients
- Anthropic and OpenAI-compatible providers
- Web client (in progress)
- Context compaction
- Container sandbox
- End-to-end and load tests
Contributing
Contributions are welcome. Read CONTRIBUTING.md first. In short:
- Open an issue before large changes.
- Use Conventional Commits.
- New dependencies need discussion, and large ones need an ADR.
- Make sure the full check passes.
Acknowledgements
Plural learns from Pi, OpenCode and Hermes.
License
Plural is licensed under the Apache License 2.0.
Metadata
Release files for plural-agent 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| plural_agent-0.1.0.tar.gz | 207.6 kB | With provenance | Details for plural_agent-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| plural_agent-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for plural_agent-0.1.0-py3-none-any.whl |
Total release size: 301.0 kB
Release files / plural_agent-0.1.0.tar.gz
| Download URL | plural_agent-0.1.0.tar.gz |
|---|---|
| Size | 207.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f43fec0399c9f6a0cb6d84d2a23e5e0d55181d42ab4903c7eb4c3fbd5d794ad2
|
|
BLAKE2b-256 checksum How to use checksums |
4d3aa9b57414794baeb1a79dc0e99779d211245c9123dae20e5a19ac7ee6f370
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 25, 2026.
Transparency logRelease files / plural_agent-0.1.0-py3-none-any.whl
| Download URL | plural_agent-0.1.0-py3-none-any.whl |
|---|---|
| Size | 93.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b0b0579feab98da1aee7fb5050a25692e0000d8e7fe0ff967e5b93087ec510dc
|
|
BLAKE2b-256 checksum How to use checksums |
cdb61fbd888ed6e2a3f6da98c0cc1ab9fda3b0e4a38f2c6fa5cb56f511e4e5f9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 25, 2026.
Transparency logSecurity for plural-agent 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for plural_agent-0.1.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 25, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.