Documentation
kino-agent
The program on the hidden machine. Installation, configuration, service management, logs, and clean removal.
Repository: Samarthegde/kino-agent · Licence: GPL-3.0 · Language: Rust
A single static binary that keeps one outbound WebSocket parked at a relay and
bridges incoming sessions to the machine’s own sshd on 127.0.0.1:22. It
opens no ports, needs no privileges, and has no configuration beyond a handful
of flags.
What it does, in four steps#
- On start it opens a persistent control WebSocket to
/ws/control?agent_id=<id>and waits. - When a manager wants in, the relay sends a
new_connectionmessage down that channel. - The agent dials a fresh data WebSocket back
(
/ws/agent/data?session_id=<id>), opens a TCP connection to127.0.0.1:22, and pipes bytes between the two. - The relay splices that socket to the manager’s. From there it is a plain
SSH session, end-to-end between your client and
sshd.
The control channel is kept alive with 30-second pings; the agent reconnects every 5 seconds if the relay or the network drops.
Install#
Kino Cloud#
Add a machine (here or in the app) and run the command it gives you:
curl -fsSL https://kino.dpdns.org/install/<one-time-enroll-key> | sudo sh
That registers the machine, installs the service, and configures
--control-url + --agent-key. The agent then discovers its own relay and
rotates its own tokens. Nothing else to configure, ever.
Self-hosted relay#
Linux (systemd):
curl -fsSL https://raw.githubusercontent.com/Samarthegde/kino-agent/main/packaging/bootstrap.sh \
| sudo sh -s -- --relay-url wss://relay.example.com
Windows (elevated PowerShell):
$s = irm https://raw.githubusercontent.com/Samarthegde/kino-agent/main/packaging/bootstrap.ps1
& ([scriptblock]::Create($s)) -RelayUrl wss://relay.example.com
The installer prints the agent id it generated. Put that and the relay URL
into the host editor in Kino SSH Manager. Add --token <token> / -Token
<token> if the relay requires auth.
Re-running an installer is safe: it updates the binary and config in place, keeps the existing agent id and token, and restarts the service.
Run it in the foreground#
Useful for debugging. Same binary, every platform:
# Fixed relay:
kino-agent --relay-url wss://relay.example.com --agent-id my-agent
# Kino Cloud (relay discovered, tokens refreshed automatically):
kino-agent --control-url https://kino.dpdns.org --agent-key kca_…
Windows does not run an SSH server by default
The agent forwards to 127.0.0.1:22 on its own machine. Install and start
OpenSSH Server first, or every session fails at the last hop.
Configuration#
Every option is a flag or an environment variable; the flag wins.
| Flag | Env var | Description |
|---|---|---|
--relay-url |
KINO_RELAY_URL |
Relay base URL, e.g. wss://relay.example.com. Required unless --control-url is set, in which case it is the fallback. |
--control-url |
KINO_CONTROL_URL |
Controller base URL. The agent fetches the enrolled relays, latency-races the top few, parks on the fastest, and reports where it landed. Re-picks automatically if that relay dies. Needs --token or --agent-key. |
--agent-key |
KINO_AGENT_KEY |
Kino Cloud machine credential (kca_…). Traded for short-lived relay tokens at startup and refreshed at ~¾ of each token’s life. Makes --agent-id optional and --token unnecessary. Needs --control-url. |
--agent-id |
KINO_AGENT_ID |
Stable unique id for this host. Any string; a UUID is a good default. Optional with --agent-key, where the controller assigns it. |
--token |
KINO_TOKEN |
Bearer token when the relay requires auth: its static RELAY_TOKEN, or an agent-role token from a controller. |
--log-file |
KINO_LOG_FILE |
Append to a file instead of stdout. Set automatically for the Windows service, which has no console. |
| — | RUST_LOG |
error / warn / info / debug / trace. Defaults to info. |
Keep the agent_id stable — it is how the relay and the manager address this
specific machine. In Kino Cloud mode it is assigned once at install and
restated by the controller on every refresh, so there is nothing to track.
Where things live#
Linux#
| Binary | /usr/local/bin/kino-agent |
| Config | /etc/kino-agent/kino-agent.env — mode 0600, root-owned |
| Unit | /etc/systemd/system/kino-agent.service |
| Status | systemctl status kino-agent |
| Logs | journalctl -u kino-agent -f |
The unit is sandboxed: DynamicUser=yes, no capabilities, read-only
filesystem, syscall filter. The agent only ever dials out and connects to
127.0.0.1:22, so it is granted nothing else — and because the user is
dynamic, removing the service leaves no account behind.
Windows#
| Binary | C:\Program Files\kino-agent\kino-agent.exe |
| Config | C:\ProgramData\kino-agent\config.json |
| Logs | C:\ProgramData\kino-agent\kino-agent.log |
| Status | Get-Service kino-agent |
| Tail logs | Get-Content 'C:\ProgramData\kino-agent\kino-agent.log' -Tail 50 -Wait |
The token is more exposed on Windows
It ends up on the service command line and in config.json, both readable
by local users of that machine. It is a relay-access secret, not an SSH
credential — but scope it: use a controller-issued token bound to this
agent id (or Kino Cloud mode, where the credential only ever buys 24-hour
tokens) so a leak exposes this host’s relay slot and nothing else.
Uninstalling#
Linux#
sudo systemctl disable --now kino-agent
sudo rm -f /etc/systemd/system/kino-agent.service
sudo rm -rf /etc/kino-agent # config + the agent key
sudo rm -f /usr/local/bin/kino-agent
sudo systemctl daemon-reload
systemctl status kino-agent should then report that the unit could not be
found. Nothing else needs cleaning up — DynamicUser=yes means there is no
leftover account or home directory, and the agent writes nothing outside those
three paths.
Windows (elevated PowerShell)#
Stop-Service kino-agent
sc.exe delete kino-agent
Remove-Item -Recurse -Force "$env:ProgramFiles\kino-agent"
Remove-Item -Recurse -Force "$env:ProgramData\kino-agent"
Then delete the machine#
Uninstalling only removes software. The machine still exists in the
controller, counts against your quota, and — this is the part that matters —
its kca_… agent key stays valid until you delete it.
Remove it on the Machines page, or:
curl -X DELETE https://kino.dpdns.org/api/machines/<agent-id> \
-H "Authorization: Bearer kck_…"
If you are uninstalling because the box was compromised, delete the machine first
Anyone who copied /etc/kino-agent/kino-agent.env before you wiped it can
keep refreshing relay tokens for as long as the machine record exists.
Deleting it kills the refresh immediately.
Building from source#
Needs stable Rust (edition 2024, i.e. ≥ 1.85):
cargo build --release # target/release/kino-agent
TLS is rustls with the ring backend, so
there is no OpenSSL system dependency and the binary is portable across
distros. Cross-compiling for Windows from Linux:
rustup target add x86_64-pc-windows-gnu
sudo apt-get install -y gcc-mingw-w64-x86-64
cargo build --release --target x86_64-pc-windows-gnu
CI builds and lints Linux x86_64, Linux aarch64, and Windows x86_64 on every
push, and attaches binaries to a GitHub release on any v* tag.
Security notes#
- The relay routes; it does not read. SSH is end-to-end encrypted between
your client and
sshd. A relay operator cannot read your session or credentials — but a malicious relay could refuse or redirect connections. Run your own, or trust the operator. - Host-key verification still applies. The manager pins each agent host’s
SSH fingerprint by agent id, so a swapped backend is caught the way
known_hostscatches it. - The agent needs no privileges. See the sandbox notes above.
- Use a relay with auth for anything public. On an open relay, anyone who
can reach it and knows an agent id can open a transport to that host, leaving
your
sshdas the only gate. Keepsshdlocked down regardless.
Found a vulnerability? Report it privately via the repository’s
CONTRIBUTING.md rather than opening a public issue.