📚 Docs

Web shell (browser terminal)

The shell app gives developers a terminal inside their app's own running container, straight from the browser — no SSH key, no VPN. It lives at https://shell.54.170.127.122.sslip.io and is installed automatically with the platform.

Sessions are real shells inside the app container (docker exec, same entry point as sudo paas-app-shell <app> on the host): unprivileged runtime user, minimal environment, and every container restriction from App container confinement applies. A browser shell can never reach the host or another tenant.

Getting access

Access is granted per person, per app, for a limited time by the platform operator. There is no self-service signup and no password.

The operator runs, on the Dokku host:

sudo paas-shell-token mint --user alice --app myapp --ttl 28800

This prints a signed token and a ready-to-share access URL:

https://shell.54.170.127.122.sslip.io/login?token=v1.eyJ...

The operator sends that URL to the developer (chat, email, ticket). The --ttl is the token lifetime in seconds (default 3600 = 1 hour; 28800 = 8 hours). When it expires, the developer's next session needs a fresh URL.

Opening a shell (developer side)

  1. Open the access URL in a browser.
  2. The platform verifies the token, sets a session cookie (HttpOnly, tied to the token's remaining lifetime), and lands you on the terminal page, already scoped to your app.
  3. Type. It's a full terminal: arrows, Ctrl keys, resizing all work. Close the tab or let it sit — sessions idle out after 30 minutes; the pty is always cleaned up server-side.

Re-opening the same URL later works until the token expires. The token rides the URL exactly once (at /login); the live session uses the cookie, so the token never sits in browser history or proxy logs beyond that first click.

GitHub Copilot CLI in the shell

The shell environment ships the GitHub Copilot CLI: type copilot for an AI pair-programmer in the terminal. Sign-in uses GitHub's device code flow — see GitHub Copilot CLI for the steps and requirements.

How a session is scoped

Architecture at a glance

Piece Where it runs Role
shell Dokku app its own container serves the terminal page (xterm.js)
paas-shell-backend.service the Dokku host, 127.0.0.1:8790 verifies tokens, spawns the pty inside app containers, enforces scoping/caps/timeouts
nginx include host (/home/dokku/shell/nginx.conf.d/) routes /ws/terminal and /login from the app domain to the backend
paas-shell-token host (/usr/local/bin/) operator CLI: mint/verify tokens

The token HMAC secret lives only on the host (/etc/paas-shell/token-secret), readable by root and the service user — never inside any app container.

Audit trail

Every grant and every session is a JSON line in the service journal:

sudo journalctl -u paas-shell-backend

Look for auth.login (who was granted access to which app, token id, expiry, source address), session.start, and session.end (duration, byte counts, reason). Service liveness: sudo systemctl status paas-shell-backend.

Operator reference