Threat model
OverviewAnyone who controls the control plane can run actions as root on every connected server. Every design decision below follows from that: keep the control plane hard to reach and hard to abuse, keep servers closed to the outside, and make every action attributable.
- Stolen or guessed passwords are stopped by rate limiting, lockout and two-factor authentication.
- Stolen tokens expire quickly, are bound to httpOnly cookies, and are revoked when reuse is detected.
- A compromised browser extension or injected script is contained by a strict, nonce-based Content Security Policy.
- A compromised account is limited by its role and leaves a tamper-evident trail.
Network model
OverviewServers never accept connections from the panel. Each agent is a single static Go binary that dials out to the control plane over one WebSocket (wss:// when the panel is behind HTTPS) and receives work over it.
- No inbound ports: the Docker socket, systemd and SSH stay private on each server.
- The browser only talks to the dashboard and control plane, never to a server.
- Registration tokens are single-use, expire after 60 minutes and are stored as SHA-256 hashes. Agent tokens are 256-bit random and stored hashed.
- Webhooks must carry a valid
X-Hub-Signature-256; the endpoint refuses to work without a secret.
Sign-in protection
IdentityEvery sign-in passes the same checks, in this order, before it gets a session.
- 1Passwordbcrypt hash
- 2Rate limitthen lockout
- 3Two-factorTOTP code
- 4SessionhttpOnly cookie
Two-factor authentication
Users can enrol any TOTP authenticator app. Enrolment issues one-time recovery codes, shown once and stored hashed. Each recovery code works exactly once.
Rate limiting and lockout
Sign-in attempts are rate limited per client address. After repeated failures an account is locked for a few minutes (unknown emails behave the same, so lockouts do not reveal which accounts exist). Lockouts end on their own (the platform admin can lift one early), are audited and are sent to the notification channels of every workspace the account belongs to.
Require two-factor for everyone
Set REQUIRE_MFA=true and every user must enrol an authenticator before using the panel.
No default credentials in production
The first admin is created from ADMIN_EMAIL and ADMIN_PASSWORD. If that is still a documented default, the panel forces a password change at first sign-in before anything else is reachable, and production mode refuses to start with it at all. Passwords are hashed with bcrypt.
Optional IP allowlist
Set ALLOWED_CIDRS to restrict the panel to known networks, such as office or VPN ranges. Agents, webhooks and the installer stay reachable so servers keep working.
Sessions and tokens
Identity- httpOnly, SameSite=Strict cookies. The refresh token lives in a cookie that page scripts cannot read, scoped to the auth endpoints and marked Secure over HTTPS. The short-lived access token is kept in memory. Tokens are never stored in
localStorage. - Short-lived access tokens. Access tokens last 15 minutes; refresh tokens rotate on every use.
- Reuse detection. If an already-used refresh token is presented, the whole session family is revoked, which ends a session stolen by an attacker as well as the victim's copy, and raises a security alert.
- Revocable sessions. See every active session with device and address, and sign out any of them or every other device at once. Disabling a user blocks all of their tokens immediately.
- Personal API tokens. Tokens for scripts and CI are shown once and stored as SHA-256 hashes. Each is limited to one workspace and to the role you pick, never more than your own, and can expire. They can't change the account, its two-factor or other tokens, and every change made with one is marked in the audit log.
- Idle timeout. Inactive sessions are signed out automatically (
SESSION_IDLE_HOURS); the web terminal closes after 30 minutes idle.
Roles and permissions
IdentityRoles are checked on every API endpoint on the server side, not just hidden in the UI.
- Metrics, servers, containers, deployments, alerts
- Viewer Developer Admin
- Deploy, create and edit applications, container actions and logs, ack alerts
- Viewer Developer Admin
- Read firewall, backups and updates
- Viewer Developer Admin
- Create databases and view the credentials of their own (audited)
- Viewer Developer Admin
- Change where an app with secrets comes from, image variants, deploy a chosen Compose commit
- Viewer Developer Admin
- Servers, systemd services and journal, users, audit log, delete applications
- Viewer Developer Admin
- Web terminal, browse, view and edit files, cron, firewall, upgrades and reboot, kill processes, notification channels
- Viewer Developer Admin
| Capability | Viewer | Developer | Admin |
|---|---|---|---|
| Metrics, servers, containers, deployments, alerts | |||
| Deploy, create and edit applications, container actions and logs, ack alerts | |||
| Read firewall, backups and updates | |||
| Create databases and view the credentials of their own (audited) | |||
| Change where an app with secrets comes from, image variants, deploy a chosen Compose commit | |||
| Servers, systemd services and journal, users, audit log, delete applications | |||
| Web terminal, browse, view and edit files, cron, firewall, upgrades and reboot, kill processes, notification channels |
Browser hardening
Platform- Nonce-based Content Security Policy. Only scripts carrying the per-request nonce run. No inline event handlers, and no third-party scripts in the panel.
- Strict security headers. HSTS over HTTPS,
X-Content-Type-Options: nosniff,X-Frame-Options: DENYagainst clickjacking,Referrer-Policy: no-referreron panel pages, a cross-origin opener policy and a restrictive permissions policy. - SameSite cookies protect state-changing requests from cross-site forgery.
- Production mode. With
YP_ENV=productionthe control plane refuses to start with unsafe settings, such as a shortJWT_SECRET, a missingENCRYPTION_KEY, the documented default admin password or a non-HTTPSPUBLIC_URL, and hides the interactive API docs.
Agent boundaries
Platform- Allowlisted actions. The agent runs only the documented actions. Commands use argument arrays, never a shell string.
- Validated inputs. Container names, images, branches, commits, env names and paths are validated on the agent itself.
- Service allowlist. systemd units must be listed in
allowed_servicesbefore they can be controlled. - Path confinement. The file manager reaches only paths inside
file_roots. Paths are resolved through symlinks before the check, top-level roots cannot be moved or deleted, and/etc/yourpanelis never reachable. - Terminal controls. Admin only, requires a fresh token on connect, audited with duration, and can be disabled per server with
"terminal_enabled": false. - Checked agent updates. A new agent is installed only if its SHA-256 matches and it starts, and the previous one comes back on its own if the new one can't reconnect. Audited, and can be refused per server with
"self_update": false. - Safe defaults. The firewall allows your SSH port before ufw is enabled. Nginx changes are validated with
nginx -tand reverted if invalid. Databases listen on 127.0.0.1 only.
Secrets and data
PlatformApplication env vars, git access tokens and database passwords are encrypted at rest with Fernet using ENCRYPTION_KEY. The API returns only key names, never values, and secrets are redacted from deployment logs. Build checkouts are deleted after each build.
Each workspace's data is isolated from every other workspace. Nothing is sent to a third party unless you configure a notification channel.
Tamper-evident audit log
PlatformSign-ins, lockouts, server and user changes, container and service actions, deploys, terminal sessions and credential reveals are recorded with actor and IP address.
Each entry stores a hash of its content chained to the previous entry's hash. Editing or deleting a past entry breaks the chain, and the panel can verify the chain end to end, so silent tampering is detectable.
Your checklist
Operate- Invite each person with their own account instead of sharing a login, and give them the lowest role that works.
- Turn on two-factor authentication for every admin in your workspace.
- Keep
allowed_servicesandfile_rootsminimal; disable the terminal on servers that do not need it. - Back up your databases from the Databases page before risky changes. The last seven backups are kept on the server.
Known limitations
OperateBeing honest about the edges helps you decide what else to put around the panel.
ENCRYPTION_KEYlives in the control plane's environment. Anyone with both the host and the database can decrypt secrets. Use a KMS or Vault if that is not acceptable.- Secrets reach containers as environment variables, which are visible to
docker inspecton that server. - The agent runs as root by design so it can manage packages, services and the firewall.
Responsible disclosure
OperateIf you believe you have found a vulnerability, please report it privately so it can be fixed before details are public.
- Open a private report by emailing [email protected]. Do not disclose details publicly.
- Include the affected version, steps to reproduce and the impact you observed.
- Give us a reasonable window to ship a fix before disclosing. You will be credited in the release notes if you wish.