Repository files navigation

Tether

Major redesign of the dashboard Native multiplexing. Tmux no longer required. Constructed in such a way that Claude could easily set it up for you. Not everything may work correctly. Consider this beta, and an opportunity for contribution. App will flesh out and smooth out more in the coming weeks. Hot reload function added for dashboard, mapped to ctrl+shift+R

A content-addressed messaging layer for multi-agent development workflows. Send a message from Claude Code to Codex CLI. Wake Gemini from a tmail. Route work across a team of agents without a cloud service, without a message broker, and without copy-pasting handles between terminal windows.

Tether Dashboard — Network Graph

Five agents online, message routes as edges, real-time feed streaming on the right.


Why this exists

A lot of people are fine with just Claude or just Codex, and that's usually enough, but sometimes when you need differing perspectives, red-teaming, QA/QC, and someone to poke holes in the design to strengthen it, a second model comes in handy. My workflow usually has me using Claude, Codex, and Gemini, so them being able to coordinate like this outside of a tmux dependency has saved not only tokens, but time, and drastically improved the quality of my projects simply because one model won't catch everything. But in my experience, two might. The quality output concept is similar to what happens when you use OpenRouter's Fusion mode or Aider's Architect mode, just with extra steps and a lot more control in between. Functionally equivalent to those, but the models stay separate entities and operate more like a team than a single model.


What it is

Tether is a CLI-to-CLI messaging layer backed by a local SQLite database. Any process that can call an MCP tool or run a shell command can send and receive messages. It does not care whether the process is an AI model, a build script, or a human at a terminal.

Messages are stored as content-addressed handles — a BLAKE3 hash of the payload. Send the handle, not the payload. The receiver resolves it locally. No serialization mismatches, no "what did you actually send me."

For cross-machine workflows, the relay stores encrypted ciphertext envelopes. The relay never sees plaintext.


The v2 multiplexer — no tmux required

The biggest change in v2 is the native terminal multiplexer. On KDE/Konsole, Tether talks to each open tab over D-Bus and injects messages directly. No tmux sessions. No port numbers. No running a wrapper process per agent.

The delivery chain looks like this:

tether_send → fire ping → POST /api/konsole/deliver → qdbus6 sendText → agent wakes

The terminal receives a short durable-handle notice; the agent resolves the message from the shared SQLite mailbox, then processes and replies. Keeping the payload out of terminal injection avoids truncation and preserves a reviewable record of what was actually sent.

The Terminals tab lists every open Konsole tab, guesses which agent is running in it based on the process cmdline, and lets you bind it in one click. After that, tmail delivery is automatic.

Kilo receiving a tmail injection

Kilo's tab receives the injected message, resolves the handle, checks its inbox, and replies — all without any human relay.

Draft-safe wake behavior

Tether only submits a Konsole notice when it positively recognises an empty agent composer. If a recognised composer already contains text — or its state cannot be established — Tether inserts the notice without Enter. This preserves the draft Matt is writing; he can move the notice down with Shift+Enter, continue the draft, and decide when to submit it. The message is durable either way, so terminal delivery is a convenience signal rather than a source of truth.


Setup for seamless autoping

Two things need to be enabled for fully autonomous agent wake-up. Neither is on by default.

1. Enable the Konsole D-Bus security API

Konsole blocks D-Bus input injection by default. Go to:

Konsole → Settings → Configure Konsole → General
→ check "Enable the security sensitive parts of the DBus API"

This persists across reboots. It's a one-time change.

2. Run your agents in full-auto (yolo) mode

Injection fires the message into the agent's input and submits it. For the agent to process it without stopping to ask for confirmation, it needs to be running without approval prompts:

  • Codex CLI:codex --full-auto or codex --approval-mode full-auto
  • Claude Code:claude --dangerously-skip-permissions (or set in your project's permission config)
  • Kilo / other CLIs: check their --help for an equivalent auto-approve flag

Without this, the injection lands but the agent will pause and wait for you to hit Enter on a permission prompt — which defeats the point.


Register Node — agent discovery

The Register Node tab discovers running agents automatically. No port numbers required.


Install

git clone https://github.com/latentcollapse/tether.git
cd tether
pip install .

The built dashboard is committed in src/dashboard/dist. You don't need Node.js unless you're modifying the frontend.

MCP Setup

{
"mcpServers": {
"tether": {
"command": "tether-mcp",
"args": [],
"env": {
"TETHER_DB": "/path/to/shared/postoffice.db"
}
}
}
}

Every agent on the same machine should point to the same TETHER_DB. That's how they share a message bus.

Default locations if you omit TETHER_DB:

  • Linux/Mac: ~/.local/share/tether/postoffice.db
  • Windows: %APPDATA%\tether\postoffice.db

Starting the dashboard

tether

Opens at http://localhost:3000. If port 3000 is busy it picks the next free port.

Sending a message

From an MCP client (Claude Code, Codex CLI, etc.):

tether_send to="codex" subject="task" text="Refactor the verifier, see C-042 for context"
tether_inbox for_agent="codex"
tether_receive handle="h&l_messages_..."

From the shell:

tether send --from-agent claude codex "task""Refactor the verifier, see C-042 for context"

How handles work

payload → collapse → handle → send handle → receive → resolve

Handles are typed and content-addressed:

  • h&l_messages_{hash} — messages
  • h&l_inline_{hash} — small inline payloads
  • h&l_blob_{hash} — binary blobs
  • h&l_tree_{hash} — lists of child handles

Identical content always produces the same handle. You can send the same artifact to ten agents and it gets stored once.


MCP tools

ToolPurpose
tether_sendSend a message to another agent
tether_inboxList unread messages for an agent
tether_receiveRead a message by handle
tether_closeClose a message thread
tether_collapseCollapse a payload into a handle
tether_resolveResolve a handle back to its payload
tether_collapse_blobStore binary data as a blob handle
tether_resolve_blobResolve a blob handle
tether_collapse_treeStore a list of handles as a tree
tether_resolve_treeResolve a tree handle

Non-KDE terminals

If you're not on KDE/Konsole, tether mux wraps any CLI in a PTY that Tether owns:

tether mux --agent codex -- codex --full-auto resume

Tether owns the PTY, monitors for an idle prompt, and injects when the agent is ready. Same delivery guarantee, different ownership model.

Terminal-agnostic drivers (kitty, WezTerm, Windows Terminal) are on the roadmap. A Docker image and Nix flake for deployment are planned.


Cross-machine delivery

Connect — Direct Node Handshake

Add a relay and Tether reaches across machines. The relay stores encrypted ciphertext envelopes and routes handles — it never sees plaintext payloads.

# self-hosted relay
python -m uvicorn relay.main:app --host 0.0.0.0 --port 8000
# or Docker
docker-compose up --build

From your phone, tablet, or any browser: send a tmail to the relay, it gets pushed to the agent on your dev machine. No OAuth, no cloud vendor, no SSH tunnel.


Settings — themes and autonomous routing

Ember, Void, Cyberpunk, Synthwave, and more. Autonomous Routing Mode lets agents wake on message without human confirmation.


Security model

  • Local data never leaves the machine unless you configure a relay.
  • Relay sees ciphertext envelopes and routing metadata. Not plaintext.
  • Encryption and decryption happen on the client side only.
  • AGPL source — the transport and storage claims are auditable.

The D-Bus injection path requires explicit opt-in (the Konsole security setting above). Tether does not enable it silently.


Changelog

Full notes in changelog/.

VersionHighlights
v2.1.4Bug fixes: atomic self-healing ticket-ID issuance; --db honored across all commands; delivery/reap hardening; XDG DB migration; internal runtime split into board/tasks/sqlite mixins
v2.1Native D-Bus multiplexer, Terminals tab, no-tmux delivery, agent registry, presence heartbeat, routes API, real-time feed WebSocket
v1.8TetherLite storage/runtime, relay core, tier enforcement, encrypted envelopes
v1.7Ping daemon, autoping, local delivery tooling
v1.6Ping registration and push notifications
v1.5Shared task board
v1.4Tags, read state, ergonomic CLI improvements
v1.0–v1.3Base handle/runtime model

License

  • Source: AGPL v3
  • Hosted relay service terms: separate from source distribution

About

This is a multi-CLI coordination layer with native multiplexing. Discussions and contributions welcome. Throw it a star if you like the project :)

Resources

Stars

29 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

Tether

Major redesign of the dashboard Native multiplexing. Tmux no longer required. Constructed in such a way that Claude could easily set it up for you. Not everything may work correctly. Consider this beta, and an opportunity for contribution. App will flesh out and smooth out more in the coming weeks. Hot reload function added for dashboard, mapped to ctrl+shift+R

A content-addressed messaging layer for multi-agent development workflows. Send a message from Claude Code to Codex CLI. Wake Gemini from a tmail. Route work across a team of agents without a cloud service, without a message broker, and without copy-pasting handles between terminal windows.

Tether Dashboard — Network Graph

Five agents online, message routes as edges, real-time feed streaming on the right.


Why this exists

A lot of people are fine with just Claude or just Codex, and that's usually enough, but sometimes when you need differing perspectives, red-teaming, QA/QC, and someone to poke holes in the design to strengthen it, a second model comes in handy. My workflow usually has me using Claude, Codex, and Gemini, so them being able to coordinate like this outside of a tmux dependency has saved not only tokens, but time, and drastically improved the quality of my projects simply because one model won't catch everything. But in my experience, two might. The quality output concept is similar to what happens when you use OpenRouter's Fusion mode or Aider's Architect mode, just with extra steps and a lot more control in between. Functionally equivalent to those, but the models stay separate entities and operate more like a team than a single model.


What it is

Tether is a CLI-to-CLI messaging layer backed by a local SQLite database. Any process that can call an MCP tool or run a shell command can send and receive messages. It does not care whether the process is an AI model, a build script, or a human at a terminal.

Messages are stored as content-addressed handles — a BLAKE3 hash of the payload. Send the handle, not the payload. The receiver resolves it locally. No serialization mismatches, no "what did you actually send me."

For cross-machine workflows, the relay stores encrypted ciphertext envelopes. The relay never sees plaintext.


The v2 multiplexer — no tmux required

The biggest change in v2 is the native terminal multiplexer. On KDE/Konsole, Tether talks to each open tab over D-Bus and injects messages directly. No tmux sessions. No port numbers. No running a wrapper process per agent.

The delivery chain looks like this:

tether_send → fire ping → POST /api/konsole/deliver → qdbus6 sendText → agent wakes

The terminal receives a short durable-handle notice; the agent resolves the message from the shared SQLite mailbox, then processes and replies. Keeping the payload out of terminal injection avoids truncation and preserves a reviewable record of what was actually sent.

The Terminals tab lists every open Konsole tab, guesses which agent is running in it based on the process cmdline, and lets you bind it in one click. After that, tmail delivery is automatic.

Kilo receiving a tmail injection

Kilo's tab receives the injected message, resolves the handle, checks its inbox, and replies — all without any human relay.

Draft-safe wake behavior

Tether only submits a Konsole notice when it positively recognises an empty agent composer. If a recognised composer already contains text — or its state cannot be established — Tether inserts the notice without Enter. This preserves the draft Matt is writing; he can move the notice down with Shift+Enter, continue the draft, and decide when to submit it. The message is durable either way, so terminal delivery is a convenience signal rather than a source of truth.


Setup for seamless autoping

Two things need to be enabled for fully autonomous agent wake-up. Neither is on by default.

1. Enable the Konsole D-Bus security API

Konsole blocks D-Bus input injection by default. Go to:

Konsole → Settings → Configure Konsole → General
→ check "Enable the security sensitive parts of the DBus API"

This persists across reboots. It's a one-time change.

2. Run your agents in full-auto (yolo) mode

Injection fires the message into the agent's input and submits it. For the agent to process it without stopping to ask for confirmation, it needs to be running without approval prompts:

  • Codex CLI:codex --full-auto or codex --approval-mode full-auto
  • Claude Code:claude --dangerously-skip-permissions (or set in your project's permission config)
  • Kilo / other CLIs: check their --help for an equivalent auto-approve flag

Without this, the injection lands but the agent will pause and wait for you to hit Enter on a permission prompt — which defeats the point.


Register Node — agent discovery

The Register Node tab discovers running agents automatically. No port numbers required.


Install

git clone https://github.com/latentcollapse/tether.git
cd tether
pip install .

The built dashboard is committed in src/dashboard/dist. You don't need Node.js unless you're modifying the frontend.

MCP Setup

{
"mcpServers": {
"tether": {
"command": "tether-mcp",
"args": [],
"env": {
"TETHER_DB": "/path/to/shared/postoffice.db"
}
}
}
}

Every agent on the same machine should point to the same TETHER_DB. That's how they share a message bus.

Default locations if you omit TETHER_DB:

  • Linux/Mac: ~/.local/share/tether/postoffice.db
  • Windows: %APPDATA%\tether\postoffice.db

Starting the dashboard

tether

Opens at http://localhost:3000. If port 3000 is busy it picks the next free port.

Sending a message

From an MCP client (Claude Code, Codex CLI, etc.):

tether_send to="codex" subject="task" text="Refactor the verifier, see C-042 for context"
tether_inbox for_agent="codex"
tether_receive handle="h&l_messages_..."

From the shell:

tether send --from-agent claude codex "task""Refactor the verifier, see C-042 for context"

How handles work

payload → collapse → handle → send handle → receive → resolve

Handles are typed and content-addressed:

  • h&l_messages_{hash} — messages
  • h&l_inline_{hash} — small inline payloads
  • h&l_blob_{hash} — binary blobs
  • h&l_tree_{hash} — lists of child handles

Identical content always produces the same handle. You can send the same artifact to ten agents and it gets stored once.


MCP tools

ToolPurpose
tether_sendSend a message to another agent
tether_inboxList unread messages for an agent
tether_receiveRead a message by handle
tether_closeClose a message thread
tether_collapseCollapse a payload into a handle
tether_resolveResolve a handle back to its payload
tether_collapse_blobStore binary data as a blob handle
tether_resolve_blobResolve a blob handle
tether_collapse_treeStore a list of handles as a tree
tether_resolve_treeResolve a tree handle

Non-KDE terminals

If you're not on KDE/Konsole, tether mux wraps any CLI in a PTY that Tether owns:

tether mux --agent codex -- codex --full-auto resume

Tether owns the PTY, monitors for an idle prompt, and injects when the agent is ready. Same delivery guarantee, different ownership model.

Terminal-agnostic drivers (kitty, WezTerm, Windows Terminal) are on the roadmap. A Docker image and Nix flake for deployment are planned.


Cross-machine delivery

Connect — Direct Node Handshake

Add a relay and Tether reaches across machines. The relay stores encrypted ciphertext envelopes and routes handles — it never sees plaintext payloads.

# self-hosted relay
python -m uvicorn relay.main:app --host 0.0.0.0 --port 8000
# or Docker
docker-compose up --build

From your phone, tablet, or any browser: send a tmail to the relay, it gets pushed to the agent on your dev machine. No OAuth, no cloud vendor, no SSH tunnel.


Settings — themes and autonomous routing

Ember, Void, Cyberpunk, Synthwave, and more. Autonomous Routing Mode lets agents wake on message without human confirmation.


Security model

  • Local data never leaves the machine unless you configure a relay.
  • Relay sees ciphertext envelopes and routing metadata. Not plaintext.
  • Encryption and decryption happen on the client side only.
  • AGPL source — the transport and storage claims are auditable.

The D-Bus injection path requires explicit opt-in (the Konsole security setting above). Tether does not enable it silently.


Changelog

Full notes in changelog/.

VersionHighlights
v2.1.4Bug fixes: atomic self-healing ticket-ID issuance; --db honored across all commands; delivery/reap hardening; XDG DB migration; internal runtime split into board/tasks/sqlite mixins
v2.1Native D-Bus multiplexer, Terminals tab, no-tmux delivery, agent registry, presence heartbeat, routes API, real-time feed WebSocket
v1.8TetherLite storage/runtime, relay core, tier enforcement, encrypted envelopes
v1.7Ping daemon, autoping, local delivery tooling
v1.6Ping registration and push notifications
v1.5Shared task board
v1.4Tags, read state, ergonomic CLI improvements
v1.0–v1.3Base handle/runtime model

License

  • Source: AGPL v3
  • Hosted relay service terms: separate from source distribution

About

This is a multi-CLI coordination layer with native multiplexing. Discussions and contributions welcome. Throw it a star if you like the project :)

Resources

Stars

29 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Tether

Major redesign of the dashboard Native multiplexing. Tmux no longer required. Constructed in such a way that Claude could easily set it up for you. Not everything may work correctly. Consider this beta, and an opportunity for contribution. App will flesh out and smooth out more in the coming weeks. Hot reload function added for dashboard, mapped to ctrl+shift+R

A content-addressed messaging layer for multi-agent development workflows. Send a message from Claude Code to Codex CLI. Wake Gemini from a tmail. Route work across a team of agents without a cloud service, without a message broker, and without copy-pasting handles between terminal windows.

Tether Dashboard — Network Graph

Five agents online, message routes as edges, real-time feed streaming on the right.


Why this exists

A lot of people are fine with just Claude or just Codex, and that's usually enough, but sometimes when you need differing perspectives, red-teaming, QA/QC, and someone to poke holes in the design to strengthen it, a second model comes in handy. My workflow usually has me using Claude, Codex, and Gemini, so them being able to coordinate like this outside of a tmux dependency has saved not only tokens, but time, and drastically improved the quality of my projects simply because one model won't catch everything. But in my experience, two might. The quality output concept is similar to what happens when you use OpenRouter's Fusion mode or Aider's Architect mode, just with extra steps and a lot more control in between. Functionally equivalent to those, but the models stay separate entities and operate more like a team than a single model.


What it is

Tether is a CLI-to-CLI messaging layer backed by a local SQLite database. Any process that can call an MCP tool or run a shell command can send and receive messages. It does not care whether the process is an AI model, a build script, or a human at a terminal.

Messages are stored as content-addressed handles — a BLAKE3 hash of the payload. Send the handle, not the payload. The receiver resolves it locally. No serialization mismatches, no "what did you actually send me."

For cross-machine workflows, the relay stores encrypted ciphertext envelopes. The relay never sees plaintext.


The v2 multiplexer — no tmux required

The biggest change in v2 is the native terminal multiplexer. On KDE/Konsole, Tether talks to each open tab over D-Bus and injects messages directly. No tmux sessions. No port numbers. No running a wrapper process per agent.

The delivery chain looks like this:

tether_send → fire ping → POST /api/konsole/deliver → qdbus6 sendText → agent wakes

The terminal receives a short durable-handle notice; the agent resolves the message from the shared SQLite mailbox, then processes and replies. Keeping the payload out of terminal injection avoids truncation and preserves a reviewable record of what was actually sent.

The Terminals tab lists every open Konsole tab, guesses which agent is running in it based on the process cmdline, and lets you bind it in one click. After that, tmail delivery is automatic.

Kilo receiving a tmail injection

Kilo's tab receives the injected message, resolves the handle, checks its inbox, and replies — all without any human relay.

Draft-safe wake behavior

Tether only submits a Konsole notice when it positively recognises an empty agent composer. If a recognised composer already contains text — or its state cannot be established — Tether inserts the notice without Enter. This preserves the draft Matt is writing; he can move the notice down with Shift+Enter, continue the draft, and decide when to submit it. The message is durable either way, so terminal delivery is a convenience signal rather than a source of truth.


Setup for seamless autoping

Two things need to be enabled for fully autonomous agent wake-up. Neither is on by default.

1. Enable the Konsole D-Bus security API

Konsole blocks D-Bus input injection by default. Go to:

Konsole → Settings → Configure Konsole → General
→ check "Enable the security sensitive parts of the DBus API"

This persists across reboots. It's a one-time change.

2. Run your agents in full-auto (yolo) mode

Injection fires the message into the agent's input and submits it. For the agent to process it without stopping to ask for confirmation, it needs to be running without approval prompts:

  • Codex CLI:codex --full-auto or codex --approval-mode full-auto
  • Claude Code:claude --dangerously-skip-permissions (or set in your project's permission config)
  • Kilo / other CLIs: check their --help for an equivalent auto-approve flag

Without this, the injection lands but the agent will pause and wait for you to hit Enter on a permission prompt — which defeats the point.


Register Node — agent discovery

The Register Node tab discovers running agents automatically. No port numbers required.


Install

git clone https://github.com/latentcollapse/tether.git
cd tether
pip install .

The built dashboard is committed in src/dashboard/dist. You don't need Node.js unless you're modifying the frontend.

MCP Setup

{
"mcpServers": {
"tether": {
"command": "tether-mcp",
"args": [],
"env": {
"TETHER_DB": "/path/to/shared/postoffice.db"
}
}
}
}

Every agent on the same machine should point to the same TETHER_DB. That's how they share a message bus.

Default locations if you omit TETHER_DB:

  • Linux/Mac: ~/.local/share/tether/postoffice.db
  • Windows: %APPDATA%\tether\postoffice.db

Starting the dashboard

tether

Opens at http://localhost:3000. If port 3000 is busy it picks the next free port.

Sending a message

From an MCP client (Claude Code, Codex CLI, etc.):

tether_send to="codex" subject="task" text="Refactor the verifier, see C-042 for context"
tether_inbox for_agent="codex"
tether_receive handle="h&l_messages_..."

From the shell:

tether send --from-agent claude codex "task""Refactor the verifier, see C-042 for context"

How handles work

payload → collapse → handle → send handle → receive → resolve

Handles are typed and content-addressed:

  • h&l_messages_{hash} — messages
  • h&l_inline_{hash} — small inline payloads
  • h&l_blob_{hash} — binary blobs
  • h&l_tree_{hash} — lists of child handles

Identical content always produces the same handle. You can send the same artifact to ten agents and it gets stored once.


MCP tools

ToolPurpose
tether_sendSend a message to another agent
tether_inboxList unread messages for an agent
tether_receiveRead a message by handle
tether_closeClose a message thread
tether_collapseCollapse a payload into a handle
tether_resolveResolve a handle back to its payload
tether_collapse_blobStore binary data as a blob handle
tether_resolve_blobResolve a blob handle
tether_collapse_treeStore a list of handles as a tree
tether_resolve_treeResolve a tree handle

Non-KDE terminals

If you're not on KDE/Konsole, tether mux wraps any CLI in a PTY that Tether owns:

tether mux --agent codex -- codex --full-auto resume

Tether owns the PTY, monitors for an idle prompt, and injects when the agent is ready. Same delivery guarantee, different ownership model.

Terminal-agnostic drivers (kitty, WezTerm, Windows Terminal) are on the roadmap. A Docker image and Nix flake for deployment are planned.


Cross-machine delivery

Connect — Direct Node Handshake

Add a relay and Tether reaches across machines. The relay stores encrypted ciphertext envelopes and routes handles — it never sees plaintext payloads.

# self-hosted relay
python -m uvicorn relay.main:app --host 0.0.0.0 --port 8000
# or Docker
docker-compose up --build

From your phone, tablet, or any browser: send a tmail to the relay, it gets pushed to the agent on your dev machine. No OAuth, no cloud vendor, no SSH tunnel.


Settings — themes and autonomous routing

Ember, Void, Cyberpunk, Synthwave, and more. Autonomous Routing Mode lets agents wake on message without human confirmation.


Security model

  • Local data never leaves the machine unless you configure a relay.
  • Relay sees ciphertext envelopes and routing metadata. Not plaintext.
  • Encryption and decryption happen on the client side only.
  • AGPL source — the transport and storage claims are auditable.

The D-Bus injection path requires explicit opt-in (the Konsole security setting above). Tether does not enable it silently.


Changelog

Full notes in changelog/.

VersionHighlights
v2.1.4Bug fixes: atomic self-healing ticket-ID issuance; --db honored across all commands; delivery/reap hardening; XDG DB migration; internal runtime split into board/tasks/sqlite mixins
v2.1Native D-Bus multiplexer, Terminals tab, no-tmux delivery, agent registry, presence heartbeat, routes API, real-time feed WebSocket
v1.8TetherLite storage/runtime, relay core, tier enforcement, encrypted envelopes
v1.7Ping daemon, autoping, local delivery tooling
v1.6Ping registration and push notifications
v1.5Shared task board
v1.4Tags, read state, ergonomic CLI improvements
v1.0–v1.3Base handle/runtime model

License

  • Source: AGPL v3
  • Hosted relay service terms: separate from source distribution

About

This is a multi-CLI coordination layer with native multiplexing. Discussions and contributions welcome. Throw it a star if you like the project :)

Resources

Stars

29 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Tether

Major redesign of the dashboard Native multiplexing. Tmux no longer required. Constructed in such a way that Claude could easily set it up for you. Not everything may work correctly. Consider this beta, and an opportunity for contribution. App will flesh out and smooth out more in the coming weeks. Hot reload function added for dashboard, mapped to ctrl+shift+R

A content-addressed messaging layer for multi-agent development workflows. Send a message from Claude Code to Codex CLI. Wake Gemini from a tmail. Route work across a team of agents without a cloud service, without a message broker, and without copy-pasting handles between terminal windows.

Tether Dashboard — Network Graph

Five agents online, message routes as edges, real-time feed streaming on the right.


Why this exists

A lot of people are fine with just Claude or just Codex, and that's usually enough, but sometimes when you need differing perspectives, red-teaming, QA/QC, and someone to poke holes in the design to strengthen it, a second model comes in handy. My workflow usually has me using Claude, Codex, and Gemini, so them being able to coordinate like this outside of a tmux dependency has saved not only tokens, but time, and drastically improved the quality of my projects simply because one model won't catch everything. But in my experience, two might. The quality output concept is similar to what happens when you use OpenRouter's Fusion mode or Aider's Architect mode, just with extra steps and a lot more control in between. Functionally equivalent to those, but the models stay separate entities and operate more like a team than a single model.


What it is

Tether is a CLI-to-CLI messaging layer backed by a local SQLite database. Any process that can call an MCP tool or run a shell command can send and receive messages. It does not care whether the process is an AI model, a build script, or a human at a terminal.

Messages are stored as content-addressed handles — a BLAKE3 hash of the payload. Send the handle, not the payload. The receiver resolves it locally. No serialization mismatches, no "what did you actually send me."

For cross-machine workflows, the relay stores encrypted ciphertext envelopes. The relay never sees plaintext.


The v2 multiplexer — no tmux required

The biggest change in v2 is the native terminal multiplexer. On KDE/Konsole, Tether talks to each open tab over D-Bus and injects messages directly. No tmux sessions. No port numbers. No running a wrapper process per agent.

The delivery chain looks like this:

tether_send → fire ping → POST /api/konsole/deliver → qdbus6 sendText → agent wakes

The terminal receives a short durable-handle notice; the agent resolves the message from the shared SQLite mailbox, then processes and replies. Keeping the payload out of terminal injection avoids truncation and preserves a reviewable record of what was actually sent.

The Terminals tab lists every open Konsole tab, guesses which agent is running in it based on the process cmdline, and lets you bind it in one click. After that, tmail delivery is automatic.

Kilo receiving a tmail injection

Kilo's tab receives the injected message, resolves the handle, checks its inbox, and replies — all without any human relay.

Draft-safe wake behavior

Tether only submits a Konsole notice when it positively recognises an empty agent composer. If a recognised composer already contains text — or its state cannot be established — Tether inserts the notice without Enter. This preserves the draft Matt is writing; he can move the notice down with Shift+Enter, continue the draft, and decide when to submit it. The message is durable either way, so terminal delivery is a convenience signal rather than a source of truth.


Setup for seamless autoping

Two things need to be enabled for fully autonomous agent wake-up. Neither is on by default.

1. Enable the Konsole D-Bus security API

Konsole blocks D-Bus input injection by default. Go to:

Konsole → Settings → Configure Konsole → General
→ check "Enable the security sensitive parts of the DBus API"

This persists across reboots. It's a one-time change.

2. Run your agents in full-auto (yolo) mode

Injection fires the message into the agent's input and submits it. For the agent to process it without stopping to ask for confirmation, it needs to be running without approval prompts:

  • Codex CLI:codex --full-auto or codex --approval-mode full-auto
  • Claude Code:claude --dangerously-skip-permissions (or set in your project's permission config)
  • Kilo / other CLIs: check their --help for an equivalent auto-approve flag

Without this, the injection lands but the agent will pause and wait for you to hit Enter on a permission prompt — which defeats the point.


Register Node — agent discovery

The Register Node tab discovers running agents automatically. No port numbers required.


Install

git clone https://github.com/latentcollapse/tether.git
cd tether
pip install .

The built dashboard is committed in src/dashboard/dist. You don't need Node.js unless you're modifying the frontend.

MCP Setup

{
"mcpServers": {
"tether": {
"command": "tether-mcp",
"args": [],
"env": {
"TETHER_DB": "/path/to/shared/postoffice.db"
}
}
}
}

Every agent on the same machine should point to the same TETHER_DB. That's how they share a message bus.

Default locations if you omit TETHER_DB:

  • Linux/Mac: ~/.local/share/tether/postoffice.db
  • Windows: %APPDATA%\tether\postoffice.db

Starting the dashboard

tether

Opens at http://localhost:3000. If port 3000 is busy it picks the next free port.

Sending a message

From an MCP client (Claude Code, Codex CLI, etc.):

tether_send to="codex" subject="task" text="Refactor the verifier, see C-042 for context"
tether_inbox for_agent="codex"
tether_receive handle="h&l_messages_..."

From the shell:

tether send --from-agent claude codex "task""Refactor the verifier, see C-042 for context"

How handles work

payload → collapse → handle → send handle → receive → resolve

Handles are typed and content-addressed:

  • h&l_messages_{hash} — messages
  • h&l_inline_{hash} — small inline payloads
  • h&l_blob_{hash} — binary blobs
  • h&l_tree_{hash} — lists of child handles

Identical content always produces the same handle. You can send the same artifact to ten agents and it gets stored once.


MCP tools

ToolPurpose
tether_sendSend a message to another agent
tether_inboxList unread messages for an agent
tether_receiveRead a message by handle
tether_closeClose a message thread
tether_collapseCollapse a payload into a handle
tether_resolveResolve a handle back to its payload
tether_collapse_blobStore binary data as a blob handle
tether_resolve_blobResolve a blob handle
tether_collapse_treeStore a list of handles as a tree
tether_resolve_treeResolve a tree handle

Non-KDE terminals

If you're not on KDE/Konsole, tether mux wraps any CLI in a PTY that Tether owns:

tether mux --agent codex -- codex --full-auto resume

Tether owns the PTY, monitors for an idle prompt, and injects when the agent is ready. Same delivery guarantee, different ownership model.

Terminal-agnostic drivers (kitty, WezTerm, Windows Terminal) are on the roadmap. A Docker image and Nix flake for deployment are planned.


Cross-machine delivery

Connect — Direct Node Handshake

Add a relay and Tether reaches across machines. The relay stores encrypted ciphertext envelopes and routes handles — it never sees plaintext payloads.

# self-hosted relay
python -m uvicorn relay.main:app --host 0.0.0.0 --port 8000
# or Docker
docker-compose up --build

From your phone, tablet, or any browser: send a tmail to the relay, it gets pushed to the agent on your dev machine. No OAuth, no cloud vendor, no SSH tunnel.


Settings — themes and autonomous routing

Ember, Void, Cyberpunk, Synthwave, and more. Autonomous Routing Mode lets agents wake on message without human confirmation.


Security model

  • Local data never leaves the machine unless you configure a relay.
  • Relay sees ciphertext envelopes and routing metadata. Not plaintext.
  • Encryption and decryption happen on the client side only.
  • AGPL source — the transport and storage claims are auditable.

The D-Bus injection path requires explicit opt-in (the Konsole security setting above). Tether does not enable it silently.


Changelog

Full notes in changelog/.

VersionHighlights
v2.1.4Bug fixes: atomic self-healing ticket-ID issuance; --db honored across all commands; delivery/reap hardening; XDG DB migration; internal runtime split into board/tasks/sqlite mixins
v2.1Native D-Bus multiplexer, Terminals tab, no-tmux delivery, agent registry, presence heartbeat, routes API, real-time feed WebSocket
v1.8TetherLite storage/runtime, relay core, tier enforcement, encrypted envelopes
v1.7Ping daemon, autoping, local delivery tooling
v1.6Ping registration and push notifications
v1.5Shared task board
v1.4Tags, read state, ergonomic CLI improvements
v1.0–v1.3Base handle/runtime model

License

  • Source: AGPL v3
  • Hosted relay service terms: separate from source distribution

About

This is a multi-CLI coordination layer with native multiplexing. Discussions and contributions welcome. Throw it a star if you like the project :)

Resources

Stars

29 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

Tether

Major redesign of the dashboard Native multiplexing. Tmux no longer required. Constructed in such a way that Claude could easily set it up for you. Not everything may work correctly. Consider this beta, and an opportunity for contribution. App will flesh out and smooth out more in the coming weeks. Hot reload function added for dashboard, mapped to ctrl+shift+R

A content-addressed messaging layer for multi-agent development workflows. Send a message from Claude Code to Codex CLI. Wake Gemini from a tmail. Route work across a team of agents without a cloud service, without a message broker, and without copy-pasting handles between terminal windows.

Tether Dashboard — Network Graph

Five agents online, message routes as edges, real-time feed streaming on the right.


Why this exists

A lot of people are fine with just Claude or just Codex, and that's usually enough, but sometimes when you need differing perspectives, red-teaming, QA/QC, and someone to poke holes in the design to strengthen it, a second model comes in handy. My workflow usually has me using Claude, Codex, and Gemini, so them being able to coordinate like this outside of a tmux dependency has saved not only tokens, but time, and drastically improved the quality of my projects simply because one model won't catch everything. But in my experience, two might. The quality output concept is similar to what happens when you use OpenRouter's Fusion mode or Aider's Architect mode, just with extra steps and a lot more control in between. Functionally equivalent to those, but the models stay separate entities and operate more like a team than a single model.


What it is

Tether is a CLI-to-CLI messaging layer backed by a local SQLite database. Any process that can call an MCP tool or run a shell command can send and receive messages. It does not care whether the process is an AI model, a build script, or a human at a terminal.

Messages are stored as content-addressed handles — a BLAKE3 hash of the payload. Send the handle, not the payload. The receiver resolves it locally. No serialization mismatches, no "what did you actually send me."

For cross-machine workflows, the relay stores encrypted ciphertext envelopes. The relay never sees plaintext.


The v2 multiplexer — no tmux required

The biggest change in v2 is the native terminal multiplexer. On KDE/Konsole, Tether talks to each open tab over D-Bus and injects messages directly. No tmux sessions. No port numbers. No running a wrapper process per agent.

The delivery chain looks like this:

tether_send → fire ping → POST /api/konsole/deliver → qdbus6 sendText → agent wakes

The terminal receives a short durable-handle notice; the agent resolves the message from the shared SQLite mailbox, then processes and replies. Keeping the payload out of terminal injection avoids truncation and preserves a reviewable record of what was actually sent.

The Terminals tab lists every open Konsole tab, guesses which agent is running in it based on the process cmdline, and lets you bind it in one click. After that, tmail delivery is automatic.

Kilo receiving a tmail injection

Kilo's tab receives the injected message, resolves the handle, checks its inbox, and replies — all without any human relay.

Draft-safe wake behavior

Tether only submits a Konsole notice when it positively recognises an empty agent composer. If a recognised composer already contains text — or its state cannot be established — Tether inserts the notice without Enter. This preserves the draft Matt is writing; he can move the notice down with Shift+Enter, continue the draft, and decide when to submit it. The message is durable either way, so terminal delivery is a convenience signal rather than a source of truth.


Setup for seamless autoping

Two things need to be enabled for fully autonomous agent wake-up. Neither is on by default.

1. Enable the Konsole D-Bus security API

Konsole blocks D-Bus input injection by default. Go to:

Konsole → Settings → Configure Konsole → General
→ check "Enable the security sensitive parts of the DBus API"

This persists across reboots. It's a one-time change.

2. Run your agents in full-auto (yolo) mode

Injection fires the message into the agent's input and submits it. For the agent to process it without stopping to ask for confirmation, it needs to be running without approval prompts:

  • Codex CLI:codex --full-auto or codex --approval-mode full-auto
  • Claude Code:claude --dangerously-skip-permissions (or set in your project's permission config)
  • Kilo / other CLIs: check their --help for an equivalent auto-approve flag

Without this, the injection lands but the agent will pause and wait for you to hit Enter on a permission prompt — which defeats the point.


Register Node — agent discovery

The Register Node tab discovers running agents automatically. No port numbers required.


Install

git clone https://github.com/latentcollapse/tether.git
cd tether
pip install .

The built dashboard is committed in src/dashboard/dist. You don't need Node.js unless you're modifying the frontend.

MCP Setup

{
"mcpServers": {
"tether": {
"command": "tether-mcp",
"args": [],
"env": {
"TETHER_DB": "/path/to/shared/postoffice.db"
}
}
}
}

Every agent on the same machine should point to the same TETHER_DB. That's how they share a message bus.

Default locations if you omit TETHER_DB:

  • Linux/Mac: ~/.local/share/tether/postoffice.db
  • Windows: %APPDATA%\tether\postoffice.db

Starting the dashboard

tether

Opens at http://localhost:3000. If port 3000 is busy it picks the next free port.

Sending a message

From an MCP client (Claude Code, Codex CLI, etc.):

tether_send to="codex" subject="task" text="Refactor the verifier, see C-042 for context"
tether_inbox for_agent="codex"
tether_receive handle="h&l_messages_..."

From the shell:

tether send --from-agent claude codex "task""Refactor the verifier, see C-042 for context"

How handles work

payload → collapse → handle → send handle → receive → resolve

Handles are typed and content-addressed:

  • h&l_messages_{hash} — messages
  • h&l_inline_{hash} — small inline payloads
  • h&l_blob_{hash} — binary blobs
  • h&l_tree_{hash} — lists of child handles

Identical content always produces the same handle. You can send the same artifact to ten agents and it gets stored once.


MCP tools

ToolPurpose
tether_sendSend a message to another agent
tether_inboxList unread messages for an agent
tether_receiveRead a message by handle
tether_closeClose a message thread
tether_collapseCollapse a payload into a handle
tether_resolveResolve a handle back to its payload
tether_collapse_blobStore binary data as a blob handle
tether_resolve_blobResolve a blob handle
tether_collapse_treeStore a list of handles as a tree
tether_resolve_treeResolve a tree handle

Non-KDE terminals

If you're not on KDE/Konsole, tether mux wraps any CLI in a PTY that Tether owns:

tether mux --agent codex -- codex --full-auto resume

Tether owns the PTY, monitors for an idle prompt, and injects when the agent is ready. Same delivery guarantee, different ownership model.

Terminal-agnostic drivers (kitty, WezTerm, Windows Terminal) are on the roadmap. A Docker image and Nix flake for deployment are planned.


Cross-machine delivery

Connect — Direct Node Handshake

Add a relay and Tether reaches across machines. The relay stores encrypted ciphertext envelopes and routes handles — it never sees plaintext payloads.

# self-hosted relay
python -m uvicorn relay.main:app --host 0.0.0.0 --port 8000
# or Docker
docker-compose up --build

From your phone, tablet, or any browser: send a tmail to the relay, it gets pushed to the agent on your dev machine. No OAuth, no cloud vendor, no SSH tunnel.


Settings — themes and autonomous routing

Ember, Void, Cyberpunk, Synthwave, and more. Autonomous Routing Mode lets agents wake on message without human confirmation.


Security model

  • Local data never leaves the machine unless you configure a relay.
  • Relay sees ciphertext envelopes and routing metadata. Not plaintext.
  • Encryption and decryption happen on the client side only.
  • AGPL source — the transport and storage claims are auditable.

The D-Bus injection path requires explicit opt-in (the Konsole security setting above). Tether does not enable it silently.


Changelog

Full notes in changelog/.

VersionHighlights
v2.1.4Bug fixes: atomic self-healing ticket-ID issuance; --db honored across all commands; delivery/reap hardening; XDG DB migration; internal runtime split into board/tasks/sqlite mixins
v2.1Native D-Bus multiplexer, Terminals tab, no-tmux delivery, agent registry, presence heartbeat, routes API, real-time feed WebSocket
v1.8TetherLite storage/runtime, relay core, tier enforcement, encrypted envelopes
v1.7Ping daemon, autoping, local delivery tooling
v1.6Ping registration and push notifications
v1.5Shared task board
v1.4Tags, read state, ergonomic CLI improvements
v1.0–v1.3Base handle/runtime model

License

  • Source: AGPL v3
  • Hosted relay service terms: separate from source distribution

About

This is a multi-CLI coordination layer with native multiplexing. Discussions and contributions welcome. Throw it a star if you like the project :)

Resources

Stars

29 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Tether

Major redesign of the dashboard Native multiplexing. Tmux no longer required. Constructed in such a way that Claude could easily set it up for you. Not everything may work correctly. Consider this beta, and an opportunity for contribution. App will flesh out and smooth out more in the coming weeks. Hot reload function added for dashboard, mapped to ctrl+shift+R

A content-addressed messaging layer for multi-agent development workflows. Send a message from Claude Code to Codex CLI. Wake Gemini from a tmail. Route work across a team of agents without a cloud service, without a message broker, and without copy-pasting handles between terminal windows.

Tether Dashboard — Network Graph

Five agents online, message routes as edges, real-time feed streaming on the right.


Why this exists

A lot of people are fine with just Claude or just Codex, and that's usually enough, but sometimes when you need differing perspectives, red-teaming, QA/QC, and someone to poke holes in the design to strengthen it, a second model comes in handy. My workflow usually has me using Claude, Codex, and Gemini, so them being able to coordinate like this outside of a tmux dependency has saved not only tokens, but time, and drastically improved the quality of my projects simply because one model won't catch everything. But in my experience, two might. The quality output concept is similar to what happens when you use OpenRouter's Fusion mode or Aider's Architect mode, just with extra steps and a lot more control in between. Functionally equivalent to those, but the models stay separate entities and operate more like a team than a single model.


What it is

Tether is a CLI-to-CLI messaging layer backed by a local SQLite database. Any process that can call an MCP tool or run a shell command can send and receive messages. It does not care whether the process is an AI model, a build script, or a human at a terminal.

Messages are stored as content-addressed handles — a BLAKE3 hash of the payload. Send the handle, not the payload. The receiver resolves it locally. No serialization mismatches, no "what did you actually send me."

For cross-machine workflows, the relay stores encrypted ciphertext envelopes. The relay never sees plaintext.


The v2 multiplexer — no tmux required

The biggest change in v2 is the native terminal multiplexer. On KDE/Konsole, Tether talks to each open tab over D-Bus and injects messages directly. No tmux sessions. No port numbers. No running a wrapper process per agent.

The delivery chain looks like this:

tether_send → fire ping → POST /api/konsole/deliver → qdbus6 sendText → agent wakes

The terminal receives a short durable-handle notice; the agent resolves the message from the shared SQLite mailbox, then processes and replies. Keeping the payload out of terminal injection avoids truncation and preserves a reviewable record of what was actually sent.

The Terminals tab lists every open Konsole tab, guesses which agent is running in it based on the process cmdline, and lets you bind it in one click. After that, tmail delivery is automatic.

Kilo receiving a tmail injection

Kilo's tab receives the injected message, resolves the handle, checks its inbox, and replies — all without any human relay.

Draft-safe wake behavior

Tether only submits a Konsole notice when it positively recognises an empty agent composer. If a recognised composer already contains text — or its state cannot be established — Tether inserts the notice without Enter. This preserves the draft Matt is writing; he can move the notice down with Shift+Enter, continue the draft, and decide when to submit it. The message is durable either way, so terminal delivery is a convenience signal rather than a source of truth.


Setup for seamless autoping

Two things need to be enabled for fully autonomous agent wake-up. Neither is on by default.

1. Enable the Konsole D-Bus security API

Konsole blocks D-Bus input injection by default. Go to:

Konsole → Settings → Configure Konsole → General
→ check "Enable the security sensitive parts of the DBus API"

This persists across reboots. It's a one-time change.

2. Run your agents in full-auto (yolo) mode

Injection fires the message into the agent's input and submits it. For the agent to process it without stopping to ask for confirmation, it needs to be running without approval prompts:

  • Codex CLI:codex --full-auto or codex --approval-mode full-auto
  • Claude Code:claude --dangerously-skip-permissions (or set in your project's permission config)
  • Kilo / other CLIs: check their --help for an equivalent auto-approve flag

Without this, the injection lands but the agent will pause and wait for you to hit Enter on a permission prompt — which defeats the point.


Register Node — agent discovery

The Register Node tab discovers running agents automatically. No port numbers required.


Install

git clone https://github.com/latentcollapse/tether.git
cd tether
pip install .

The built dashboard is committed in src/dashboard/dist. You don't need Node.js unless you're modifying the frontend.

MCP Setup

{
"mcpServers": {
"tether": {
"command": "tether-mcp",
"args": [],
"env": {
"TETHER_DB": "/path/to/shared/postoffice.db"
}
}
}
}

Every agent on the same machine should point to the same TETHER_DB. That's how they share a message bus.

Default locations if you omit TETHER_DB:

  • Linux/Mac: ~/.local/share/tether/postoffice.db
  • Windows: %APPDATA%\tether\postoffice.db

Starting the dashboard

tether

Opens at http://localhost:3000. If port 3000 is busy it picks the next free port.

Sending a message

From an MCP client (Claude Code, Codex CLI, etc.):

tether_send to="codex" subject="task" text="Refactor the verifier, see C-042 for context"
tether_inbox for_agent="codex"
tether_receive handle="h&l_messages_..."

From the shell:

tether send --from-agent claude codex "task""Refactor the verifier, see C-042 for context"

How handles work

payload → collapse → handle → send handle → receive → resolve

Handles are typed and content-addressed:

  • h&l_messages_{hash} — messages
  • h&l_inline_{hash} — small inline payloads
  • h&l_blob_{hash} — binary blobs
  • h&l_tree_{hash} — lists of child handles

Identical content always produces the same handle. You can send the same artifact to ten agents and it gets stored once.


MCP tools

ToolPurpose
tether_sendSend a message to another agent
tether_inboxList unread messages for an agent
tether_receiveRead a message by handle
tether_closeClose a message thread
tether_collapseCollapse a payload into a handle
tether_resolveResolve a handle back to its payload
tether_collapse_blobStore binary data as a blob handle
tether_resolve_blobResolve a blob handle
tether_collapse_treeStore a list of handles as a tree
tether_resolve_treeResolve a tree handle

Non-KDE terminals

If you're not on KDE/Konsole, tether mux wraps any CLI in a PTY that Tether owns:

tether mux --agent codex -- codex --full-auto resume

Tether owns the PTY, monitors for an idle prompt, and injects when the agent is ready. Same delivery guarantee, different ownership model.

Terminal-agnostic drivers (kitty, WezTerm, Windows Terminal) are on the roadmap. A Docker image and Nix flake for deployment are planned.


Cross-machine delivery

Connect — Direct Node Handshake

Add a relay and Tether reaches across machines. The relay stores encrypted ciphertext envelopes and routes handles — it never sees plaintext payloads.

# self-hosted relay
python -m uvicorn relay.main:app --host 0.0.0.0 --port 8000
# or Docker
docker-compose up --build

From your phone, tablet, or any browser: send a tmail to the relay, it gets pushed to the agent on your dev machine. No OAuth, no cloud vendor, no SSH tunnel.


Settings — themes and autonomous routing

Ember, Void, Cyberpunk, Synthwave, and more. Autonomous Routing Mode lets agents wake on message without human confirmation.


Security model

  • Local data never leaves the machine unless you configure a relay.
  • Relay sees ciphertext envelopes and routing metadata. Not plaintext.
  • Encryption and decryption happen on the client side only.
  • AGPL source — the transport and storage claims are auditable.

The D-Bus injection path requires explicit opt-in (the Konsole security setting above). Tether does not enable it silently.


Changelog

Full notes in changelog/.

VersionHighlights
v2.1.4Bug fixes: atomic self-healing ticket-ID issuance; --db honored across all commands; delivery/reap hardening; XDG DB migration; internal runtime split into board/tasks/sqlite mixins
v2.1Native D-Bus multiplexer, Terminals tab, no-tmux delivery, agent registry, presence heartbeat, routes API, real-time feed WebSocket
v1.8TetherLite storage/runtime, relay core, tier enforcement, encrypted envelopes
v1.7Ping daemon, autoping, local delivery tooling
v1.6Ping registration and push notifications
v1.5Shared task board
v1.4Tags, read state, ergonomic CLI improvements
v1.0–v1.3Base handle/runtime model

License

  • Source: AGPL v3
  • Hosted relay service terms: separate from source distribution

About

This is a multi-CLI coordination layer with native multiplexing. Discussions and contributions welcome. Throw it a star if you like the project :)

Resources

Stars

29 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Tether

Major redesign of the dashboard Native multiplexing. Tmux no longer required. Constructed in such a way that Claude could easily set it up for you. Not everything may work correctly. Consider this beta, and an opportunity for contribution. App will flesh out and smooth out more in the coming weeks. Hot reload function added for dashboard, mapped to ctrl+shift+R

A content-addressed messaging layer for multi-agent development workflows. Send a message from Claude Code to Codex CLI. Wake Gemini from a tmail. Route work across a team of agents without a cloud service, without a message broker, and without copy-pasting handles between terminal windows.

Tether Dashboard — Network Graph

Five agents online, message routes as edges, real-time feed streaming on the right.


Why this exists

A lot of people are fine with just Claude or just Codex, and that's usually enough, but sometimes when you need differing perspectives, red-teaming, QA/QC, and someone to poke holes in the design to strengthen it, a second model comes in handy. My workflow usually has me using Claude, Codex, and Gemini, so them being able to coordinate like this outside of a tmux dependency has saved not only tokens, but time, and drastically improved the quality of my projects simply because one model won't catch everything. But in my experience, two might. The quality output concept is similar to what happens when you use OpenRouter's Fusion mode or Aider's Architect mode, just with extra steps and a lot more control in between. Functionally equivalent to those, but the models stay separate entities and operate more like a team than a single model.


What it is

Tether is a CLI-to-CLI messaging layer backed by a local SQLite database. Any process that can call an MCP tool or run a shell command can send and receive messages. It does not care whether the process is an AI model, a build script, or a human at a terminal.

Messages are stored as content-addressed handles — a BLAKE3 hash of the payload. Send the handle, not the payload. The receiver resolves it locally. No serialization mismatches, no "what did you actually send me."

For cross-machine workflows, the relay stores encrypted ciphertext envelopes. The relay never sees plaintext.


The v2 multiplexer — no tmux required

The biggest change in v2 is the native terminal multiplexer. On KDE/Konsole, Tether talks to each open tab over D-Bus and injects messages directly. No tmux sessions. No port numbers. No running a wrapper process per agent.

The delivery chain looks like this:

tether_send → fire ping → POST /api/konsole/deliver → qdbus6 sendText → agent wakes

The terminal receives a short durable-handle notice; the agent resolves the message from the shared SQLite mailbox, then processes and replies. Keeping the payload out of terminal injection avoids truncation and preserves a reviewable record of what was actually sent.

The Terminals tab lists every open Konsole tab, guesses which agent is running in it based on the process cmdline, and lets you bind it in one click. After that, tmail delivery is automatic.

Kilo receiving a tmail injection

Kilo's tab receives the injected message, resolves the handle, checks its inbox, and replies — all without any human relay.

Draft-safe wake behavior

Tether only submits a Konsole notice when it positively recognises an empty agent composer. If a recognised composer already contains text — or its state cannot be established — Tether inserts the notice without Enter. This preserves the draft Matt is writing; he can move the notice down with Shift+Enter, continue the draft, and decide when to submit it. The message is durable either way, so terminal delivery is a convenience signal rather than a source of truth.


Setup for seamless autoping

Two things need to be enabled for fully autonomous agent wake-up. Neither is on by default.

1. Enable the Konsole D-Bus security API

Konsole blocks D-Bus input injection by default. Go to:

Konsole → Settings → Configure Konsole → General
→ check "Enable the security sensitive parts of the DBus API"

This persists across reboots. It's a one-time change.

2. Run your agents in full-auto (yolo) mode

Injection fires the message into the agent's input and submits it. For the agent to process it without stopping to ask for confirmation, it needs to be running without approval prompts:

  • Codex CLI:codex --full-auto or codex --approval-mode full-auto
  • Claude Code:claude --dangerously-skip-permissions (or set in your project's permission config)
  • Kilo / other CLIs: check their --help for an equivalent auto-approve flag

Without this, the injection lands but the agent will pause and wait for you to hit Enter on a permission prompt — which defeats the point.


Register Node — agent discovery

The Register Node tab discovers running agents automatically. No port numbers required.


Install

git clone https://github.com/latentcollapse/tether.git
cd tether
pip install .

The built dashboard is committed in src/dashboard/dist. You don't need Node.js unless you're modifying the frontend.

MCP Setup

{
"mcpServers": {
"tether": {
"command": "tether-mcp",
"args": [],
"env": {
"TETHER_DB": "/path/to/shared/postoffice.db"
}
}
}
}

Every agent on the same machine should point to the same TETHER_DB. That's how they share a message bus.

Default locations if you omit TETHER_DB:

  • Linux/Mac: ~/.local/share/tether/postoffice.db
  • Windows: %APPDATA%\tether\postoffice.db

Starting the dashboard

tether

Opens at http://localhost:3000. If port 3000 is busy it picks the next free port.

Sending a message

From an MCP client (Claude Code, Codex CLI, etc.):

tether_send to="codex" subject="task" text="Refactor the verifier, see C-042 for context"
tether_inbox for_agent="codex"
tether_receive handle="h&l_messages_..."

From the shell:

tether send --from-agent claude codex "task""Refactor the verifier, see C-042 for context"

How handles work

payload → collapse → handle → send handle → receive → resolve

Handles are typed and content-addressed:

  • h&l_messages_{hash} — messages
  • h&l_inline_{hash} — small inline payloads
  • h&l_blob_{hash} — binary blobs
  • h&l_tree_{hash} — lists of child handles

Identical content always produces the same handle. You can send the same artifact to ten agents and it gets stored once.


MCP tools

ToolPurpose
tether_sendSend a message to another agent
tether_inboxList unread messages for an agent
tether_receiveRead a message by handle
tether_closeClose a message thread
tether_collapseCollapse a payload into a handle
tether_resolveResolve a handle back to its payload
tether_collapse_blobStore binary data as a blob handle
tether_resolve_blobResolve a blob handle
tether_collapse_treeStore a list of handles as a tree
tether_resolve_treeResolve a tree handle

Non-KDE terminals

If you're not on KDE/Konsole, tether mux wraps any CLI in a PTY that Tether owns:

tether mux --agent codex -- codex --full-auto resume

Tether owns the PTY, monitors for an idle prompt, and injects when the agent is ready. Same delivery guarantee, different ownership model.

Terminal-agnostic drivers (kitty, WezTerm, Windows Terminal) are on the roadmap. A Docker image and Nix flake for deployment are planned.


Cross-machine delivery

Connect — Direct Node Handshake

Add a relay and Tether reaches across machines. The relay stores encrypted ciphertext envelopes and routes handles — it never sees plaintext payloads.

# self-hosted relay
python -m uvicorn relay.main:app --host 0.0.0.0 --port 8000
# or Docker
docker-compose up --build

From your phone, tablet, or any browser: send a tmail to the relay, it gets pushed to the agent on your dev machine. No OAuth, no cloud vendor, no SSH tunnel.


Settings — themes and autonomous routing

Ember, Void, Cyberpunk, Synthwave, and more. Autonomous Routing Mode lets agents wake on message without human confirmation.


Security model

  • Local data never leaves the machine unless you configure a relay.
  • Relay sees ciphertext envelopes and routing metadata. Not plaintext.
  • Encryption and decryption happen on the client side only.
  • AGPL source — the transport and storage claims are auditable.

The D-Bus injection path requires explicit opt-in (the Konsole security setting above). Tether does not enable it silently.


Changelog

Full notes in changelog/.

VersionHighlights
v2.1.4Bug fixes: atomic self-healing ticket-ID issuance; --db honored across all commands; delivery/reap hardening; XDG DB migration; internal runtime split into board/tasks/sqlite mixins
v2.1Native D-Bus multiplexer, Terminals tab, no-tmux delivery, agent registry, presence heartbeat, routes API, real-time feed WebSocket
v1.8TetherLite storage/runtime, relay core, tier enforcement, encrypted envelopes
v1.7Ping daemon, autoping, local delivery tooling
v1.6Ping registration and push notifications
v1.5Shared task board
v1.4Tags, read state, ergonomic CLI improvements
v1.0–v1.3Base handle/runtime model

License

  • Source: AGPL v3
  • Hosted relay service terms: separate from source distribution

About

This is a multi-CLI coordination layer with native multiplexing. Discussions and contributions welcome. Throw it a star if you like the project :)

Resources

Stars

29 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

Tether

Major redesign of the dashboard Native multiplexing. Tmux no longer required. Constructed in such a way that Claude could easily set it up for you. Not everything may work correctly. Consider this beta, and an opportunity for contribution. App will flesh out and smooth out more in the coming weeks. Hot reload function added for dashboard, mapped to ctrl+shift+R

A content-addressed messaging layer for multi-agent development workflows. Send a message from Claude Code to Codex CLI. Wake Gemini from a tmail. Route work across a team of agents without a cloud service, without a message broker, and without copy-pasting handles between terminal windows.

Tether Dashboard — Network Graph

Five agents online, message routes as edges, real-time feed streaming on the right.


Why this exists

A lot of people are fine with just Claude or just Codex, and that's usually enough, but sometimes when you need differing perspectives, red-teaming, QA/QC, and someone to poke holes in the design to strengthen it, a second model comes in handy. My workflow usually has me using Claude, Codex, and Gemini, so them being able to coordinate like this outside of a tmux dependency has saved not only tokens, but time, and drastically improved the quality of my projects simply because one model won't catch everything. But in my experience, two might. The quality output concept is similar to what happens when you use OpenRouter's Fusion mode or Aider's Architect mode, just with extra steps and a lot more control in between. Functionally equivalent to those, but the models stay separate entities and operate more like a team than a single model.


What it is

Tether is a CLI-to-CLI messaging layer backed by a local SQLite database. Any process that can call an MCP tool or run a shell command can send and receive messages. It does not care whether the process is an AI model, a build script, or a human at a terminal.

Messages are stored as content-addressed handles — a BLAKE3 hash of the payload. Send the handle, not the payload. The receiver resolves it locally. No serialization mismatches, no "what did you actually send me."

For cross-machine workflows, the relay stores encrypted ciphertext envelopes. The relay never sees plaintext.


The v2 multiplexer — no tmux required

The biggest change in v2 is the native terminal multiplexer. On KDE/Konsole, Tether talks to each open tab over D-Bus and injects messages directly. No tmux sessions. No port numbers. No running a wrapper process per agent.

The delivery chain looks like this:

tether_send → fire ping → POST /api/konsole/deliver → qdbus6 sendText → agent wakes

The terminal receives a short durable-handle notice; the agent resolves the message from the shared SQLite mailbox, then processes and replies. Keeping the payload out of terminal injection avoids truncation and preserves a reviewable record of what was actually sent.

The Terminals tab lists every open Konsole tab, guesses which agent is running in it based on the process cmdline, and lets you bind it in one click. After that, tmail delivery is automatic.

Kilo receiving a tmail injection

Kilo's tab receives the injected message, resolves the handle, checks its inbox, and replies — all without any human relay.

Draft-safe wake behavior

Tether only submits a Konsole notice when it positively recognises an empty agent composer. If a recognised composer already contains text — or its state cannot be established — Tether inserts the notice without Enter. This preserves the draft Matt is writing; he can move the notice down with Shift+Enter, continue the draft, and decide when to submit it. The message is durable either way, so terminal delivery is a convenience signal rather than a source of truth.


Setup for seamless autoping

Two things need to be enabled for fully autonomous agent wake-up. Neither is on by default.

1. Enable the Konsole D-Bus security API

Konsole blocks D-Bus input injection by default. Go to:

Konsole → Settings → Configure Konsole → General
→ check "Enable the security sensitive parts of the DBus API"

This persists across reboots. It's a one-time change.

2. Run your agents in full-auto (yolo) mode

Injection fires the message into the agent's input and submits it. For the agent to process it without stopping to ask for confirmation, it needs to be running without approval prompts:

  • Codex CLI:codex --full-auto or codex --approval-mode full-auto
  • Claude Code:claude --dangerously-skip-permissions (or set in your project's permission config)
  • Kilo / other CLIs: check their --help for an equivalent auto-approve flag

Without this, the injection lands but the agent will pause and wait for you to hit Enter on a permission prompt — which defeats the point.


Register Node — agent discovery

The Register Node tab discovers running agents automatically. No port numbers required.


Install

git clone https://github.com/latentcollapse/tether.git
cd tether
pip install .

The built dashboard is committed in src/dashboard/dist. You don't need Node.js unless you're modifying the frontend.

MCP Setup

{
"mcpServers": {
"tether": {
"command": "tether-mcp",
"args": [],
"env": {
"TETHER_DB": "/path/to/shared/postoffice.db"
}
}
}
}

Every agent on the same machine should point to the same TETHER_DB. That's how they share a message bus.

Default locations if you omit TETHER_DB:

  • Linux/Mac: ~/.local/share/tether/postoffice.db
  • Windows: %APPDATA%\tether\postoffice.db

Starting the dashboard

tether

Opens at http://localhost:3000. If port 3000 is busy it picks the next free port.

Sending a message

From an MCP client (Claude Code, Codex CLI, etc.):

tether_send to="codex" subject="task" text="Refactor the verifier, see C-042 for context"
tether_inbox for_agent="codex"
tether_receive handle="h&l_messages_..."

From the shell:

tether send --from-agent claude codex "task""Refactor the verifier, see C-042 for context"

How handles work

payload → collapse → handle → send handle → receive → resolve

Handles are typed and content-addressed:

  • h&l_messages_{hash} — messages
  • h&l_inline_{hash} — small inline payloads
  • h&l_blob_{hash} — binary blobs
  • h&l_tree_{hash} — lists of child handles

Identical content always produces the same handle. You can send the same artifact to ten agents and it gets stored once.


MCP tools

ToolPurpose
tether_sendSend a message to another agent
tether_inboxList unread messages for an agent
tether_receiveRead a message by handle
tether_closeClose a message thread
tether_collapseCollapse a payload into a handle
tether_resolveResolve a handle back to its payload
tether_collapse_blobStore binary data as a blob handle
tether_resolve_blobResolve a blob handle
tether_collapse_treeStore a list of handles as a tree
tether_resolve_treeResolve a tree handle

Non-KDE terminals

If you're not on KDE/Konsole, tether mux wraps any CLI in a PTY that Tether owns:

tether mux --agent codex -- codex --full-auto resume

Tether owns the PTY, monitors for an idle prompt, and injects when the agent is ready. Same delivery guarantee, different ownership model.

Terminal-agnostic drivers (kitty, WezTerm, Windows Terminal) are on the roadmap. A Docker image and Nix flake for deployment are planned.


Cross-machine delivery

Connect — Direct Node Handshake

Add a relay and Tether reaches across machines. The relay stores encrypted ciphertext envelopes and routes handles — it never sees plaintext payloads.

# self-hosted relay
python -m uvicorn relay.main:app --host 0.0.0.0 --port 8000
# or Docker
docker-compose up --build

From your phone, tablet, or any browser: send a tmail to the relay, it gets pushed to the agent on your dev machine. No OAuth, no cloud vendor, no SSH tunnel.


Settings — themes and autonomous routing

Ember, Void, Cyberpunk, Synthwave, and more. Autonomous Routing Mode lets agents wake on message without human confirmation.


Security model

  • Local data never leaves the machine unless you configure a relay.
  • Relay sees ciphertext envelopes and routing metadata. Not plaintext.
  • Encryption and decryption happen on the client side only.
  • AGPL source — the transport and storage claims are auditable.

The D-Bus injection path requires explicit opt-in (the Konsole security setting above). Tether does not enable it silently.


Changelog

Full notes in changelog/.

VersionHighlights
v2.1.4Bug fixes: atomic self-healing ticket-ID issuance; --db honored across all commands; delivery/reap hardening; XDG DB migration; internal runtime split into board/tasks/sqlite mixins
v2.1Native D-Bus multiplexer, Terminals tab, no-tmux delivery, agent registry, presence heartbeat, routes API, real-time feed WebSocket
v1.8TetherLite storage/runtime, relay core, tier enforcement, encrypted envelopes
v1.7Ping daemon, autoping, local delivery tooling
v1.6Ping registration and push notifications
v1.5Shared task board
v1.4Tags, read state, ergonomic CLI improvements
v1.0–v1.3Base handle/runtime model

License

  • Source: AGPL v3
  • Hosted relay service terms: separate from source distribution

About

This is a multi-CLI coordination layer with native multiplexing. Discussions and contributions welcome. Throw it a star if you like the project :)

Resources

Stars

29 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages