Terminal-native orbital-mechanics rocket simulator. A take on Kerbal Space
Program that lives in your terminal, distributed as a single static Go binary.
Inspiration
I love Kerbal Space Program, I love TUI Applications. I decided the two should be married for when I'm bored and have a terminal available.
The Game
By default, you spawn in an Apollo-style SIV-B in a 500km circular orbit. Switch targets to Moon (press t to switch and T to clear). Plant a Hohmann transfer + inclination change (press H). Or, fly it all manually. See Controls & flight guide for a quick tour, a launch walkthrough, and the full list of keys.
Plan transfers between planets and moons, fly your rocket off the pad and into orbit
by hand, rendezvous and dock, stage away spent boosters, and bring a capsule
home under parachute — all drawn with braille-canvas graphics and driven from
the keyboard. No mouse required, no GUI, just a single binary in your terminal.
Under the hood it's a real orbital-mechanics sim: gravity, fuel, atmospheric drag, and timing all
matter, the way they do in real life. Unlike KSP (without mods), the default game renders our solar system. Launches are hard, they take 7.5km/s for LEO - just like real life. The moon is inclined, the earth is tilted on its axis. To match the real solar systems, there are real life vessels with accurate loadouts of thrust.
Recently introduced: a familiar 1/10th-scale system named Lumen; home to a familiar planet, Kernel, with two moons, Cursor and Glyph - and a nearby red planet, Rust. A Lumen-specific vessel is scaled to that environment. It's a little rough appearance-wise as no textures have ported over to that system, but vessels fly and hit familiar Delta-V marks for launch, Cursor (Mun) transfers, etc. From a default game launch press TAB to cycle through the available solar systems until you reach Lumen. Press F to cycle to the planet Kernel, then press N to spawn a craft there.
Each vessel is bound for its lifetime to the system it spawns in — the simulator flies every vessel against its own system and the camera follows your active vessel's system, so a craft parked in Sol keeps orbiting while you fly in Lumen (TAB stays a browse-only camera toggle). Adding Lumen also changed the body catalog, so the on-disk save format moved to v8; older saves auto-migrate on load.
When your live trajectory is heading into a body's sphere of influence, the orbit map draws the full encounter arc ahead of arrival — entry, perilune, and exit — with a Perilune ⊕ marker and an always-on SOI PASS chip showing the altitude and time to closest approach. No need to target the body first. Every orbital marker (apoapsis, periapsis, nodes, closest approach, maneuver nodes) is now a single colored glyph: shape is type, color is type, brightness is state (nominal / counterfactual / alarm). To read an encounter up close, press f to focus the body it passes — the camera fits to the body's sphere of influence so the capture curve fills the canvas, and from there the view is yours: +/- zoom holds until you change focus, view, or system.
Recently introduced: an in-game Vehicle Assembly Building (VAB). From the
main view press Esc → [Build (VAB)] to design your own rocket from fine
parts — compose engines, fuel tanks, command cores, antennas, and structure
into stages, stack the stages, mark dock seams for nose payloads, and watch a
live Δv / TWR / mass readout as you build. Editing is in place,
maneuver-form style: you open onto a stage, press ←/→ on its engine or tank
row to swap that part within its kind (the engine leads the stage's chemistry),
+/- to add more, and enter on a shipped catalog part (an S-IVB, a Falcon
booster) to crack it open into editable components and tweak from there. Set
a tΣ Δv target and a tank row will hint how many tanks close the gap.
Save a design and it shows up in the spawn form (n) alongside the built-in
craft, so you design once and launch many. Designs are portable files under
~/.config/terminal-space-program/designs/ (KSP .craft-style); drop one into
the sibling loadouts/ overlay dir to share it as a mod. Multiple engines in a
stage combine honestly (thrust adds, Isp is the thrust-weighted blend) and a
stage holds a single fuel chemistry, so everything you build flies on the same
honest physics as the shipped fleet.
Recently introduced: a multi-save system. Esc → [Save Game] / [Load Game]
opens one browser for every save — your named saves (create one with the
+ New save… row, or overwrite an existing one), plus the managed quicksave
lane (F5 to save, F9 to instant-load, no confirm) and three rotating
autosaves (a real-time interval, 5 minutes by default and tunable in Settings,
plus one on quit). The old single save.json is auto-imported as a named save
the first time you launch this version and is left in place, untouched, as a
downgrade safety net. Saves are flat, independent files under
~/.local/state/terminal-space-program/saves/ ($XDG_STATE_HOME if set) — no
save-schema change, so existing saves load exactly as before.
The visual foundation was lifted (with MIT attribution) from
furan917/go-solar-system. See
NOTICE.md for the full acknowledgments list.
# Linux x86_64
curl -L https://github.com/jasonfen/terminal-space-program/releases/latest/download/terminal-space-program-linux-amd64.tar.gz | tar xz
./terminal-space-program
Replace linux-amd64 with linux-arm64, darwin-amd64, darwin-arm64, or
windows-amd64 (use the .zip variant on Windows).
No Go toolchain, no libc dance. CGO_ENABLED=0 static binaries.
Build from source
git clone https://github.com/jasonfen/terminal-space-program
cd terminal-space-program
go build ./cmd/terminal-space-program
./terminal-space-program
Requires Go 1.24 or newer.
Command-line flags
By default the game opens with a vessel in low Earth orbit. Flags let you jump
straight to a different start — a system, a body to orbit or launch from, an
orbit altitude and inclination, or a named launch site:
--version and the --list-* discovery flags print and exit. See the
command-line reference for every flag, units, defaults, and
more examples.
Multiplayer (ssh)
Host a shared session straight from your own game — no separate server:
terminal-space-program --serve # play AND accept guests (port 23234)
terminal-space-program serve invite dave # mint a one-time invite code
ssh -p 23234 your-host # guests join from any terminal
You can also start hosting from inside a running game — no restart, no
--serve: open the O session roster and press h (press it again to stop,
which drops guests but keeps everyone's progress).
Running the session lives on that roster too. The host mints (i) and revokes
(r) invite codes and removes players (x), and can share the load: p
promotes a player to admin, who then handles invites and removals without
being able to promote anyone else, remove another admin, or remove the host.
u restarts the server — everyone is warned, drained with their progress
saved, and reconnects a moment later; on a box set up to self-update, the same
key adopts a newer release when one is published.
Guests enroll once with the invite code (their ssh key becomes their identity)
and get their own persistent space program on your machine. Everyone warps
time independently: other players appear as dim "ghost" craft — with their
orbits drawn on your map — evaluated at your clock, and the O session
roster shows who's ahead or behind. From that roster, t targets one of their
craft (with more than one in your system, it opens a picker so you aim at the
right ship), s sync-warps you forward to a player's time to fly formation,
and vspectates a player —
fitting the camera to their ghost orbit and following it so you can watch their
burns play out. Warp clamps, planted burns, and SOI transitions are all honored
en route.
Closing the distance takes orbits of coasting, so warp there together:
w on a player's roster row arms a rendezvous warp toward your predicted
closest approach with them. They get a persistent prompt on their main screen —
y joins — and from that moment your warps are rate-locked all
the way to the encounter, planted burns firing en route. Either side cancels
with / — and only with /: the manual warp keys and the auto-warp
toggle (G / [»Burn]) are inert while the coast runs (it owns the
rate), so a stray . or G can't tear the rendezvous down. If the encounter drifts off the committed approach mid-coast, a
warning chip says so. You arrive at closest approach at 1× — already coupled —
and slide straight into the final approach.
The agreement doesn't end there. Arriving hands the ship back so you can
brake at closest approach, but the two of you stay time-locked through the
whole terminal phase — the burns, the waiting, the last few hundred metres —
so nobody has to sit at 1× while the other lines up. In that phase the player
who proposed the rendezvous flies the pair's clock; whoever joined rides
copilot, and can brake the pair (,) or release back to following (.) but
never push it faster. Either side burning holds you both at the burn cap. The
RENDEZVOUS block on the map always says whose clock you're on and what's
holding it, so a warp key that does nothing always explains itself. It ends
when you dock, or when either of you presses / — flying 100 km wide to set up
a better approach won't drop it.
Proximity does the rest for players with no agreement: come within 35 km of a
player you're synced with, closing slower than 100 m/s, and your time-warp
couples to theirs, so neither can skip ahead during the approach — a
TIME LOCK line says who and at what rate. The session roster's RANGE column
shows how close you already are. Dock your craft to theirs and you fly one
shared stack — the guest can U undock their own component at any time, and
the pilot can hand the whole stack over with Jtransfer control (refused
if they aren't in the session — someone has to be there to take the stick).
Nobody ever gets stuck: the pilot's U releases a partner's craft even if
they've disconnected, and it's waiting for them, safed, when they come back;
and if the pilot is the one who's gone, the guest riding their stack can
press J right back to take the stick themselves — an empty seat needs
nobody's permission.
Coordinate it all without leaving the sim: ~ opens chat on the flight
view. Type and press enter to broadcast to the session; a leading @handle
sends a private line (tab completes the handle against who's online — a typo
refuses to send rather than broadcasting by accident). The sim keeps running
while you type, esc bails, and messages ride the map as transient chips for
~30 seconds — chat is live coordination, not a message board: nothing is
stored, and it never depends on your craft's CommNet link.
Custom vehicles
Vehicle loadouts and stage parts are data, not code. Drop a .json file in
~/.config/terminal-space-program/loadouts/ (or under $XDG_CONFIG_HOME) to add
your own loadouts and parts, or override a built-in by reusing its id. A loadout
is an ordered list of part references; a part is one atomic stage. Run
terminal-space-program --list-loadouts to see the merged catalog and confirm
yours loaded — a malformed file is skipped with a warning, never failing the rest.
See the command-line reference for the format.
Package sessiondir is the server-side session directory for multiplayer (v0.27 S3, ADR 0034): a versioned session.json holding the player roster, outstanding invite codes, and the body-catalog hash, plus one save envelope per enrolled player (current save.SchemaVersion).
Package sessiondir is the server-side session directory for multiplayer (v0.27 S3, ADR 0034): a versioned session.json holding the player roster, outstanding invite codes, and the body-catalog hash, plus one save envelope per enrolled player (current save.SchemaVersion).
Package settings holds the player's global UI preferences — currently the default visibility of each orbit-screen Chip — persisted to a standalone settings.json under $XDG_CONFIG_HOME/terminal-space-program/.
Package settings holds the player's global UI preferences — currently the default visibility of each orbit-screen Chip — persisted to a standalone settings.json under $XDG_CONFIG_HOME/terminal-space-program/.