-
Notifications
You must be signed in to change notification settings - Fork 1
Security Model
vibe-coder is a single-user LAN tool. The threat model assumes:
- The host PC is trusted (you own it).
- The LAN is trusted (your home/office network behind a firewall).
- The operator is trusted (you).
- Public internet attackers cannot reach the server (no port forwarding, no public IP exposure).
If any of those assumptions fail, you need a reverse proxy with TLS, a WAF, and a higher-grade auth layer — beyond the scope of vibe-coder MVP.
POST /api/auth/login returns a token. Clients can pass it as
Authorization: Bearer <token> or as a vibe_session cookie. Both paths
converge in installAuth (Ktor plugin).
client → POST /api/auth/login {username, password}
→ 200 {token, deviceId, serverName, username}
← stores token in DataStore / cookie
client → GET /api/projects
Header: Authorization: Bearer <token>
→ 200 [...]
- BCrypt cost 12 (configurable in
server.yml). - Stored as hash only. The plaintext password is never persisted.
- Timing-safe
dummy verifyruns on missing users (prevents enumeration).
10 consecutive /api/auth/login failures lock the account for 15 minutes.
Window is per-account, not per-IP — a determined attacker can rotate
usernames, but the brute-force throughput is bounded.
Two paths:
- Visit
/setupin a browser → fill the form. - Set
VIBECODER_ADMIN_USERNAMEandVIBECODER_ADMIN_PASSWORDenv vars before firstdocker compose up -d. The entrypoint hands them to the server; if no admin exists yet, it auto-creates one.
After auto-bootstrap, change the password via /password so the
plaintext doesn't linger in .env.
Every disk read/write goes through PathSafety.normalizeAndCheck, which:
- Resolves
..and symlinks. - Checks the result is a descendant of
WorkspacePath.root(or.vibecoder/). - Rejects with
ApiException(400, "path_traversal", ...)if not.
Direct consequence: a malicious project name or file path cannot escape the workspace. Claude itself, when asked to "edit ../../../etc/passwd", hits the same check and reports failure to the user.
workspace:
uploadDeniedExtensions:
- exe
- bat
- cmd
- ps1
- shConfigurable in server.yml. The default list blocks Windows scripts +
shell scripts that could be accidentally executed by other tools.
Auth happens via the first frame, not URL query:
{ "type": "auth", "token": "<token>" }Sent within 5 s of connection, else server closes. This avoids leaking the token to access logs, proxies, or browser history.
- All Claude / Gradle / Git / npm child processes run as the unprivileged
vibeuser (UID/GID matched to host viaPUID/PGID). -
vibehas NOPASSWDsudoinside the container — but only inside the container. The container itself has no access to the host filesystem except the bind mounts in./vibe-coder-data/. - No raw shell endpoint. There's no
/api/execor web terminal. - The
script -qPTY wrap used for the Claude semi-automatic OAuth exposes only a single one-line text input field (the OAuth code), not a full shell.
- All external commands have hard timeouts (5 min default for short ops, 10 min for git clone, 30 min for builds).
- Cancellation calls
Process.destroyForcibly()afterdestroy()waits 5 s. - Background tasks (build / install / clone) emit
Done(status=…)over WebSocket on completion, including TIMEOUT/CANCELED states. Clients can always tell.
The container listens on 0.0.0.0:17880 inside its network. Compose
maps it to ${VIBE_PORT:-17880} on the host. Do NOT forward this
port to the public internet. If you need remote access:
- VPN into your LAN (Tailscale / WireGuard).
- Or expose only through a reverse proxy with HTTPS termination + auth.
.env is in .gitignore so it never reaches git, but it sits on disk
in plaintext. Lock its permissions:
chmod 0600 ~/vibe-coder/.envIf you set VIBECODER_ADMIN_PASSWORD here for auto-bootstrap, rotate it
via /password after first login, then either remove it from .env or
accept the risk that someone with file read access can see it.
Open a private security advisory on GitHub. Do not file public issues.