Documentation
Installation
Every install shape: VPS, desktop app, kiosk session, paths and flags.
On this page
Overview
There is exactly one installer and one command. What changes between a VPS, a laptop and a kiosk machine is not the download — it is which host integration the installer sets up around the same binary.
curl -fsSL https://tde.sh/install | shThe install page is the short version with a copy button. This page is the long version: every flag, every path, every override.
Platforms and artifacts
| Platform | Artifact | Build target |
|---|---|---|
| Linux | tde-<tag>-<triple>.tar.gz (static musl) | x86_64-unknown-linux-musl, aarch64-unknown-linux-musl |
| macOS | TDE-<tag>.dmg containing TDE.app | universal (x86_64 + arm64 apple-darwin) |
Linux and macOS do not share bytes. They are separate builds from separate
workflows, and the installer picks by uname.
The Linux binary is fully static: no glibc, no OpenSSL (TLS is rustls), SQLite compiled in. It runs on Alpine, Debian, Ubuntu and stock VPS images without a single shared-library dependency.
On macOS, TDE.app does not draw pixels itself. Its launcher opens
Terminal.app and runs the embedded binary, so the experience is a normal
terminal window. Anything below about the Linux kiosk session does not apply.
What the installer actually does
- Reads any existing install receipt (
~/.local/share/tde/install.json, then/opt/tde/install.json) and prints the currently installed version. A pre-receipt binary found onPATHis reported as a legacy install and migrated safely. - Detects OS and architecture, and refuses unsupported combinations.
- Resolves the release: it reads
tag_name(or the oldertag) out of the channel feed athttps://tde.sh/releases/<channel>.json, with plainsedso nojqis needed on a minimal host. - Downloads the artifact and its
.sha256sidecar. A missing checksum or a mismatch aborts the install — there is no unverified path. - Installs atomically (see Where things land).
- Applies whichever host integrations you asked for.
Re-running the command is always safe, and is a perfectly good manual update.
Install modes
Flags may be given in any order, after -s --:
curl -fsSL https://tde.sh/install | sh -s -- --persist --system| Flag | Effect |
|---|---|
| (none) | Headless install: just the binary. The primary VPS shape. |
--app | Desktop-app launcher: installs kitty plus a "TDE" applications-menu entry. |
--no-app | Opt out of the app launcher. |
--desktop | Also install the kiosk login session. |
--login | Auto-start TDE on interactive SSH login. |
--persist | Auto-start TDE inside a tmux session that survives disconnects. |
--system | Install system-wide to /usr/local/bin (uses sudo when not root). |
--channel NAME | Release channel to resolve (default stable). |
-h, --help | Print usage and exit. |
--persist and --login are the same slot: --persist wins whichever order
you pass them in.
Headless / VPS — the default
No flags. You get the binary and nothing else, which is exactly right for a server: the desktop is drawn by your terminal over SSH.
ssh user@vps
curl -fsSL https://tde.sh/install | sh
tde--app — the desktop app
Installs kitty through your package manager and drops a launcher into your applications menu. kitty is chosen deliberately: its graphics protocol is what gives the built-in browser, image viewer and PDF viewer real-pixel rendering, which plain GTK terminals cannot display.
On Linux it installs into user scope, no root required:
~/.local/share/applications/tde-app.desktop~/.local/share/icons/hicolor/512x512/apps/tde.png
and refreshes the desktop database when update-desktop-database exists. The
entry runs kitty --class=tde-app --title=TDE --start-as=maximized tde.
On macOS there is no .desktop machinery: the installer installs kitty via
Homebrew when kitty is absent, or warns and points at kitty's download page if
Homebrew is missing. Then you open kitty and run tde.
--desktop — the kiosk login session
Turns TDE into a session you can pick at the display manager, replacing the GUI
desktop. This one needs root (it writes under /usr/), so the installer uses
sudo when you are not root.
It installs:
cage— the Wayland kiosk compositor.kitty— best-effort, the preferred kiosk terminal.foot— the lightweight fallback terminal./usr/local/bin/tde-session— the session launcher./usr/share/wayland-sessions/tde.desktop— TDE (Text Desktop)./usr/share/xsessions/tde-x11-nested.desktop— a nested X11 fallback entry.
It also removes any old /usr/share/xsessions/tde.desktop, because on some
display managers that entry started cage as a nested X client and the desktop
came up tiny instead of taking over the monitor. Prefer the Wayland entry; the
nested X11 one is a fallback, not a real kiosk.
tde-session picks its host in this order:
cage+kitty(full graphics protocol, truecolor, HiDPI)cage+foot(sixel: real pixels, 256-color palette)cage+alacritty(cells only)- bare
kitty - the current terminal, with a warning
It reads a few environment variables:
| Variable | Default | Effect |
|---|---|---|
TDE_BIN | tde | Binary to launch inside the kiosk terminal. |
TDE_FONT | Geist Mono if fontconfig knows it, else monospace | Kiosk font family. |
TDE_FONT_SIZE | 14 | Global UI scale — bigger font means bigger cells, so everything scales. |
TDE_CAGE_MODE | last | cage -m mode. extend spans all connected outputs; last fills one. |
Log out and pick TDE (Text Desktop) from the session menu (LightDM, GDM,
SDDM). TDE_FONT_SIZE is the single lever for scale: try 11 on a dense 4K
panel, 18 on a TV.
--login and --persist — SSH autostart
Both write a marked block into ~/.profile, between
# >>> TDE autostart >>> and # <<< TDE autostart <<<. The block is removed
and rewritten on every install, so re-running never stacks duplicates.
--login execs TDE directly:
# >>> TDE autostart >>>
if [ -z "$TDE_NO_AUTOSTART" ] && [ -z "$TDE_STARTED" ] && [ -t 0 ] && command -v tde >/dev/null 2>&1; then
export TDE_STARTED=1
exec tde
fi
# <<< TDE autostart <<<--persist installs tmux if missing and execs TDE inside an attach-or-create
session instead, so the desktop survives a dropped connection:
TDE_TMUX_SESSION="${TDE_TMUX_SESSION:-loop}"
exec tmux new-session -A -s "$TDE_TMUX_SESSION" tdeSet TDE_TMUX_SESSION to use a name other than loop.
--system — system-wide install
Without it, a non-root install is per user. With it (or when you are already
root), everything moves to system paths and sudo is used as needed. The
installer aborts if --system is requested with neither root nor sudo
available.
Where things land
Linux installs never overwrite a binary in place. Each release goes into its own immutable directory and a symlink is swapped atomically, which is what makes an interrupted install a no-op and a rollback possible.
| Scope | Binary entry point | Install root |
|---|---|---|
| user (default) | ~/.local/bin/tde | ~/.local/share/tde |
--system / root | /usr/local/bin/tde | /opt/tde |
Inside the install root:
<root>/versions/<tag>/tde the immutable release binary
<root>/current symlink -> versions/<tag>
<root>/install.json the install receipt~/.local/bin/tde is a symlink to <root>/current/tde, replaced by an
atomic rename. If a plain (non-symlink) binary was already sitting there from
an older install, it is preserved as tde.legacy for rollback.
The receipt records what was installed and where the updater should look next time:
{
"schema": 1,
"version": "v0.3.32",
"channel": "stable",
"mode": "user",
"root": "/home/you/.local/share/tde",
"manifest": "https://tde.sh/releases/stable.json"
}mode is user, system, or macos-app. The in-app updater reads
manifest from here, which is how a mirror chosen at install time keeps
applying to later background updates — see Updating.
On macOS the app is installed to ~/Applications/TDE.app, or /Applications
with --system. The previous bundle is kept as TDE.app.previous, and the
receipt is still written to ~/.local/share/tde/install.json.
If the chosen bin directory is not on your PATH, the installer prints the
exact line to add to ~/.profile rather than editing it silently.
Environment overrides
| Variable | Default | Effect |
|---|---|---|
TDE_CHANNEL | stable | Channel to resolve (same as --channel). |
TDE_MANIFEST_URL | https://tde.sh/releases/<channel>.json | Release-feed override. Recorded in the receipt. |
TDE_RELEASE_BASE | https://tde.sh/releases | Base for immutable artifact URLs. |
TDE_VERSION | latest in the feed | Install a specific tag, skipping feed resolution. |
TDE_URL | — | Full artifact URL. Overrides repo and version resolution entirely. |
TDE_PACKAGING_BASE | https://tde.sh/packaging | Where --app / --desktop fetch the .desktop files, icon and tde-session. |
The checksum sidecar is always fetched from <artifact-url>.sha256, including
when you set TDE_URL — a mirror has to publish both.
# Pin a version
TDE_VERSION=v0.3.30 curl -fsSL https://tde.sh/install | sh
# Point everything at an internal mirror
TDE_MANIFEST_URL=https://mirror.corp/tde/stable.json \
TDE_RELEASE_BASE=https://mirror.corp/tde/releases \
TDE_PACKAGING_BASE=https://mirror.corp/tde/packaging \
curl -fsSL https://tde.sh/install | shBuilding from source
For development, or when you cannot reach the release host:
cargo build --release -p tde
./target/release/tdeThe repository also carries scripted equivalents of the installer for CI and
image builds — packaging/install.sh for the kiosk login session (needs root,
targets apt/dnf/pacman/zypper hosts) and packaging/install-vps.sh for the
headless path, which builds the static binary itself when cargo is present.
Both are aimed at a repo checkout, not at end users.
A cargo build living under target/debug or target/release never
auto-updates itself.
Uninstall
packaging/uninstall.sh removes the system install and the login session:
sudo packaging/uninstall.shIt deletes /usr/local/bin/tde, /usr/local/bin/tde-session and the three
session entries (PREFIX overrides /usr/local). It deliberately leaves
/opt/tde, ~/.tde, cage and foot alone.
For a user install, or to remove state as well, delete by hand:
rm -f ~/.local/bin/tde ~/.local/bin/tde.legacy
rm -rf ~/.local/share/tde # versioned binaries + install receipt
rm -rf ~/.tde # runtime state, update staging
rm -rf ~/.config/tde # configuration
rm -f ~/.local/share/applications/tde-app.desktopIf you used --login or --persist, also delete the marked block in
~/.profile between # >>> TDE autostart >>> and # <<< TDE autostart <<<.
Next
- Updating — the background updater, channels and mirrors.
- Configuration —
~/.config/tde/config.toml. - Troubleshooting — when something did not come up.