ASAysha Shafiq
← All project blogs

How I brought Textual apps to SSH with Textish

I liked how Charmbracelet's Wish made terminal apps available over SSH, but Python's Textual ecosystem had no equivalent. I built Textish to fill that gap.

STAT_01One-line API for serving Textual apps over SSH
STAT_02Four concurrency models explored
STAT_03Fresh app state for every connection

I wanted Wish for Python

I had used Charmbracelet’s Wish in Go and loved the idea: take a terminal application and make it available to anyone with an SSH client. I wanted the same experience for Python’s Textual framework, but nothing like it existed.

So I built Textish. A developer can pass in a Textual app, call serve(), and give every SSH connection its own live interface without asking the user to install the app locally.

The simple API hid a concurrency problem

Every connected user needs independent input, screen state, terminal dimensions, and application state. My first designs created a process, subinterpreter, or thread for each connection. I built and compared all three because each offered a different kind of isolation.

They also added overhead that did not match the workload. These apps spend most of their time waiting for terminal input or sending updates over the network, so the core problem was I/O concurrency, not raw CPU parallelism.

I matched the architecture to the workload

The version I shipped runs every session as an asyncio task on one shared event loop. Each connection receives a fresh Textual app and SSH driver, while imported code and infrastructure stay shared.

That design kept the isolation users could actually observe without paying for a new Python runtime per session. I added connection limits, startup throttling, and backpressure for slow clients so one session could not overwhelm the server.

Textish taught me to choose architecture from the shape of the workload. The most isolated option was not automatically the best one; the right design was the lightest model that kept every user’s application state independent.

Next articleHow I built an AI-powered Slack bot to answer student questions