- Go 100%
Root is now clean: cmd/termkey (main) + internal/{theme,corpus,store,tui}.
typing engine and race hub stay with the app in tui — they share the app
model and the engine's internals, so a split would only leak API.
|
||
|---|---|---|
| deploy | ||
| internal | ||
| .gitignore | ||
| go.mod | ||
| go.sum | ||
| LICENSE | ||
| README.md | ||
termkey
A typing speed test you play over SSH — no install, no signup. Point your terminal at the server and you drop straight into a full-screen TUI: timed tests, word counts, book quotes, live WPM, leaderboards, and real-time multiplayer races.
ssh termkey.xyz
Your SSH key is your identity — no passwords, nothing to install, nothing to sign up for. Anonymous play works immediately; claim a username to save stats and appear on the leaderboards.
Features
- Modes — timed (15/30/60s), word count (10/25/50), or a random public-domain book quote
- Live feedback — WPM, accuracy, and a per-second WPM graph after every test
- Accounts — your SSH public key is your login; link extra machines with a one-time code
- Stats — personal bests, averages, accuracy trends, recent history
- Leaderboards — per mode and duration, daily and all-time
- Races — real-time multiplayer with multi-room lobbies and live progress bars
- Moderation — automatic username filtering and an admin ban screen
Navigation: arrows / enter to move, b leaderboards, r races, s stats, esc back.
Run it yourself
Requires Go 1.26+.
git clone https://git.evileko.dev/evileko/termkey
cd termkey
go run ./cmd/termkey -addr :2222 -data ./data
ssh -p 2222 localhost
-addr— listen address (default:2222)-data— state directory for the SQLite database and generated SSH host key
Build a static binary (pure Go — SQLite included, no cgo, no runtime deps):
CGO_ENABLED=0 go build -o termkey ./cmd/termkey
Deploy
termkey is a single static binary plus a SQLite database. Run it under systemd;
hardened unit files are in deploy/:
deploy/termkey-test.service— port 2022, leaves your admin sshd untouched (ssh -p 2022 host)deploy/termkey.service— port 22, so baressh hostworks
For the bare-port setup, termkey needs port 22, so move your admin sshd to
another port first — and verify you can log in on the new port before you
disconnect. Point your ~/.ssh/config at the new admin port afterward.
DNS is just an A record → your server's IP. Behind Cloudflare, set it
DNS-only (the proxy doesn't forward SSH). State (database + generated SSH
host key) lives in the -data directory and survives restarts.
Moderation
- Admins are identified by SSH key: put admin public keys (authorized_keys
format, one per line) in
<data>/admin_keys. Admins get an[m]moderation entry in the menu — filter the user list, ban and unban. - Username filtering is automatic and leetspeak-aware via go-away. Banned users are dropped from the leaderboards and blocked on connect.
How it works
Built on the charm stack — wish
(SSH server), bubbletea (TUI), and
lipgloss (styling) — with
modernc.org/sqlite for storage (pure Go). It's a
single package main, no internal packages.
All timing happens server-side and pastes are ignored, so results reflect real
keystrokes. termkey only ever runs the TUI: it never spawns a shell and
registers no exec, SFTP, or port-forwarding handlers, so a connecting user can't
reach anything but the game. The systemd unit sandboxes it further
(DynamicUser, seccomp, read-only filesystem, dropped capabilities).
License
GPL-3.0 © EvilEKo