Security & remote access
Erebros is a local-first control plane. It listens on 127.0.0.1:3300, runs AI agents
that can execute commands, reads and writes your files, and spends money on paid
providers. This page explains how it is protected, what the PIN is, and what you do
when you want to reach it from another device.
The short version
Section titled “The short version”Using Erebros only on this machine? You have nothing to do. It asks for no PIN on
127.0.0.1/localhost, before or after any update. Read on only if you want to open it from another device.
| Where you open Erebros | What happens |
|---|---|
On the same machine — http://127.0.0.1:3300 or http://localhost:3300 |
No PIN. |
| From anywhere else — a Tailscale name, a LAN address, a reverse-proxy hostname | PIN required. The first correct PIN from a host adds it to Allowed hosts automatically. |
Erebros generated a PIN for you at its very first start. It was printed to the console
once and is kept in ~/.erebros/access-pin (readable by your user only). You can see it
any time in Settings → Security → Reveal.
Installing on a new machine
Section titled “Installing on a new machine”Nothing changes in the installation. After the first npm start (or the autostart
service) you will see, once, in the console / journalctl --user -u erebros:
Erebros access PIN for remote hosts (Tailscale / LAN): K7Q4-M2X9Kept in /home/you/.erebros/access-pin. Loopback needs no PIN.Open http://127.0.0.1:3300 and work. You will not be asked for the PIN here.
Reaching Erebros from another device (phone, laptop)
Section titled “Reaching Erebros from another device (phone, laptop)”- (Optional) Allow the host name. Settings → Security → Allowed hosts — add
the name you will type on the other device, e.g.
rog.tail1234.ts.net. You can skip this: a host Erebros does not know yet gets the unlock screen, and a correct PIN adds it to the list for you. The list is where you review and remove hosts. - Expose the port privately. Tailscale:
tailscale serve --bg 3300. Or an SSH tunnel:ssh -L 3300:127.0.0.1:3300 you@machine(a tunnel arrives aslocalhost, so it needs neither the allow-list nor the PIN). - Open it there —
http://rog.tail1234.ts.net:3300. You get the Enter access PIN screen. Enter the PIN from Settings → Security → Reveal (orcat ~/.erebros/access-pin). The session lasts 30 days.
Never expose port 3300 to the public internet, with or without a PIN. Tailscale or a tunnel only. The walkthrough with screenshots is Reach Erebros from your phone.
Settings → Security
Section titled “Settings → Security”- Generated PIN for remote hosts (default) — loopback open, every other host asks for the generated PIN. Reveal, Copy, Regenerate (regenerating signs every remote session out).
- My own PIN, everywhere — your own PIN (8+ characters), required on this machine
too. Equivalent to
EREBROS_ACCESS_PIN=<pin>in.env.local. Changing it signs every session out. - No PIN anywhere —
EREBROS_ACCESS_PIN=off. Only the host/origin checks remain. Not recommended if any host besides loopback is allowed. - Allowed hosts — the
EREBROS_ALLOWED_ORIGINSlist:host[:port]or a full origin, comma-separated. - Daily spend ceiling —
EREBROS_DAILY_SPEND_USD(default 25). Every paid engine (images, video, voice, 3D, upscales) refuses above it until local midnight with HTTP 402.0= no ceiling. The card shows what was spent today.
The status line at the top of the card tells you whether this page is gated — the answer depends on how you reached it. The card itself is described on Settings → Security.
Why it works this way
Section titled “Why it works this way”- Loopback needs no PIN because a PIN there would protect nothing: every process
on the machine already runs as you and can read the PIN file. The attacks that matter
on loopback come from the browser — a malicious page on another site talking to
127.0.0.1:3300, or DNS-rebinding tricks — and those are stopped by Erebros refusing anyHostit does not answer for and any cross-site request. That is also why local integrations (Hermes bots, OpenMontage tools, the updater, the smoke tests) keep working: they talk to the API from the machine, with no PIN. - Remote hosts always need a PIN because that is exactly where an unauthenticated shell would be a problem — and Erebros’s agents are a shell.
- Allowed hosts exist so that a rebinding page cannot become “the Erebros page” just
by pointing its own domain at your loopback address. A host not on the list only ever
sees the unlock screen (and
401on the API) until the PIN is entered — which the attacker’s page cannot do. With the gate switched off there is no PIN to prove anything, so unknown hosts get421 Misdirected Requestinstead.
What else is enforced (no action needed)
Section titled “What else is enforced (no action needed)”- Sessions are signed with a per-install secret (
~/.erebros/session-secret), expire server-side, and are revoked when the PIN changes. The unlock form is rate-limited. - HTML, SVG and apps that AI models generate are served in a browser sandbox: they render and run, but cannot call Erebros’s API or read its cookies.
- Agents spawned by Erebros receive only the environment variables they need, not every
API key in
.env.local. The permission mode an agent runs with is decided by Erebros per app, never by the request. - Secrets are stored
0600; the Settings panel shows masked values only; URLs of providers cannot be changed from the UI (redirecting one would leak its key). - Paid routes are rate-limited and pass through the spend ceiling; expensive runs ask for a server-issued confirmation.
- Self-update only applies the exact commit it announced, installs dependencies with
npm ci --ignore-scripts, and never regenerates the lockfile unless you tell it to. - Server-side fetches of URLs you paste (R-Forge, N-Forge, D-Forge) refuse private
and loopback addresses. Set
EREBROS_ALLOW_PRIVATE_FETCH=1if you deliberately ingest a LAN site. - Community skills pass a mandatory quarantine → static scan → sandbox gate before install. See Skills.
Environment reference
Section titled “Environment reference”# .env.example — Remote-access gateEREBROS_ACCESS_PIN= # unset = generated PIN for remote hosts; <pin> = yours, everywhere; off = no gateEREBROS_ALLOWED_ORIGINS= # host[:port] or origin, comma-separated — everything Erebros answers for besides loopbackEREBROS_DAILY_SPEND_USD=25 # daily ceiling across paid engines; 0 = noneEREBROS_ALLOW_PRIVATE_FETCH= # 1 = let R-Forge/N-Forge fetch LAN / loopback URLsEREBROS_CHAT_PERMISSION_MODE= # bypass = let the /chat agents run without permission prompts (autonomous apps are unaffected)EREBROS_SESSION_SECRET= # optional override for ~/.erebros/session-secret (32+ chars)Files: ~/.erebros/access-pin (generated PIN), ~/.erebros/session-secret (session
signing key). Both 0600. Delete access-pin and restart to get a new one — or use
Regenerate.
Troubleshooting
Section titled “Troubleshooting”421 Misdirected Request— the gate is off (EREBROS_ACCESS_PIN=off) and the host name you typed is not in Allowed hosts. Add it, or turn the gate back on so the PIN can let you in.403 refused: this request came from …— a page on another site (or another local port) tried to call Erebros. Only Erebros’s own pages may.- Locked out on the machine itself — you set your own PIN and forgot it: edit
.env.local, removeEREBROS_ACCESS_PIN, restart. The generated PIN then applies to remote hosts only. - “Too many tries” — wait the shown seconds; the limiter backs off exponentially and resets on success.
- 402 “daily spend cap reached” — raise the ceiling in Settings → Security or wait for midnight.
- A remote device was unlocked and now is not — the PIN was changed or regenerated; enter the new one.
- Pages load blank from a remote host in dev mode — add the host to
allowedDevOriginsinnext.config.ts(see Configuration).