monoki docs Install Source

Security

What monoki defends against, how, and — just as importantly — what it does not.

Authentication

Session cookies only. No API keys, no OAuth, no tokens to leak in a shell history.

Authorization

Every access decision lives in one file (internal/api/authz.go); the storage layer enforces nothing and takes no "acting user" argument, so there is exactly one place to audit.

Tab reuse and noopener

By default a tile carries a stable window name, so clicking Grafana five times gives you one Grafana tab rather than five. That cannot be combined with rel="noopener", and the trade-off is worth stating plainly.

When noopener is set, the spec forces the target to _blank in a new browsing context group whose name the opener can never find again — so every click would spawn a fresh tab, which is the exact opposite of the feature. So the reuse path omits noopener and severs the link itself immediately afterwards (w.opener = null, which is settable cross-origin and is the pre-rel defence against reverse tabnabbing).

That is weaker than rel="noopener", not equivalent: it is best-effort, and there is a brief window before it runs. It is proportionate here because monoki points at apps you run and URLs you pasted, not at arbitrary web content.

If you would rather not make that trade, set an app's link target to always open in a new tab — that path uses noopener,noreferrer and gives up reuse. The tile menu's Open in a new tab does the same for a single click.

Referer is suppressed globally by a <meta name="referrer" content="no-referrer"> rather than per-link, which covers more than the attribute did (subresources, and the menu's own links) and avoids noreferrer, which implies noopener.

Server-side fetching (SSRF)

monoki fetches URLs a user typed, from inside your network. This is its one genuinely risky surface, and it is treated that way.

MONOKI_ALLOW_PRIVATE_FETCH defaults to true, which is the deliberate exception: bookmarking http://nas.lan:5000 is the product's main use case, and it requires reaching a private address. Set it to false if your instance has users you would not trust with a curl on the server. All the other controls above apply either way. See Configuration.

Stored icons

Icon bytes come from arbitrary sites, and monoki serves them from its own origin — so every icon response carries X-Content-Type-Options: nosniff, Content-Security-Policy: default-src 'none'; style-src 'unsafe-inline'; sandbox, and Content-Disposition: inline. SVG is accepted because too many modern sites offer nothing else; the UI only ever renders icons through an <img> tag, which does not execute SVG script regardless.

Audit log

Every mutating action writes a row with a dotted action name — group.share, app.update, user.update. It is readable by admins.

The log deliberately has no foreign key to the accounts table, so deleting a user leaves their trail intact rather than cascading it away.

What monoki does not do

Stated plainly, because a security page that only lists strengths is not much use:

Reporting a problem

Open a confidential issue on the GitLab project rather than a public one.