kache-ssh 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.pyc
4
+ dist/
5
+ build/
6
+ *.egg-info/
7
+ .pytest_cache/
@@ -0,0 +1,37 @@
1
+ # Working on Kache
2
+
3
+ **Using Kache** (sending orders, reading the feed, MCP): read
4
+ [src/kache/AGENTS.md](src/kache/AGENTS.md), which is also printed by `kache guide`.
5
+
6
+ **Changing Kache:**
7
+
8
+ - Setup: `python -m venv .venv`, then `.venv/Scripts/pip install -e ".[dev]"`
9
+ (Windows) or `.venv/bin/pip install -e ".[dev]"`.
10
+ - Tests: `python -m pytest -q`. They start real gatekeepers on 127.0.0.1
11
+ with temporary state folders and test through the real SSH path, the CLI
12
+ (subprocesses), the MCP server and the Textual app (`run_test`).
13
+
14
+ Layout (`src/kache/`):
15
+
16
+ | file | role |
17
+ |---|---|
18
+ | `agent.py` | the gatekeeper: SSH server, internal `kache:` requests, orders, feed, pairing |
19
+ | `client.py` | connecting (address race, key pinning), orders, pairing, device list |
20
+ | `probe.py` | status snapshots, Tailscale discovery, `doctor` (shared by CLI, MCP, TUI) |
21
+ | `network.py` | Tailscale and interface addresses, listen modes |
22
+ | `cli.py` | the `kache` command |
23
+ | `tui.py` | the Kache terminal (Textual) |
24
+ | `mcp.py` | MCP server over stdio |
25
+ | `evidence.py`, `telemetry.py` | failure reports, health numbers |
26
+ | `config.py`, `keys.py`, `service.py` | state folder, device key, autostart |
27
+
28
+ Rules:
29
+
30
+ - Bump `PROTOCOL` in `__init__.py` when gatekeepers of different versions
31
+ could misread each other.
32
+ - Bump `SCHEMA` when a JSON shape that agents read changes incompatibly, and
33
+ update `src/kache/AGENTS.md` with it.
34
+ - The client must only ever offer the device key. `_DeviceKeyClient` exists
35
+ because asyncssh would otherwise add ssh-agent keys first.
36
+ - Windows orders run from a temporary UTF-8 `.ps1` (`-File`), not
37
+ `-EncodedCommand`, which turns errors into CLIXML.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Simon Malm
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,174 @@
1
+ Metadata-Version: 2.5
2
+ Name: kache-ssh
3
+ Version: 0.1.0
4
+ Summary: Kache: one terminal to command all your devices over SSH, with a gatekeeper agent on every device.
5
+ Author: Simon Malm
6
+ License-Expression: MIT
7
+ License-File: LICENSE
8
+ Keywords: ai-agents,fleet,mcp,orchestration,remote,ssh,tailscale,terminal,tui
9
+ Classifier: Environment :: Console
10
+ Classifier: Operating System :: MacOS
11
+ Classifier: Operating System :: Microsoft :: Windows
12
+ Classifier: Operating System :: POSIX :: Linux
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Topic :: System :: Systems Administration
15
+ Requires-Python: >=3.10
16
+ Requires-Dist: asyncssh>=2.14
17
+ Requires-Dist: platformdirs>=4.0
18
+ Requires-Dist: psutil>=5.9
19
+ Requires-Dist: textual>=8.0
20
+ Provides-Extra: dev
21
+ Requires-Dist: build>=1.0; extra == 'dev'
22
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
23
+ Requires-Dist: pytest>=8; extra == 'dev'
24
+ Provides-Extra: gpu
25
+ Requires-Dist: nvidia-ml-py>=12; extra == 'gpu'
26
+ Description-Content-Type: text/markdown
27
+
28
+ # Kache
29
+
30
+ One terminal to command all your devices.
31
+
32
+ Kache is a hub that runs in your terminal. Pair your computers once, then
33
+ type a command and it runs on the device you picked, or on all of them at
34
+ once, exactly as if you were sitting at that machine. Every device runs a
35
+ small **gatekeeper** that only obeys devices you paired, refuses orders that
36
+ cannot work there, and sends back an evidence report when something fails.
37
+ Every order anyone sends (you, a script, or an AI agent working for you)
38
+ shows up in the live feed with the exact command.
39
+
40
+ ```
41
+ pip install kache-ssh
42
+ kache
43
+ ```
44
+
45
+ ## What you get
46
+
47
+ - **A real remote terminal.** What you type is what runs on the other device,
48
+ in its own shell (PowerShell on Windows, your shell on Linux and macOS).
49
+ `cd` sticks between commands, output streams live, Ctrl+C stops an order.
50
+ - **One device or all of them.** Pick a device, or press F6 and send the same
51
+ order to every device in parallel; output is labelled per device.
52
+ - **Device health at a glance.** Online/offline, CPU and RAM use, GPU load,
53
+ temperature and memory (NVIDIA), and CPU temperature where the OS exposes it.
54
+ - **Pairing with a yes/no question.** Ask another device to pair; its Kache
55
+ terminal asks *"laptop wants to send orders to this device. Code 482 913.
56
+ Accept?"* Both screens show the same code, so nobody can sit in between.
57
+ - **Works at home and away.** Kache remembers every address of a device (LAN,
58
+ Tailscale IP, Tailscale name) and uses whichever answers. Pair once at
59
+ home; it keeps working over [Tailscale](https://tailscale.com) anywhere.
60
+ - **A gatekeeper on every device.** Unknown devices can only say hello or ask
61
+ to pair. Paired devices are pinned by key. Orders for commands that do not
62
+ exist on that device are refused up front (Linux/macOS) or explained
63
+ afterwards (Windows).
64
+ - **Evidence when things break.** A failing order comes back with a report:
65
+ exit code, last lines of output, labelled hints (command not found,
66
+ permission, disk full, missing Python module, network, out of memory...)
67
+ and the machine's state at that moment.
68
+ - **A live feed of every order.** Who sent what to which device, when, and
69
+ how it ended, including orders from scripts and AI agents, which are
70
+ labelled with the agent's name.
71
+ - **Built for AI agents too.** Every command has `--json`, there is an MCP
72
+ server (`kache mcp`), and `kache guide` explains everything an agent needs.
73
+
74
+ ## Quick start
75
+
76
+ On every device:
77
+
78
+ ```
79
+ pip install kache-ssh
80
+ kache service install # keep the gatekeeper running at logon (recommended)
81
+ kache # open the Kache terminal
82
+ ```
83
+
84
+ In the Kache terminal on the device you want to command *from*, press **F2**
85
+ and enter the other device's address: its LAN IP, Tailscale IP or Tailscale
86
+ machine name (devices on your tailnet are listed for you). Accept on the
87
+ other device when the codes match. It appears in your device list, and
88
+ anything you type now runs there.
89
+
90
+ On Windows, the first time the gatekeeper starts, Windows may ask whether
91
+ Python may accept connections. Allow it on private networks.
92
+
93
+ ### Keys
94
+
95
+ | Key | Action |
96
+ |------------|-----------------------------------------------|
97
+ | Enter | send what you typed to the selected device(s) |
98
+ | ↑ / ↓ | previous / next command |
99
+ | Ctrl+↑ / ↓ | select the previous / next device |
100
+ | F6 | switch between one device and all devices |
101
+ | Ctrl+C | stop the running order(s) |
102
+ | F2 | pair a new device |
103
+ | F3 | rename the selected device |
104
+ | F4 | remove the selected device |
105
+ | F5 | show the last failure's evidence |
106
+ | Ctrl+Q | quit |
107
+
108
+ ## Away from home: Tailscale
109
+
110
+ Kache connects devices directly, so a device must be reachable from the one
111
+ commanding it. At home that is your LAN. Away from home, install
112
+ [Tailscale](https://tailscale.com) (free for personal use) on both devices
113
+ and sign in with the same account. Then:
114
+
115
+ - `kache discover` lists the devices on your tailnet and which run Kache;
116
+ - pairing by Tailscale name works: `kache pair my-desktop`;
117
+ - devices paired over LAN switch to Tailscale automatically when the LAN
118
+ address stops answering;
119
+ - `kache listen tailscale` makes a gatekeeper answer on your tailnet only,
120
+ ignoring the local network.
121
+
122
+ ## For scripts and AI agents
123
+
124
+ Everything the terminal does is also a command with `--json` output, and
125
+ every order still appears in the feed:
126
+
127
+ ```
128
+ kache devices --json # status and health of every device
129
+ kache run desktop --json --timeout 60 -- nvidia-smi
130
+ kache run all --as nightly-job -- git pull # labelled in every feed
131
+ kache feed -d all -f --json # live JSON-lines feed from all devices
132
+ kache evidence E-3f2a9c -d desktop # a failure report
133
+ kache doctor desktop --json # why a device cannot be reached
134
+ ```
135
+
136
+ For MCP clients (Claude Code, Claude Desktop, Cursor and others):
137
+
138
+ ```
139
+ claude mcp add kache -- kache mcp
140
+ ```
141
+
142
+ Agents get the tools `kache_devices`, `kache_run`, `kache_feed`,
143
+ `kache_evidence`, `kache_doctor`, `kache_discover`, `kache_pending` and
144
+ `kache_pair`. Accepting pair requests and revoking devices stays with
145
+ people. `kache guide` prints the full guide: JSON shapes, feed event kinds,
146
+ exit codes and rules for agents.
147
+
148
+ ## How it works
149
+
150
+ Each device has one ed25519 key, created on first run. The gatekeeper is an
151
+ SSH server (built on asyncssh, port 7722) that accepts any key but only lets
152
+ unknown keys say hello or ask to pair; paired keys may send orders. The
153
+ Kache terminal is an SSH client that pins each paired device's key. Nothing
154
+ goes through a server in between.
155
+
156
+ State lives in one folder per device (`kache whoami` shows where; override
157
+ with `KACHE_HOME`): the device key, its name, the devices it commands, the
158
+ devices trusted to command it, the feed and the evidence reports.
159
+
160
+ If something does not connect, `kache doctor` checks every step (gatekeeper,
161
+ Tailscale, each address, the device's key, the pairing) and says what to fix.
162
+
163
+ ## Status
164
+
165
+ Version 0.1: pairing, orders, sessions, evidence, live feed, health,
166
+ Tailscale, JSON output and MCP work on Windows, Linux and macOS.
167
+
168
+ Planned:
169
+ - full interactive sessions (`kache attach` for programs like vim or htop);
170
+ - confirmation popups for dangerous orders;
171
+ - pairing both directions in one step;
172
+ - a relay for devices that cannot reach each other directly.
173
+
174
+ MIT licensed.
@@ -0,0 +1,147 @@
1
+ # Kache
2
+
3
+ One terminal to command all your devices.
4
+
5
+ Kache is a hub that runs in your terminal. Pair your computers once, then
6
+ type a command and it runs on the device you picked, or on all of them at
7
+ once, exactly as if you were sitting at that machine. Every device runs a
8
+ small **gatekeeper** that only obeys devices you paired, refuses orders that
9
+ cannot work there, and sends back an evidence report when something fails.
10
+ Every order anyone sends (you, a script, or an AI agent working for you)
11
+ shows up in the live feed with the exact command.
12
+
13
+ ```
14
+ pip install kache-ssh
15
+ kache
16
+ ```
17
+
18
+ ## What you get
19
+
20
+ - **A real remote terminal.** What you type is what runs on the other device,
21
+ in its own shell (PowerShell on Windows, your shell on Linux and macOS).
22
+ `cd` sticks between commands, output streams live, Ctrl+C stops an order.
23
+ - **One device or all of them.** Pick a device, or press F6 and send the same
24
+ order to every device in parallel; output is labelled per device.
25
+ - **Device health at a glance.** Online/offline, CPU and RAM use, GPU load,
26
+ temperature and memory (NVIDIA), and CPU temperature where the OS exposes it.
27
+ - **Pairing with a yes/no question.** Ask another device to pair; its Kache
28
+ terminal asks *"laptop wants to send orders to this device. Code 482 913.
29
+ Accept?"* Both screens show the same code, so nobody can sit in between.
30
+ - **Works at home and away.** Kache remembers every address of a device (LAN,
31
+ Tailscale IP, Tailscale name) and uses whichever answers. Pair once at
32
+ home; it keeps working over [Tailscale](https://tailscale.com) anywhere.
33
+ - **A gatekeeper on every device.** Unknown devices can only say hello or ask
34
+ to pair. Paired devices are pinned by key. Orders for commands that do not
35
+ exist on that device are refused up front (Linux/macOS) or explained
36
+ afterwards (Windows).
37
+ - **Evidence when things break.** A failing order comes back with a report:
38
+ exit code, last lines of output, labelled hints (command not found,
39
+ permission, disk full, missing Python module, network, out of memory...)
40
+ and the machine's state at that moment.
41
+ - **A live feed of every order.** Who sent what to which device, when, and
42
+ how it ended, including orders from scripts and AI agents, which are
43
+ labelled with the agent's name.
44
+ - **Built for AI agents too.** Every command has `--json`, there is an MCP
45
+ server (`kache mcp`), and `kache guide` explains everything an agent needs.
46
+
47
+ ## Quick start
48
+
49
+ On every device:
50
+
51
+ ```
52
+ pip install kache-ssh
53
+ kache service install # keep the gatekeeper running at logon (recommended)
54
+ kache # open the Kache terminal
55
+ ```
56
+
57
+ In the Kache terminal on the device you want to command *from*, press **F2**
58
+ and enter the other device's address: its LAN IP, Tailscale IP or Tailscale
59
+ machine name (devices on your tailnet are listed for you). Accept on the
60
+ other device when the codes match. It appears in your device list, and
61
+ anything you type now runs there.
62
+
63
+ On Windows, the first time the gatekeeper starts, Windows may ask whether
64
+ Python may accept connections. Allow it on private networks.
65
+
66
+ ### Keys
67
+
68
+ | Key | Action |
69
+ |------------|-----------------------------------------------|
70
+ | Enter | send what you typed to the selected device(s) |
71
+ | ↑ / ↓ | previous / next command |
72
+ | Ctrl+↑ / ↓ | select the previous / next device |
73
+ | F6 | switch between one device and all devices |
74
+ | Ctrl+C | stop the running order(s) |
75
+ | F2 | pair a new device |
76
+ | F3 | rename the selected device |
77
+ | F4 | remove the selected device |
78
+ | F5 | show the last failure's evidence |
79
+ | Ctrl+Q | quit |
80
+
81
+ ## Away from home: Tailscale
82
+
83
+ Kache connects devices directly, so a device must be reachable from the one
84
+ commanding it. At home that is your LAN. Away from home, install
85
+ [Tailscale](https://tailscale.com) (free for personal use) on both devices
86
+ and sign in with the same account. Then:
87
+
88
+ - `kache discover` lists the devices on your tailnet and which run Kache;
89
+ - pairing by Tailscale name works: `kache pair my-desktop`;
90
+ - devices paired over LAN switch to Tailscale automatically when the LAN
91
+ address stops answering;
92
+ - `kache listen tailscale` makes a gatekeeper answer on your tailnet only,
93
+ ignoring the local network.
94
+
95
+ ## For scripts and AI agents
96
+
97
+ Everything the terminal does is also a command with `--json` output, and
98
+ every order still appears in the feed:
99
+
100
+ ```
101
+ kache devices --json # status and health of every device
102
+ kache run desktop --json --timeout 60 -- nvidia-smi
103
+ kache run all --as nightly-job -- git pull # labelled in every feed
104
+ kache feed -d all -f --json # live JSON-lines feed from all devices
105
+ kache evidence E-3f2a9c -d desktop # a failure report
106
+ kache doctor desktop --json # why a device cannot be reached
107
+ ```
108
+
109
+ For MCP clients (Claude Code, Claude Desktop, Cursor and others):
110
+
111
+ ```
112
+ claude mcp add kache -- kache mcp
113
+ ```
114
+
115
+ Agents get the tools `kache_devices`, `kache_run`, `kache_feed`,
116
+ `kache_evidence`, `kache_doctor`, `kache_discover`, `kache_pending` and
117
+ `kache_pair`. Accepting pair requests and revoking devices stays with
118
+ people. `kache guide` prints the full guide: JSON shapes, feed event kinds,
119
+ exit codes and rules for agents.
120
+
121
+ ## How it works
122
+
123
+ Each device has one ed25519 key, created on first run. The gatekeeper is an
124
+ SSH server (built on asyncssh, port 7722) that accepts any key but only lets
125
+ unknown keys say hello or ask to pair; paired keys may send orders. The
126
+ Kache terminal is an SSH client that pins each paired device's key. Nothing
127
+ goes through a server in between.
128
+
129
+ State lives in one folder per device (`kache whoami` shows where; override
130
+ with `KACHE_HOME`): the device key, its name, the devices it commands, the
131
+ devices trusted to command it, the feed and the evidence reports.
132
+
133
+ If something does not connect, `kache doctor` checks every step (gatekeeper,
134
+ Tailscale, each address, the device's key, the pairing) and says what to fix.
135
+
136
+ ## Status
137
+
138
+ Version 0.1: pairing, orders, sessions, evidence, live feed, health,
139
+ Tailscale, JSON output and MCP work on Windows, Linux and macOS.
140
+
141
+ Planned:
142
+ - full interactive sessions (`kache attach` for programs like vim or htop);
143
+ - confirmation popups for dangerous orders;
144
+ - pairing both directions in one step;
145
+ - a relay for devices that cannot reach each other directly.
146
+
147
+ MIT licensed.
@@ -0,0 +1,42 @@
1
+ [build-system]
2
+ requires = ["hatchling>=1.24"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "kache-ssh"
7
+ version = "0.1.0"
8
+ description = "Kache: one terminal to command all your devices over SSH, with a gatekeeper agent on every device."
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = "MIT"
12
+ license-files = ["LICENSE"]
13
+ authors = [{ name = "Simon Malm" }]
14
+ keywords = ["ssh", "remote", "orchestration", "terminal", "tui", "fleet", "tailscale", "mcp", "ai-agents"]
15
+ classifiers = [
16
+ "Environment :: Console",
17
+ "Operating System :: Microsoft :: Windows",
18
+ "Operating System :: POSIX :: Linux",
19
+ "Operating System :: MacOS",
20
+ "Programming Language :: Python :: 3",
21
+ "Topic :: System :: Systems Administration",
22
+ ]
23
+ dependencies = [
24
+ "asyncssh>=2.14",
25
+ "textual>=8.0",
26
+ "psutil>=5.9",
27
+ "platformdirs>=4.0",
28
+ ]
29
+
30
+ [project.optional-dependencies]
31
+ gpu = ["nvidia-ml-py>=12"]
32
+ dev = ["pytest>=8", "pytest-asyncio>=0.23", "build>=1.0"]
33
+
34
+ [project.scripts]
35
+ kache = "kache.cli:main"
36
+
37
+ [tool.hatch.build.targets.wheel]
38
+ packages = ["src/kache"]
39
+
40
+ [tool.pytest.ini_options]
41
+ asyncio_mode = "auto"
42
+ testpaths = ["tests"]
@@ -0,0 +1,212 @@
1
+ # Kache guide for scripts and AI agents
2
+
3
+ Kache sends shell commands to a person's own devices (their "paired"
4
+ devices) and keeps a live feed of every order. You can use all of it from a
5
+ shell (`kache ... --json`) or as MCP tools (`kache mcp`). This guide is
6
+ everything you need; `kache --help` and `kache <command> --help` list flags.
7
+
8
+ ## How it works
9
+
10
+ - Every device runs a **gatekeeper** (an SSH server on TCP port 7722). It
11
+ executes orders only from devices it has paired with, and only after a
12
+ person said yes to the pairing on that device.
13
+ - An **order** is one command line. It runs exactly as written in the
14
+ device's own shell: **PowerShell on Windows**, the user's shell
15
+ (`$SHELL -c`) on Linux and macOS. Check a device's `os` before writing
16
+ commands for it.
17
+ - **Sessions:** `cd` is remembered per sender and session name, so
18
+ `cd C:\proj` followed by `git status` in the same session works like a
19
+ terminal. Other shell state (variables, activated venvs) does not carry
20
+ over: each order is a fresh shell started in the session folder.
21
+ - **Every order is visible.** Each one appears in the feed of the device
22
+ that ran it, with the exact command, who sent it and how it ended. The
23
+ person who owns the devices may be watching live.
24
+ - **Labels.** Orders carry a label naming who gave them. It is taken from
25
+ `--as`, then `$KACHE_AS`, then an AI agent's own name (`$AI_AGENT`,
26
+ `CLAUDECODE`). Through MCP the label is the MCP client's name. Never
27
+ pretend to be the human.
28
+ - **Failures come with evidence.** When an order fails, is refused or is
29
+ stopped, the gatekeeper writes an evidence report: hints, the last output
30
+ lines, and the machine's state at that moment.
31
+ - **Addresses.** A paired device is remembered with all of its addresses
32
+ (LAN IP, Tailscale IP, Tailscale name). Kache tries the one that worked
33
+ last first, then the others, so devices stay reachable away from home
34
+ when both run Tailscale.
35
+
36
+ ## Rules for agents
37
+
38
+ 1. Look before you act. Start with `kache devices --json`. It shows what
39
+ exists, which devices are online, their OS, and how loaded they are.
40
+ 2. Prefer read-only commands until you know the state. Ask the person before
41
+ anything destructive: deleting, overwriting, rebooting, killing processes,
42
+ or changing system settings.
43
+ 3. Always pass a timeout (`--timeout` / `timeout_seconds`). Never start
44
+ interactive programs; they wait forever for input. Kache has no TTY yet.
45
+ For long jobs, start them in the background on the device (for example
46
+ `Start-Process`, `nohup ... &`, or a scheduled task) and poll.
47
+ 4. Check a device's health before heavy work. A hot GPU or full RAM shows in
48
+ `telemetry`.
49
+ 5. Pairing needs a person. You may start one (`kache pair` / `kache_pair`)
50
+ and must tell the person the 6-digit code. Only they can accept it on
51
+ the other device. Accepting, denying and revoking are not offered to
52
+ agents through MCP. Do not use `kache accept` unless the person
53
+ explicitly tells you to.
54
+ 6. When a device cannot be reached, run `kache doctor DEVICE --json` and
55
+ follow its `fix` fields. Do not guess.
56
+
57
+ ## Commands
58
+
59
+ All commands accept `--json`. JSON output is one object (pretty-printed)
60
+ with `"v"` (the schema version, currently 1). In JSON mode, errors are
61
+ `{"v":1,"ok":false,"error":"..."}` on stdout, with exit code 1.
62
+
63
+ | Command | Does |
64
+ |---|---|
65
+ | `kache devices` | this device and every paired device: status and health |
66
+ | `kache run TARGET [--as NAME] [--session S] [--timeout SEC] -- COMMAND` | send an order. TARGET is a device name, `a,b,c`, `all` (every paired device) or `this` |
67
+ | `kache feed [-d DEVICE\|all] [-n N] [-f]` | recent events; `-f` follows live. `--json` prints JSON lines, one event per line |
68
+ | `kache evidence ID -d DEVICE` | a failure report (always JSON) |
69
+ | `kache doctor [DEVICE]` | connectivity diagnosis with fixes |
70
+ | `kache discover` | Tailscale devices, and which run Kache |
71
+ | `kache pair ADDRESS` | ask a device to accept this one (a person must accept there) |
72
+ | `kache pending` | pair requests waiting on this device |
73
+ | `kache whoami` | this device: name, addresses, fingerprint, listen mode |
74
+ | `kache rename OLD NEW`, `kache remove NAME` | manage your device list |
75
+ | `kache trusted`, `kache revoke NAME` | who may command this device |
76
+ | `kache listen [all\|tailscale\|IP]` | which networks this gatekeeper answers on |
77
+ | `kache name NEW` | rename this device |
78
+ | `kache agent`, `kache service install`, `kache stop` | run, autostart, stop the gatekeeper |
79
+ | `kache mcp` | MCP server on stdio |
80
+ | `kache` | the interactive Kache terminal (for people) |
81
+
82
+ ### `kache run ... --json`
83
+
84
+ ```json
85
+ {"v": 1, "ok": false, "command": "python train.py", "label": "claude-code",
86
+ "results": [
87
+ {"device": "desktop", "exit": 1, "duration": 0.84, "cwd": "C:\\proj",
88
+ "evidence": "E-3f2a9c", "summary": "A Python module is missing on this device.",
89
+ "rejected": null, "timed_out": false,
90
+ "stdout": "...", "stderr": "...ModuleNotFoundError: No module named 'torch'\n",
91
+ "evidence_report": {"...": "see Evidence below"}},
92
+ {"device": "pi", "exit": null, "error": "cannot reach pi (...)", "unreachable": true}
93
+ ]}
94
+ ```
95
+
96
+ - `results` always holds one entry per target, in target order.
97
+ - `exit` is `null` when the device could not be reached.
98
+ - `rejected` is set when the gatekeeper refused the order before running
99
+ it, for example a command that is not installed on Linux or macOS, or a
100
+ Kache version mismatch.
101
+
102
+ ### `kache devices --json`
103
+
104
+ ```json
105
+ {"v": 1, "devices": [
106
+ {"name": "desktop", "this_device": false, "online": true,
107
+ "address": "100.81.247.101", "addresses": ["100.81.247.101", "192.168.50.152"], "port": 7722,
108
+ "fingerprint": "SHA256:...", "os": "Windows 11", "kache": "0.1.0", "trusts_you": true,
109
+ "telemetry": {"cpu": 12.5, "cpu_temp": null, "ram": 41.0, "ram_used": 13.1, "ram_total": 31.8,
110
+ "disk": 63.2, "uptime": 86400, "os": "Windows 11", "kache": "0.1.0", "time": 1760000000.0,
111
+ "gpus": [{"name": "NVIDIA GeForce RTX 4060 Ti", "temp": 54, "util": 97,
112
+ "mem_used": 5.2, "mem_total": 8.0}]},
113
+ "error": null}]}
114
+ ```
115
+
116
+ - `cpu`, `ram` and `disk` are percentages; memory is in GiB.
117
+ - `cpu_temp` is in °C, or `null` where the OS does not expose it (Windows).
118
+ - `trusts_you: false` means the other device removed this one. Pair again.
119
+
120
+ ### Feed events (`kache feed --json`, `kache_feed`)
121
+
122
+ One JSON object per line. All of them have `v`, `kind`, `time` (unix
123
+ seconds), `device` (the gatekeeper that saw it) and `device_fp`. Events
124
+ replayed from history on connect have `"replay": true`.
125
+
126
+ | kind | extra fields |
127
+ |---|---|
128
+ | `order` | `id`, `cmd` (exact command), `sender` {`name`, `label`, `via` (`cli`/`terminal`/`mcp`), `fingerprint`, `addr`}, `cwd`, `session` |
129
+ | `done` | `id`, `cmd`, `sender`, `exit`, `duration`, `session`; `evidence` + `summary` on failure; `rejected`; `cancelled: true` when stopped |
130
+ | `evidence` | `id` (E-...), `order` (order id), `cmd`, `sender`, `summary`, `exit` |
131
+ | `pair-request` | `id` (P-...), `name`, `os`, `fingerprint`, `addr`, `code` |
132
+ | `paired` | `name`, `fingerprint`, `addr` |
133
+ | `pair-declined` | `name`, `fingerprint`, `reason` |
134
+ | `unpaired` | `sender`, `text` |
135
+ | `unreachable` | `text` (from the CLI or MCP, when a device's feed could not be opened) |
136
+
137
+ ### Evidence reports
138
+
139
+ ```json
140
+ {"id": "E-3f2a9c", "time": 1760000000.0, "device": "desktop",
141
+ "order": {"id": "O-1a2b3c", "cmd": "python train.py", "sender": {"...": "..."}, "cwd": "C:\\proj"},
142
+ "exit": 1, "duration": 0.84, "summary": "A Python module is missing on this device.",
143
+ "hints": [{"kind": "python-module", "text": "The Python module 'torch' is not installed on this device."}],
144
+ "stdout_tail": ["..."], "stderr_tail": ["..."], "command_found": true,
145
+ "system": {"os": "Windows 11", "machine": "AMD64", "python": "3.13.1", "kache": "0.1.0",
146
+ "shell": "powershell.exe", "telemetry": {"...": "..."}, "top_processes": [{"...": "..."}]}}
147
+ ```
148
+
149
+ Hint kinds: `command-not-found`, `permission`, `disk-full`,
150
+ `python-module`, `network`, `port-in-use`, `python-exception`,
151
+ `out-of-memory`, `signal`, `cancelled`.
152
+
153
+ ## Exit codes (`kache run`)
154
+
155
+ | code | meaning |
156
+ |---|---|
157
+ | 0 | the order succeeded on every target |
158
+ | n | the order's own exit code (the highest one across several devices) |
159
+ | 1 | Kache error: bad input, gatekeeper not running, pairing refused... |
160
+ | 2 | at least one device could not be reached |
161
+ | 77 | the device does not accept orders from this one (not paired) |
162
+ | 124 | `--timeout` expired; the order was stopped on the device |
163
+ | 126 | refused: Kache versions differ |
164
+ | 127 | refused: the command does not exist on that device (Linux/macOS; on Windows a missing command exits 1 with a `command-not-found` hint) |
165
+
166
+ ## MCP
167
+
168
+ Add Kache to any MCP client. In Claude Code:
169
+ `claude mcp add kache -- kache mcp`
170
+
171
+ | Tool | Arguments |
172
+ |---|---|
173
+ | `kache_devices` | none |
174
+ | `kache_run` | `target`, `command`, optional `session`, `timeout_seconds` (default 120, max 3600) |
175
+ | `kache_feed` | optional `device` (default `all`), `limit` (default 30, max 60) |
176
+ | `kache_evidence` | `id`, `device` |
177
+ | `kache_doctor` | optional `device` |
178
+ | `kache_discover` | none |
179
+ | `kache_pending` | none |
180
+ | `kache_pair` | `address` |
181
+
182
+ - Results are the same JSON as the CLI, in `structuredContent`, with a
183
+ text copy in `content`.
184
+ - Each output stream keeps its last 60,000 characters per device; anything
185
+ cut is reported in `stdout_truncated` / `stderr_truncated`.
186
+ - The default session is `mcp-<client name>`, so `cd` persists across
187
+ calls.
188
+
189
+ ## Files (read-only for agents)
190
+
191
+ `kache whoami` prints the state folder: `%LOCALAPPDATA%\kache` on Windows,
192
+ `~/.local/share/kache` on Linux, `~/Library/Application Support/kache` on
193
+ macOS, or `$KACHE_HOME` if set.
194
+
195
+ | file | contents |
196
+ |---|---|
197
+ | `feed.jsonl` | every event this gatekeeper emitted, one JSON per line (rotated at 5 MB to `feed.old.jsonl`) |
198
+ | `evidence/E-*.json` | evidence reports |
199
+ | `devices.json` | devices this one commands: addresses, port, pinned fingerprint |
200
+ | `trusted.json` | devices allowed to command this one |
201
+ | `device.json` | this device's name, port and listen mode |
202
+ | `device_key` | this device's private key. Never read, copy or print it |
203
+
204
+ ## Troubleshooting
205
+
206
+ | Symptom | Cause / fix |
207
+ |---|---|
208
+ | `cannot reach X (... refused)` | the device is on, but Kache is not running there: `kache service install` on it |
209
+ | `cannot reach X (... timed out)` | the device is off or asleep, on another network without Tailscale, or a firewall drops port 7722 |
210
+ | `answered with a different key` | the device was reinstalled, or this is not the same device. Remove it and pair again; never ignore this |
211
+ | exit 77 | that device no longer trusts this one: pair again |
212
+ | a command works by hand but not through Kache | Kache uses a non-interactive shell without your profile (PowerShell `-NoProfile`). Use full paths or set variables in the command |
@@ -0,0 +1,17 @@
1
+ """Kache: one terminal to command all your devices.
2
+
3
+ Every device runs a gatekeeper agent (an SSH server with its own rules);
4
+ the Kache terminal pairs devices, shows their health, sends orders to one
5
+ device or all of them, and keeps a live feed of every order anyone sends.
6
+ """
7
+
8
+ __version__ = "0.1.0"
9
+
10
+ # Bumped whenever devices running different Kache versions could
11
+ # misunderstand each other. The gatekeeper refuses orders from other
12
+ # protocol versions instead of guessing.
13
+ PROTOCOL = 1
14
+
15
+ # Version of the JSON shapes scripts and AI agents read (feed events, CLI
16
+ # --json output, MCP tool results). Every feed event carries it as "v".
17
+ SCHEMA = 1
@@ -0,0 +1,3 @@
1
+ from .cli import main
2
+
3
+ main()