Please report suspected vulnerabilities privately through GitHub's security advisory reporting flow for this repository. Do not open a public issue or include working credentials, access tokens, or exploit details in a public discussion.
If private reporting is unavailable, contact the repository owner privately and include the affected version, reproduction steps, and impact.
Repository administrators should enable the dependency graph, Dependabot alerts,
secret scanning with push protection, and branch protection requiring the
security checks and the Quality workflow's Lint and typecheck and Tests
checks before merging. Actions should be restricted to
trusted or explicitly approved actions, with read-only default GITHUB_TOKEN
permissions.
- Complete first-run onboarding before exposing the instance to untrusted traffic; the first account created becomes the administrator.
- Set a unique production
APP_KEYand keep.envoutside version control. The server generates and persists one in its data directory (/app/tmpin the container) when it is omitted. It refuses to start in production with a key published in this repository: the key the tests use, or the placeholder that images of earlier versions were built with. - Set
APP_URLexplicitly to the HTTPS URL used by the deployment. In Coolify, set it to the same public origin assigned to the service. - Set
TRUST_PROXYto the proxies in front of the instance. The Compose and Coolify default,loopback,uniquelocal, trusts proxies on the loopback interface and on private networks. Usetrueonly when the proxy overwritesX-Forwarded-For; otherwise clients choose the address that rate limits and call logs record. - Persist
/app/tmp; losing it also loses the SQLite database, encrypted MCP secrets, generated key, and Deno sandbox cache. The server creates its files there withumask 077; a volume created by an earlier version keeps its existing modes until you runchmod -R go-rwx /app/tmpin the container. - Put the application behind a TLS-terminating reverse proxy rather than exposing the container directly to the public internet.
- Treat configured npm MCP packages as executable third-party code. Pin versions and use a separate, restricted deployment boundary for untrusted packages.
- Invite only trusted operators. Members can manage the shared MCP registry and gateway access tokens, so membership is not a read-only role.
- Revoke access tokens when a user or upstream integration is no longer trusted.
- Sign-in attempts are counted before the password is checked and limited per account and client address, and per client address across accounts. IPv6 clients are counted by /64. Current-password confirmations in settings are limited per user.
- Browser sessions are checked on the server on every request. They end after
2 hours idle or 24 hours, and signing out, changing the password, or running
user:reset-passwordrevokes the account's sessions, including a copied session cookie. Password changes and resets also revoke every remember-me token. - The gateway authorization endpoint records a decision only from the CSRF-protected consent form, and sends a rejected request back only to a redirect URI on the user's own device.
- Each access token is limited to 600 gateway requests per minute. Dynamically registered OAuth clients are capped at 1000, and clients unused for 90 days are removed.
- Multipart request bodies are ignored; the instance never writes uploads to disk.
- Authenticated upstream requests and OAuth token requests follow redirects only within the same origin. Upstream responses are limited to 32 MiB, and error responses to 64 KiB.
- npm MCP packages run in a Deno sandbox that can read and write only its own
directory, less the files Deno itself reads there when it starts (
.npmrc,deno.json,deno.jsonc,package.json). Environment variables that would configure the sandbox itself, such asPATH,SSLKEYLOGFILEand loader or Deno runtime variables, are refused. All npm MCPs share one Deno cache that packages can read but not write. - A saved bearer token, header value, or environment value is not carried over when an MCP is pointed at another origin, transport, or npm package.
- An MCP on a public address cannot send the instance to OAuth endpoints on loopback, private, or link-local addresses, and its OAuth resource indicator must match the MCP URL. MCP URLs entered by an operator may still point at private addresses. Starting OAuth with a different provider or client drops the saved tokens first.
- MCP and OAuth endpoints must use HTTP(S). Query parameters and embedded URL credentials are supported; prefer the encrypted authentication fields when the provider allows them.
- Upstream error details are redacted before logging or display, and optional MCP argument/response captures are capped at 64 KiB per field.
- The browser UI sends a restrictive Content Security Policy with per-response script nonces. Page data embedded in a full page load is escaped so that stored text, such as a logged tool name or an MCP description, cannot break out of its script element, and page data kept in the browser history is encrypted and unreadable after sign-out.
- Mail written by strangers is converted to text in a separate short-lived process, and attachment links check their signature before a download is counted against the per-MCP limits.
- The only route that takes a file is the temporary signed upload link a built-in MCP hands to an agent. The signature is checked before the body is read, a link takes one file, and the file is deleted an hour after its upload. Each MCP is limited in how many files and bytes wait for it. A mail attachment is always bytes sent to such a link: no tool can name a path on the instance or a URL to attach.
- The container image builds from base images pinned by digest: the Rust
toolchain that compiles the server, the Debian runtime, and the Deno binary
that sandboxes npm MCP packages. The build refuses a
Cargo.lockthat does not match the manifests. Dependabot proposes updates for the images, for Cargo dependencies, and for GitHub Actions. - The image runs the server as an unprivileged user, uid 1000, under an init
that reaps the Deno child processes. Before an image is published, a
container of each architecture is started without capabilities and checked:
it must answer
/health, run as uid 1000, and leave nothing in its data directory that another user can read. - Release workflows compile and run dependency code with a read-only token. Only a separate job can push the tag and create the release, and it publishes the exact commit that was validated. That job restores no cache, and the only code it compiles is the release tool of this repository, which has no dependencies.
- Pull requests and pushes to
mainrun the format check, Clippy with warnings denied, the tests of every crate, and a release build. The security checks auditCargo.lockagainst the RustSec advisory database, analyse the code with CodeQL, and scan the history for secrets.