Manef Shell OS is Public Alpha / Developer Preview software. It has not had a
third-party security audit. Only the latest commit on main is supported; there
are no release branches yet.
Please do not open a public issue for security vulnerabilities.
Use GitHub's private vulnerability reporting flow for this repository:
- Open the repository on GitHub.
- Go to Security → Report a vulnerability.
- Include the affected version or commit, reproduction steps, impact, and any logs with secrets removed.
If private vulnerability reporting is unavailable, open a minimal public issue asking the maintainer to enable private advisory intake. Do not include exploit details, passwords, session secrets, API keys, private file contents, or full environment files in that public issue.
An authenticated MSO session can run commands and access files as the Linux user that owns the process. Treat it like SSH in a browser.
- Run MSO as a dedicated non-root user.
- Prefer Tailscale or another VPN for real deployments.
- If using a public domain, put HTTPS, firewall rules, and strict access control in front of the app.
- Do not expose the raw app port directly to the public internet.
- Do not commit
.env.local, API keys, or data from~/.mso. - Use demo mode (
NEXT_PUBLIC_OS_DEMO=1) for public showcases.
MSO can manage separate applications that already run on the box (Hermes, OpenClaw) and frame each one's own dashboard in a window. Those apps keep their own runtime, config, data and privileges; MSO talks to them through their CLIs, their HTTP surfaces and systemd, and writes nothing into their state directories.
The dashboards are framed with allow-same-origin, because their SPAs do not boot
without it. On the cockpit's own origin that also made upstream JavaScript same-origin
with the cockpit, so it could take window.top.fetch and call /api/v1/exec/run
with the signed-in session. No Content-Security-Policy can prevent that: a policy binds
a realm, not a reference held across realms. Each dashboard is therefore served from its
own host on the same process (hermes.os.<domain>, openclaw.os.<domain>), which makes
window.top cross-origin and opaque. Middleware enforces the other half: on one of those
hosts every path is rewritten into that app's dashboard proxy (and /_next/* is refused
outright), so no cockpit route — not /api/v1/exec/run, not /api/auth/*, not a page — can
be reached there at all.
This boundary is browser-realm only. A plugin or extension installed into Hermes or OpenClaw runs inside that daemon, with that daemon's privileges, and those daemons execute shell commands on the host. Installing an untrusted plugin into either application is handing it arbitrary code execution as the user that runs it. Separate origins do not change that, and neither does anything else MSO can do.
Operational rules for anyone maintaining a split-origin deployment:
- Never create a
*.os.<domain>wildcard record, and never point a newX.os.<domain>name at the app port without deciding what serves it. The session cookie is widened to that whole namespace. MSO 404s unknown names in the namespace as a backstop, but DNS is the primary control. NEXT_PUBLIC_MANAGED_APP_HOST_TEMPLATEandOS_SESSION_COOKIE_DOMAINare one decision. Set both or neither. The second alone widens the session cookie to every host under the domain while each of them still serves the full cockpit API.SameSite=Strictkeeps working only while the app hosts share the cockpit's registrable domain. Moving one to a different domain makes those requests cross-site and the framed dashboard stops authenticating; that is a design change, not a config tweak.- Leaving both variables unset is the default and is safe: MSO then serves no upstream dashboard at all, and the feature windows fall back to their CLI view. The old single-origin mode (dashboards under a path on the cockpit origin) has been removed — it handed upstream JS a realm holding your session, and no header could take it back.
- MSO expects HTTPS (or
localhost). The session cookie isSecure, so a plain-http deployment on an IP cannot log in at all — that is deliberate, not a bug to work around. - A managed-app backup copies that application's state directory into
~/.mso/backups/<app>/<timestamp>/, including any credentials the application keeps there —~/.openclaw/credentials/andidentity/are in scope, and so is anything secret inopenclaw.jsonor under~/.hermes. Treat those copies exactly like the application's own config: never publish them, and include them when you rotate that application's secrets. The copy directory is created0700and the manifest0600, but nothing encrypts the contents. Symlinks are skipped rather than followed, so a link inside the state dir cannot pull bytes in from outside it, and a restore cannot be aimed outside the tree by an absolute link (docs/MANAGED-APPS.md §6).
- Auth bypass: session forgery without
OS_SESSION_SECRET, device-approval bypass, or rate-limit defeat that enables practical brute force. - Filesystem jail escape: reading or writing outside
OS_FS_READ_ROOTS/OS_FS_WRITE_ROOTS, or reaching denied credential material such as.env*or~/.mso/*through the file APIs. - Unauthenticated access to live host routes such as
/api/v1/*,/api/assistant,/api/config, or/api/auth/devices. - CSRF or clickjacking that triggers host actions cross-origin.
- Escape from a managed-app origin: reaching any cockpit route, the cockpit realm, or a
sibling managed app's origin from inside a framed dashboard on
hermes.os.<domain>/openclaw.os.<domain>, or getting the proxy to fetch something other than its own loopback upstream.
- An already-authenticated owner session doing documented owner actions such as running commands or accessing files within configured roots.
- Bypassing the destructive-command guard with shell tricks. It is an accident tripwire, not a sandbox.
- Deployments that ignore the minimum posture: non-root user, strong secrets, Tailscale/VPN or protected HTTPS, and narrow filesystem roots.
- What a plugin installed into Hermes or OpenClaw can do inside that daemon, including running host commands. That is the application's trust model, not MSO's boundary.
- Single-origin mode (
NEXT_PUBLIC_MANAGED_APP_HOST_TEMPLATEunset) behaving as documented: a framed dashboard's JavaScript can reach the cockpit realm there. Serving the dashboards from their own origins is the fix, and it is a deployment choice.
BYOK AI credentials are stored server-side in ~/.mso/config.json, never in
the client bundle. To rotate a key, stop MSO, edit the config file or remove the
provider from Settings → AI, then restart/sign in again.
To rotate auth secrets:
- Change
OS_SESSION_SECRETand restart to invalidate sessions. - Change
OS_LOGIN_PASSWORDand restart to require the new password. - Remove entries from
~/.mso/auth-devices.jsonto revoke devices.
The JSONL audit trail defaults to ~/.mso/audit.log and can grow over time.
Use logrotate or your normal host log retention system. Do not publish audit
logs without checking for private paths, command names, or other sensitive
context.