Getting Started
This guide walks you through installing epm, then spinning up shell — a
web-based terminal with a mobile command palette that runs on your own machine. You'll
have it running in your browser in about 10 minutes. Getting it on your phone takes a few
more.
1. Install epm
curl -fsSL https://raw.githubusercontent.com/Slam-Dunk-Software/epm/main/install.sh | sh
Installs a pre-built binary to /usr/local/bin. Supports macOS (Intel + Apple Silicon) and Linux (x86_64 + ARM64 / Raspberry Pi).
Windows: Native Windows is not supported. Install WSL2 and run the installer from there.
Verify:
epm --version
2. Get shell
epm new shell
cd ~/eps/shell
epm new scaffolds into ~/eps/ by default — all your EPS projects live together in one place. If you'd rather call it something personal — terminal, cockpit, whatever feels right — just pass a name:
epm new shell seeing_stone
cd ~/eps/seeing_stone
The directory name is yours. The harness is yours. EPS doesn't care what you call it.
Setup
Set up SSH access for the terminal:
shell works by having the Node server SSH into your own machine to create a terminal
session — that's how it gets a real PTY without needing root or a native daemon. It needs
a key it can use to authenticate without a password prompt.
First, make sure SSH is enabled on your machine:
- macOS: System Settings → General → Sharing → Remote Login → On
- Linux:
sudo systemctl enable ssh && sudo systemctl start ssh
Then create a dedicated key for shell:
ssh-keygen -t ed25519 -f ~/.ssh/shell_key -N ""
cat ~/.ssh/shell_key.pub >> ~/.ssh/authorized_keys
If you already have a key in ~/.ssh/authorized_keys, you can skip keygen and point
SSH_KEY_PATH at it in your .env instead.
Install dependencies:
shell is a Node.js app. You need Node 18 or later — if you're not sure what you have, run node --version. If you don't have Node, install it from nodejs.org first, then:
npm install
Don't skip this. If you run
epm services startbeforenpm install, the service will crash on startup.epm services logs shellwill show you the error, but save yourself the detour — runnpm installfirst.
Create a .env file with your PIN:
SHELL_TOKEN=1234
This is the PIN on the lock screen. Change it to something only you know.
3. Serve
epm services start
epm services reads eps.toml, starts the server, and registers the service. Check it:
epm services ps
NAME PORT STATUS
shell 4444 running
Open http://localhost:4444 in your browser. Enter your PIN. You're in.
Having issues? Expand troubleshooting
Start by checking the logs:
epm services logs shell # or whatever you named it
Most issues are obvious from the first few lines.
Setup screen instead of PIN pad
You haven't set SHELL_TOKEN in your .env yet. Add it:
SHELL_TOKEN=1234
Then epm services restart shell.
"SSH key not found" on startup
The server can't find your SSH key. Make sure the path in .env matches what you created:
SSH_KEY_PATH=~/.ssh/shell_key
If you're using an existing key, point SSH_KEY_PATH at it and confirm it's in ~/.ssh/authorized_keys.
tmux not found
Install it: brew install tmux (Mac) or sudo apt install tmux (Linux), then epm services restart shell.
Garbled or missing unicode characters
tmux needs the right locale. Add to your .env:
LANG=en_US.UTF-8
LC_ALL=en_US.UTF-8
If still broken, add to ~/.tmux.conf:
set -g default-terminal "xterm-256color"
Then epm services restart shell.
Port already in use
epm services start will automatically pick the next available port if 4444 is taken and update eps.toml. Check epm services ps to see which port your instance landed on.
4. Access from your phone
Shell is running on your machine. To reach it from your phone you need a way to connect — Tailscale is the easiest option, but there are others.
Option A: Tailscale (recommended)
Tailscale creates a private encrypted network between your devices in about two minutes — no port forwarding, no firewall config.
On your Mac:
The easiest way is the Tailscale macOS app — it runs as a menu bar app and manages the daemon automatically. Sign in and you're done.
If you prefer the terminal:
brew install tailscale
sudo tailscaled & # start the daemon
tailscale up # authenticate (opens browser)
Find your Mac's Tailscale IP:
tailscale ip -4
You'll get something like 100.x.x.x. EPS will use this automatically on the next deploy.
On your iPhone:
Install Tailscale from the App Store, sign in with the same account, and tap Connect.
Then restart shell so it binds to your Tailscale IP:
epm services restart shell
Open http://<your-tailscale-ip>:4444 on your phone. You're in your shell from anywhere
on your tailnet.
Option B: SSH app (Termius, JuiceSSH, etc.)
If you'd rather use a dedicated SSH client than a browser-based terminal:
Generate an SSH key on your Mac (if you don't have one):
bash ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519 -N ""Add it to authorized keys:
bash cat ~/.ssh/id_ed25519.pub >> ~/.ssh/authorized_keysCopy the private key to your phone and add your Mac's LAN IP (
ipconfig getifaddr en0) to your SSH app.
Enable SSH in System Settings → General → Sharing → Remote Login.
You can still deploy EPS harnesses alongside this —
epm newandepm services startwork the same either way.
5. Make it yours
Open CUSTOMIZE.md in your shell directory. Key things to personalize:
palette.json— add your own command shortcuts to the palette (the buttons on the side panel / mobile bottom sheet)SHELL_TOKEN— change the PIN in.envTMUX_SESSION— attach to a specific tmux session by namepublic/index.html— the whole frontend is a single file, edit it freely
After any change:
epm services restart shell
6. Start on login
To have epm services launch all your services automatically when you log in:
epm services install-startup
On macOS this installs a LaunchAgent. On Linux it installs a systemd user unit (~/.config/systemd/user/epm-startup.service). Individual services opt in via startup = true in their eps.toml — all official EPS harnesses include this by default.
7. Add observatory
observatory watches all your running services and sends you an SMS if something goes down.
epm new observatory
cd ~/eps/observatory
epm services start
epm services ps
NAME PORT STATUS
shell 4444 running
observatory 9090 running
Open http://localhost:9090 (or http://<your-tailscale-ip>:9090) to see your dashboard.
8. Feeling ambitious? Add HTTPS
If you've got Tailscale running, you can get a real HTTPS cert for your machine in about two minutes — no reverse proxy, no Let's Encrypt, no port 80.
Tailscale issues certificates for your MagicDNS hostname (e.g. my-mac.tail1234.ts.net) via its built-in CA. Enable it first:
In the Tailscale admin console, turn on MagicDNS and HTTPS certificates.
On your Mac, fetch the cert:
tailscale cert $(tailscale whois $(tailscale ip -4) | grep Name: | awk '{print $2}')
This writes two files to the current directory: <hostname>.ts.net.crt and <hostname>.ts.net.key.
- Add them to your
.env:
TLS_CERT=/path/to/<hostname>.ts.net.crt
TLS_KEY=/path/to/<hostname>.ts.net.key
- Restart:
epm services restart shell
You can now open https://<hostname>.ts.net:4444 from any device on your tailnet. Note: once TLS is enabled you'll need to use the hostname — https://<hostname>.ts.net:4444. The raw IP still works over plain HTTP if you need it.
What's next
- Browse packages on epm.dev — todo lists, CRMs, note apps, SMS endpoints, and more
- Open any package page in your browser with
epm open <name> - Read What is EPS? for the philosophy behind the stack
- Check the CUSTOMIZE.md concept doc to understand how harness authors design extension points
- Use
epm adopt <name>to pull a harness into your repo as first-class source code, andepm sync <name>to check for upstream changes later