Term50 is a terminal emulator for BlackBerry 10.
It uses libghostty-vt as its terminal parser/state model and renders the visible grid directly through the BB10 Screen API + FreeType.
It runs multiple shells as tabs, and is configured and scripted in Lua (~/.term.lua).
The current release requires OS version >= 10.3.
You need the BlackBerry 10 NDK (for qcc and the packager) and Nix (for the bbnix userland, bundled Fen CLI, and bundled font staging).
Everything links against headers + libraries that ship with the NDK (libscreen, libbps, libfreetype, libicu*, libclipboard); there are no vendored ARM prebuilts.
Third-party source lives under vendor/ as submodules (libghostty-vt, Lua 5.4).
After cloning:
git submodule update --init --recursiveCompile the binaries (inside the BBNDK shell, e.g. nix run .#shell from the bbdev workspace):
make # builds Term50 + termctlbbnix is a required dependency: it supplies the login shell (zsh), the terminfo database, and ssh/tmux/mosh.
Term50 also bundles the BB10 Fen coding-agent CLI as fen on $PATH.
The package targets stage bbnix, Fen, and the curated font bundle automatically, so they need Nix and a BB10 sysroot (BBNIX_SYSROOT); bbnix/Fen builds are impure.
export BBNIX_SYSROOT=/path/to/bbndk-linux
make package-dev # stage bbnix + Fen + fonts, build, package a dev-mode Term50.bar
make deploy # package-dev, then install + launch on the deviceDevice credentials for deploy/connect live in an untracked .env — copy .env.example to .env and set BBIP/BBPASS.
make package-release # optimized, non-devMode Term50.barBlackBerry's code-signing / BBID servers are gone, so the bar can't be signed.
Distribute the unsigned release bar by sideloading (Sachesi, or blackberry-deploy to a device with Development Mode on).
Each tab owns its own pty, child shell, and Ghostty bridge (so its own scrollback); background tabs keep consuming output. Default tab keys sit under metamode (double-tap the metamode key — right shift by default — then a letter), mirroring tmux's window keys:
| Metamode key | Action | tmux analogue |
|---|---|---|
c |
tab_new |
prefix c |
n |
tab_next |
prefix n |
p |
tab_prev |
prefix p |
x |
tab_close |
prefix & |
A one-row tab strip appears at the top when more than one tab is open (and flashes in on any tab action or top-edge tap); tap a numbered pill to jump to a tab or + to open one.
All four actions are also callable from Lua, e.g. term.action("tab_new").
The app quits once the last tab is gone.
~/.term.lua configures keybindings and scripts the terminal; see share/term.lua.reference for the full reference.
Term50 also exposes a term:// URI invoke target and an in-sandbox control socket (termctl) for notifications and tab control — the bundled share/AGENTS.md (also at $TERMCTL_AGENT_DOC on-device) documents both surfaces.