Skip to content

About

On-demand, verified software packages for the PostHog browser terminal

Resources

Code of conduct

Security policy

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

PostHog terminal assets

Optional software for the browser terminal in PostHog/posthog. These archives are downloaded on first use and are separate from the terminal's boot image and bundled Unix tools.

Tool Version Runtime
Node.js 22.23.2 Alpine Linux 3.22 x86, musl
Neovim 0.11.1-r1 Alpine Linux 3.22 x86, musl and LuaJIT
Doom (Freedoom) 0.13.0-3 Static fbDOOM, Linux framebuffer, PS/2 input and serial multiplayer
pi 0.87.1 Node.js 22.19 or later
sl 5.02-r1 Alpine Linux 3.22 x86, musl and ncurses
cmatrix 2.0-r2 Alpine Linux 3.22 x86, musl and ncurses
figlet 2.2.5-r3 Alpine Linux 3.22 x86, musl
nyancat 1.5.2-r1 Alpine Linux 3.22 x86, musl

The terminal runs 32-bit Linux in v86. The archives include runtime dependencies, so installation needs no package manager or network connection inside the VM. pi includes Alpine x86 builds of fd and ripgrep for local file discovery and search. pi runs with PI_OFFLINE=1 and defaults to Claude Opus 5 through the PostHog provider in pi-provider.mjs. Use /model to choose Opus 5, Sonnet 5, Sonnet 4.6, or Haiku 4.5. The browser bridge enforces an idle timeout; active generations can run longer than two minutes. The provider carries Anthropic Messages streams through the terminal's /posthog/.ai bridge using the signed-in PostHog session. It keeps gateway credentials outside the VM and uses pi's upstream message and tool-call handling. External login and package downloads still require general networking, which the VM does not provide.

Build

Use Python 3.13 or later:

python3 build.py

The builder checks every upstream download against the pinned integrity value in recipes/. It never runs npm lifecycle scripts. Node.js and its shared libraries come from Alpine's official x86 packages. pi comes from the published npm package and its dependency lockfile, with missing upstream lockfile integrity values pinned from the npm registry. Type declarations, type-only packages, and source maps are omitted from the pi archive. Upstream runtime code remains unchanged; the PostHog provider extension and launchers are maintained here.

The Node launcher configures its own library and ICU data paths. The guest must provide /lib/ld-musl-i386.so.1; the PostHog terminal links its bundled musl loader there. This lets Node execute directly and preserves process.execPath for child processes.

manifest.json records compressed and expanded sizes, SHA-256 hashes, commands, and tool dependencies. Tar entries have fixed modes, timestamps, and ownership, and gzip has a fixed timestamp.

Validate

From a prepared PostHog checkout, run:

.codex/with-flox node /path/to/terminal-assets/smoke.mjs "$PWD"

The smoke test uses the checkout's v86 package and existing kernel and firmware. It checks Node execution, Unicode regex data, child Node processes, and pi's version and help output in a 512 MiB VM. The terminal's live Storybook story exercises browser downloads, verification, caching, and installation.

For the classic terminal commands, run python3 build.py --only classics, then .codex/with-flox node /path/to/terminal-assets/smoke.mjs "$PWD" --classics from a prepared PostHog checkout. The smoke test checks banner fonts and piped input, bounded Nyan Cat animation, ncurses rendering, and quitting Matrix in the Linux guest. Each command has a separate archive with its own runtime libraries, so it does not need Node.js or another optional package. The sl launcher enables Ctrl+C with -e; cmatrix exits with q or Ctrl+C, and nyancat exits with Ctrl+C. The figlet launcher sets its bundled font directory and accepts upstream options and piped text. recipes/classics-packages.json pins the Alpine packages, hashes, licenses, and corresponding source recipes. The archives include the upstream license notices, including nyancat's copyright notice in its source file.

For Neovim, run python3 build.py --only neovim, then .codex/with-flox node /path/to/terminal-assets/smoke.mjs "$PWD" --neovim from a prepared PostHog checkout. The smoke test checks file saves, Lua modules, syntax files, help documents, and child Neovim processes without installing Node.js. The nvim launcher sets the bundled library, Lua module, terminfo, and Neovim runtime paths. Like Node.js, Neovim requires the guest's /lib/ld-musl-i386.so.1 link. recipes/neovim-packages.json pins the Alpine packages, hashes, licenses, and source recipes. The archive includes Neovim's runtime and help files, LuaJIT, Lua modules, shared libraries, and upstream license notices. Plugins that require downloads cannot install inside the guest because it has no general networking.

Publish and add tools

Commit archives as ordinary Git files, not Git LFS pointers. GitHub raw serves them with browser-compatible CORS headers; GitHub release downloads do not provide the same browser access. Keep each compressed archive below GitHub's 100 MiB file limit.

After testing a new archive, push a commit here, then update frontend/src/scenes/terminal/terminal-packages.json in PostHog/posthog. Use the full commit hash in its baseUrl and copy the matching package metadata. The frontend must pin both the URL and checksum; it must never download an executable manifest from a mutable branch. A package archive extracts into /opt/posthog-packages/<id>-<version>/ and must not contain paths outside that directory.

For another tool, add a pinned recipe and builder here, then register its commands and dependencies in the frontend manifest. The browser fetches and verifies archives; the guest installer serializes installs and only publishes a completed directory.

Licensing and sources

Upstream packages retain their licenses. licenses/ contains the Node.js dependency notices, pi's MIT license, musl's copyright notice, SQLite's public-domain notice, and the GCC runtime exception and GPL text for libgcc and libstdc++. The builder includes these notices in each archive; npm dependency license files are also preserved. recipes/licenses.json records notice source URLs and hashes.

Node and bundled dependency sources: Node.js v22.23.2. Alpine build recipes and corresponding source URLs: Alpine aports 3.22. GCC runtime sources: GCC 14.2.0. pi sources: pi v0.87.1. The exact binary packages, versions, and hashes are in recipes/node-packages.json and recipes/pi-lock.json; companion tool sources and hashes are in recipes/pi-tools.json.

Doom and framebuffer

python3 build.py --only doom builds the standalone Doom archive from checksum-pinned binaries in binaries/. It includes fbDOOM, Freedoom phase 1, the launcher, and license notices. The launcher uses virtual console 5 and sends display on and display off to the host. The guest must provide that display command and framebuffer and input drivers. Closing the host display sends Ctrl+C to the serial foreground process group; the launcher restores the console when interrupted.

images/linux-fb-bzimage.bin is the Linux 5.6.15 framebuffer kernel, with SHA-256 33ca60bd4832f0cf202845fa7ac1a60776c0215e8a20d21a3f07d3722f99e415. It retains the stock terminal initramfs and adds Bochs framebuffer and PS/2 drivers. sh recipes/display/build.sh rebuilds the kernel and static fbDOOM binary with Docker. The kernel and game data originate from PostHog/posthog#103893, commit 0b3b63d8c4e5f5e3703a558fd87b5353556ca95d. The build pins fbDOOM to 17280163bc95e5d954d2efaa0633489b763b4cd1 and musl to 1.2.5. binaries/freedoom1.wad.gz contains the unmodified phase 1 WAD from the Freedoom 0.13.0 release. See licenses/display-NOTICE.txt for source and license details.

The fbDOOM binary is rebuilt with recipes/display/Dockerfile.doom and mouse.patch, which reads PS/2 packets from /dev/input/mice. Mouse movement turns the player without moving forward or backward, and the left mouse button fires. controls.patch enables configuration loading and saving and aligns Space and Ctrl bindings with the console input driver. The caller can supply WASD bindings with -config; existing configuration files remain editable.

Run .codex/with-flox node /path/to/terminal-assets/smoke-doom-controls.mjs "$PWD" from a prepared PostHog checkout to record a game and verify movement, strafing, turning, firing, and use commands.

Multiplayer

netplay.patch enables Chocolate Doom 2.1.0's network code, which fbDOOM omits; Dockerfile.doom copies those sources from a pinned commit. The guest has no network card, so net_serial.c sends packets over /dev/ttyS2, the third serial port, and the browser relays them to the other players. Each SLIP frame starts with a peer byte: guest frames name the destination and browser frames name the source. Peer 0 is the game host, peers 1 to 254 are clients, and peer 255 carries text control messages. The guest sends host, join <code>, and launched; the browser replies with room <code>, joined, or error <message>. net_lobby.c replaces the textscreen lobby with a text screen on the active virtual console. The host presses Space to start, or passes -nodes <n> to start when that many players have joined.

The host runs doom -server -deathmatch, and other players run doom -connect <code> with the room code from the lobby. Run .codex/with-flox node /path/to/terminal-assets/smoke-doom-deathmatch.mjs "$PWD" from a prepared PostHog checkout to connect two VMs through a local relay and verify that the host records the client's movement in a deathmatch.

About

On-demand, verified software packages for the PostHog browser terminal

Resources

Code of conduct

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages