termulaa is a single-user, loopback-only developer tool. It is not designed, tested, or intended for multi-user or remote use.
The HTTP server binds to 127.0.0.1 only. It has:
- No per-request authentication.
- Host-header allowlisting for
127.0.0.1,localhost, and::1on the configured port. - Origin allowlisting for the same loopback origins on both HTTP and WebSocket requests.
- No wildcard CORS — the server echoes only allowed loopback origins.
This means: any web page loaded in any browser on the same machine can call the API and attach to a live PTY if it uses an allowed Host/Origin. This is accepted, known risk for a single-user dev tool running on a personal machine.
Changing the listener bind from 127.0.0.1 to 0.0.0.0 (or any LAN/VPN
address) without also adding the controls below is a security incident. In
src/cmd/termulaa/main.go the bind is pinned at 127.0.0.1:<port>
and commented accordingly.
- Per-request authentication — a bearer secret, or an OS-local credential (keychain, UDS). Every HTTP and WebSocket request must be authenticated.
- Strict CORS policy — explicit allowlist, not
*. - WebSocket origin check —
CheckOriginmust validate theOriginheader against an allowlist. - TLS — required on any non-loopback interface.
- Rate limiting / session quotas — to contain abuse or bugs.
Until all five are in place, keep the loopback bind.
termulaa -rc runs a reverse tunnel agent (see
docs/rc-protocol.md). This does not violate
the loopback rule:
- The terminal server's bind is untouched — still
127.0.0.1only. - No inbound port is opened anywhere. The agent only dials out (WebSocket over TLS) to a rendezvous server, and browser traffic arrives as multiplexed streams inside those outbound connections.
- The rendezvous rewrites
Host/Originto loopback values, so the local termulaa still enforces its DNS-rebinding guard unchanged. - The agent is a byte pump with no HTTP parsing and no terminal
knowledge;
termulaa -rcnever starts the terminal server.
The token is the capability. Anyone holding a valid tunnel token can attach an agent, and anyone the rendezvous admits to the viewer side of that agent gets your terminal — i.e. arbitrary command execution as your user. Treat tokens like SSH private keys. They expire by design and can be revoked at the rendezvous pairing page; expiry is discovered at connect time (the token is opaque to termulaa and never parsed).
Residual risks, accepted knowingly when you opt in to -rc:
- Rendezvous compromise or malice — the rendezvous terminates TLS and can open streams to your terminal at will. Only tunnel to a rendezvous you trust as much as the machine itself (ideally your own).
- Same-origin serving removes browser isolation. A rendezvous may serve the proxied terminal under a path prefix on its own hostname (see docs/rc-protocol.md §6.1) instead of a dedicated view host. That is convenient — no extra DNS or TLS — but it puts a remote shell and the rendezvous's own web app on the SAME browser origin, so the browser no longer isolates them from each other: an XSS bug in either one can drive the other (read the terminal, send keystrokes, call the rendezvous's APIs as you). A dedicated view host restores that boundary and is the more secure deployment. Choose it when you care about worst-case containment more than setup effort.
- Token leakage — a leaked token is remote shell access until it expires or is revoked.
-rc-insecuredisables TLS certificate verification on the agent's dials. It exists for development against self-signed rendezvous certs only; with it set, an on-path attacker can become your rendezvous. Never use it in production. It persists in~/.termulaa/rc.json; remove it with-rc-insecure=false.
Not running -rc (the default) leaves the historical posture exactly as
described above.
This is a personal project and has no paid security team. Best-effort response only.
- Non-sensitive issues — open a GitHub issue.
- Sensitive issues (e.g. a working exploit) — use GitHub's private vulnerability reporting, or email mr.sudiptadeb@gmail.com.