📚 Docs

Web shell (browser terminal)

The shell app gives developers a terminal inside their app's own running container, straight from the browser. A registered Dokku SSH key can approve browser access without an administrator. It lives at https://shell.54.195.181.75.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. Open https://shell.54.195.181.75.sslip.io/?app=myapp (omit ?app= to access the shell app). Without a valid session, the page displays a short-lived SSH login command. Run it on your own computer using your registered Dokku SSH key (specify -i <private-key> if needed), for example:

ssh -t -i ~/.ssh/id_ed25519 dokku@<platform-host> shell:login ABCD-EFGH

Read the app and browser address in the SSH prompt and type yes only if they match your request. The waiting browser connects automatically. Codes expire after 5 minutes and are single-use; the resulting app-scoped session lasts 24 hours. A stolen code alone cannot log a different browser in. The SSH key must already be registered with Dokku; local dokku shell:login does not authenticate you. Treat registered keys as authorized for the apps they request: this platform has no per-app ownership registry, and self-service login does not add one. If you need app-specific grants, disable self-service (SHELL_SSH_LOGIN=false in the service unit) and use the operator flow below.

Operator fallback (no registered SSH key): On the Dokku host run:

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.195.181.75.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 operator-provided access URL in a browser (or use the SSH flow above).
  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: Tab completes commands and paths; Ctrl-L clears the display and Ctrl-R searches command history in Bash. Other app images without Bash fall back to /bin/sh and may not support these shortcuts. Arrows, Ctrl keys, and resizing work. The shell app image includes curl, python (Python 3), jq, and less; shells in other apps use those apps' own images and toolsets. 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 default shell app ships the GitHub Copilot CLI: type copilot for an AI pair-programmer with built-in PaaS instructions and a paas-deploy skill. Other app-scoped shells use their own app images and may not include Copilot. Sign-in uses GitHub's device code flow when needed — see GitHub Copilot CLI for the steps, deployment workflow, and shared deploy-key limitations.

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, /login, and /login/ssh/ from the app domain to the backend
paas-shell-login Dokku plugin Dokku host checks the SSH key identity and approves codes over a group-restricted unix socket
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.ssh_login.start, auth.ssh_login.approve, auth.ssh_login.complete, and auth.ssh_login.reject for self-service access; or auth.login for operator grants (user, app, token id, expiry, source address). session.start and session.end record duration, byte counts, and reason. Service liveness: sudo systemctl status paas-shell-backend.

Operator reference