Show HN: Pythond, Persistent Python daemon where state survives across calls

rangersui1 pts0 comments

pythond

pythond

sshd gives you a shell. pythond gives you Python.

Persistent Python daemon with named sessions. Code in, result out.<br>Variables, connections, and threads survive between calls.<br>Agents send code through WebSocket. Humans attach with a real terminal.<br>Same namespace.

pip install pythond

Python 3.10+. Three entry points:

CommandRoleLike<br>pythonddaemonsshd<br>pyshsession clientfunction call / attach<br>pyctldaemon controlsystemctl

Quick start

pyctl start<br>pysh new work<br>pysh run work "x = 42"<br>pysh run work "x + 1"<br># 43<br>pysh attach work

State persists

pysh run work "import sqlite3; db = sqlite3.connect('app.db')"

# ... 100 turns later ...

pysh run work "db.execute('SELECT count(*) FROM users').fetchone()"<br># (42,)

The connection never closed. The namespace is the workspace.

Async execution

# fire: thread, shares namespace<br>pysh fire work "model = train(X, y)"<br>pysh poll work abc123<br># {"cell_id":"abc123", "status":"done", "output":"..."}<br>pysh run work "model.score(X_test)" # model is there

# fork: child process (POSIX only), killable, pickles vars back<br>pysh fork work "results = expensive_search(params)"<br>pysh int work # SIGKILL the fork (POSIX)

Remote

AI agents can't hold SSH sessions open. The local daemon holds<br>the connection the agent can't hold.

# server<br>pip install pythond<br>pyctl start --listen 0.0.0.0:7984 --show-token<br># Non-loopback --listen auto-enables TLS<br># prints token and cert fingerprint

# client: self-signed server cert must be pinned before connecting<br># copy server ~/.pythond/tls/cert.pem to client as ~/server_cert.pem<br>pyctl pin ~/server_cert.pem<br>pythond daemon<br>pyctl connect work 10.0.0.5:7984 --tls<br>pysh run work "import platform; platform.node()"<br># remote-host-01

mTLS plus token

# client: generate client cert<br>pyctl cert<br># copy client ~/.pythond/tls/cert.pem to server as ~/client_cert.pem

# server: generate server cert, then trust client cert<br>pyctl cert<br># copy server ~/.pythond/tls/cert.pem to client as ~/server_cert.pem<br>pyctl trust ~/client_cert.pem<br>pyctl start --listen 0.0.0.0:7984 --show-token<br># Non-loopback --listen auto-enables TLS<br># cert is required and token is still required

# client: pin server cert, then connect (client cert sent automatically)<br>pyctl pin ~/server_cert.pem<br>pythond daemon<br>pyctl connect work 10.0.0.5:7984 --tls<br>pysh run work "x"

Write file, then exec

Complex code with quotes, f-strings, or SQL? Write a file, load it.<br>No escaping.

cat > /tmp/task.py

Two channels, one namespace

Agent channel (pysh run): Human channel (pysh attach):<br>code in, text out POSIX PTY / Windows WinPTY<br>no ANSI, no parsing tab completion, Ctrl-C<br>WebSocket protocol Ctrl-] to detach<br>\ /<br>same Python namespace

Two control surfaces

pysh run work "x = 1" # agent/script: one-shot command<br>pysh attach work # human developer: interactive attach<br>pyctl status # operator: daemon status

pyctl manages the daemon. pysh manages sessions.<br>No command secretly starts the daemon, creates a session, and attaches in one<br>step. pysh attach is a real PTY on POSIX/WSL. On native Windows it<br>uses WinPTY, which preserves execution and shared state but is not fully<br>byte-transparent for readline screen redraws. Use WSL when exact terminal<br>behaviour matters.

Commands

pysh new create session<br>pysh run "code" sync exec, raw output<br>pysh fire "code" async thread, shares namespace (can't kill C code)<br>pysh fork "code" async process (POSIX), killable, pickles vars back<br>pysh poll [cell_id] check async result<br>pysh int interrupt (fire=best effort, fork=kill)<br>pysh kill terminate session<br>pysh ls list sessions<br>pysh status JSON health<br>pysh vars JSON namespace names<br>pysh complete "text" JSON completion candidates<br>pysh attach human REPL (Ctrl-] to detach)

pyctl start [--listen H:P] [--tls] [--show-token] start daemon<br>pyctl stop stop daemon<br>pyctl status daemon endpoint metadata and liveness<br>pyctl connect [--tls] proxy to remote<br>pyctl disconnect drop remote<br>pyctl cert show/generate TLS cert<br>pyctl trust authorize client<br>pyctl pin verify server

pyctl cert prints the certificate path, key path,<br>fingerprint, and role hints: copy this cert to the server for<br>pyctl trust when this machine is a client, or copy it to the<br>client for pyctl pin when this machine is a server.

Protocol

WebSocket text frames. First line = command. After newline = code body.<br>Python code is never JSON-escaped.

ws.send("run work\nprint('hello')") # "hello"<br>ws.send("fire work\ntrain(epochs=50)") # {"cell_id":"..."}<br>ws.send("ls") # " work: alive"

Security

The security model mirrors SSH:

pythondSSH equivalent<br>token in daemon.jsonprivate key in ~/.ssh/<br>pyctl trust cert.pemadding a line to authorized_keys<br>pyctl pin cert.pemadding a line to known_hosts<br>authenticated clientlogged-in user

Once authenticated, a client has full access to all sessions — no per-session permission isolation. Same as SSH: once you log in, you are that user with all their permissions.

Not a sandbox: code runs with the daemon user's OS permissions.

ModeAuth<br>Local POSIXAF_UNIX...

pysh pyctl work cert daemon client

Related Articles