Skip to content

Security & remote access

M.A.V.E.N is a local-first control plane. It listens on 127.0.0.1:3000, 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.

Using M.A.V.E.N 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 M.A.V.E.N What happens
On the same machine - http://127.0.0.1:3000 or http://localhost:3000 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.

M.A.V.E.N generated a PIN for you at its very first start. It was printed to the console once and is kept in ~/.maven/access-pin (readable by your user only). You can see it any time in Settings → Security → Reveal.

Nothing changes in the installation. After the first npm start (or the autostart service) you will see, once, in the console / journalctl --user -u maven:

MAVEN access PIN for remote hosts (Tailscale / LAN): K7Q4-M2X9
Kept in /home/you/.maven/access-pin. Loopback needs no PIN.

Open http://127.0.0.1:3000 and work. You will not be asked for the PIN here.

Reaching M.A.V.E.N from another device (phone, laptop)

Section titled “Reaching M.A.V.E.N from another device (phone, laptop)”
  1. (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 M.A.V.E.N 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.
  2. Expose the port privately. Tailscale: tailscale serve --bg 3000. Or an SSH tunnel: ssh -L 3000:127.0.0.1:3000 you@machine (a tunnel arrives as localhost, so it needs neither the allow-list nor the PIN).
  3. Open it there - http://rog.tail1234.ts.net:3000. You get the Enter access PIN screen. Enter the PIN from Settings → Security → Reveal (or cat ~/.maven/access-pin). The session lasts 30 days.

Never expose port 3000 to the public internet, with or without a PIN. Tailscale or a tunnel only. The walkthrough with screenshots is Reach M.A.V.E.N from your phone.

  • 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 MAVEN_ACCESS_PIN=<pin> in .env.local. Changing it signs every session out.
  • No PIN anywhere - MAVEN_ACCESS_PIN=off. Only the host/origin checks remain. Not recommended if any host besides loopback is allowed.
  • Allowed hosts - the MAVEN_ALLOWED_ORIGINS list: host[:port] or a full origin, comma-separated.
  • Daily spend ceiling - MAVEN_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.

  • 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:3000, or DNS-rebinding tricks - and those are stopped by M.A.V.E.N refusing any Host it 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 M.A.V.E.N’s agents are a shell.
  • Allowed hosts exist so that a rebinding page cannot become “the M.A.V.E.N page” just by pointing its own domain at your loopback address. A host not on the list only ever sees the unlock screen (and 401 on 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 get 421 Misdirected Request instead.
  • Sessions are signed with a per-install secret (~/.maven/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 M.A.V.E.N’s API or read its cookies.
  • Agents spawned by M.A.V.E.N receive only the environment variables they need, not every API key in .env.local. The permission mode an agent runs with is decided by M.A.V.E.N 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 MAVEN_ALLOW_PRIVATE_FETCH=1 if you deliberately ingest a LAN site.
  • Community skills pass a mandatory quarantine → static scan → sandbox gate before install. See Skills.
Terminal window
# .env.example - Remote-access gate
MAVEN_ACCESS_PIN= # unset = generated PIN for remote hosts; <pin> = yours, everywhere; off = no gate
MAVEN_ALLOWED_ORIGINS= # host[:port] or origin, comma-separated - everything MAVEN answers for besides loopback
MAVEN_DAILY_SPEND_USD=25 # daily ceiling across paid engines; 0 = none
MAVEN_ALLOW_PRIVATE_FETCH= # 1 = let R-Forge/N-Forge fetch LAN / loopback URLs
MAVEN_CHAT_PERMISSION_MODE= # bypass = let the /chat agents run without permission prompts (autonomous apps are unaffected)
MAVEN_SESSION_SECRET= # optional override for ~/.maven/session-secret (32+ chars)

Files: ~/.maven/access-pin (generated PIN), ~/.maven/session-secret (session signing key). Both 0600. Delete access-pin and restart to get a new one - or use Regenerate.

  • 421 Misdirected Request - the gate is off (MAVEN_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 MAVEN. Only M.A.V.E.N’s own pages may.
  • Locked out on the machine itself - you set your own PIN and forgot it: edit .env.local, remove MAVEN_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 allowedDevOrigins in next.config.ts (see Configuration).