Administrator protection
A workspace started with MSK_ADMIN_READ_ONLY treats every administrator as a read-only
administrator. Nobody can change the configuration — not the seeded admin account, not an account
promoted through SSO, not a Library token issued by an administrator — until the restriction is
lifted with a one-time code the workspace writes into its own storage folder, or with a token
Curiosity signs for that one deployment.
The point is to separate two things that are normally the same: holding an administrator login, and being able to change the workspace. With this on, an attacker who phishes an admin password, or an administrator acting alone, still cannot rewrite an AI tool, add an endpoint, change access control, or issue a token. Lifting the restriction takes a second kind of access — the server's filesystem, or a token from Curiosity — and it lasts for a bounded period, between 30 minutes and 8 hours.
Who this is for
Deployments where admin access is meant to be exercised deliberately and rarely: regulated environments, workspaces holding sensitive content, and any deployment where the people who administer the workspace are not the people who run the server. It is off by default.
Turning it on
Set the variable and restart the workspace:
-e MSK_ADMIN_READ_ONLY=true
The value is read from the process configuration only — an environment variable, a mosaik.yml /
curiosity.yml entry, or a --MSK_ADMIN_READ_ONLY command-line flag. It is never read from the
workspace database, so it cannot be turned off from inside the product by anyone who gets admin
access.
Anything except an explicitly falsy value turns the protection on. false, 0, no, off,
disabled and an empty value leave it off; every other spelling enables it, so a typo fails closed
rather than silently leaving the workspace open.
On start-up the log confirms it:
Administrator accounts are read-only (MSK_ADMIN_READ_ONLY); writing is unlocked with a one-time code written to /data/curiosity
What changes while it is on
Reads are untouched. An administrator still sees every admin page, every setting, every log and every audit entry — the workspace looks the same. What fails is the write:
- Every endpoint that requires a full administrator answers 403 Forbidden. In the interface this surfaces as a toast pointing at the page below.
- Holding the
SystemAdminrole in an endpoint's allow-list does not get around it, and neither does a Library or API token issued by an administrator: the check is on the write, not on how the caller authenticated. - Sudo, the admin assistant, still reads the
workspace and drafts in his own copy, and says so when asked. What he cannot do is hand a change
over (
commit) or run stored code (run) — the same two things a read-only administrator cannot ask him for. - Ordinary users are unaffected. Searching, chatting, uploading and everything else a non-admin does goes on as normal, and so does ingestion through connectors and scheduled tasks.
A read-only administrator — an account with the ReadOnlySystemAdmin role rather than
SystemAdmin — is unchanged by all of this. They were already read-only, and they cannot lift the
restriction.
Lifting it
Manage → Access → Administrator Protection (#/manage/access/admin-protection) runs a two-step
hand-shake. Only a full administrator sees the controls; a read-only administrator sees the current
state and nothing else.
Ask the server for a code
Click Write a new code to the storage folder. The workspace generates a one-time code and writes
it to admin-unlock-code.txt in the storage folder (MSK_GRAPH_STORAGE), owner-readable only on
Linux and macOS. The page reports the full path. The code is not shown in the interface and is
not returned over the API.
Read the code off the server
Open the file wherever the storage volume is reachable — a shell on the host, kubectl exec, the
mounted volume:
cat /data/curiosity/admin-unlock-code.txt
Type it back, with a duration
Paste the code into the page, pick how long administrators may write (30 minutes, 1, 2, 4 or 8 hours), and click Unlock. The file is deleted as soon as the code is used.
The code is good for 15 minutes and 5 attempts. After either runs out it stops working and the file is removed; ask for a new one. Requesting a code replaces any code still pending.
Once unlocked, every administrator on the workspace can write for the period chosen — the unlock is workspace-wide, not per-account. The page shows how long is left and who lifted it, and Make administrators read-only again ends the period early.
Keep the file out of backups and log shipping
admin-unlock-code.txt lives in the storage folder, which is usually the volume that gets backed
up. It is short-lived and single-use, so the exposure is small, but if your backup runs every few
minutes it is worth excluding the file by name.
When the storage folder is out of reach
A hosted deployment gives nobody a shell on the server, so there is no file to read. The same box on the same page also accepts a token Curiosity signs for your deployment: paste it in place of the code, pick a duration, and click Unlock. There is no second endpoint, no second field, and the page does not ask which of the two you are holding.
- The value is tried as a signed token first, and falls through to the code comparison when it is not one. A value that is neither counts as a wrong code and costs one of the five attempts.
- The token is checked against the same Curiosity public key a license is checked against, so an
administrator can neither mint one nor extend one. It has to carry the
ADMIN-UNLOCKpurpose and the deployment identifier of this workspace — a token minted for another workspace unlocks nothing. The identifier is compared exactly, so its casing matters. - Three months is the ceiling. A token signed for a longer period is refused whatever signed it.
- It buys exactly what a code buys: the same 30 minutes to 8 hours, workspace-wide, and any code still pending in the storage folder is consumed along with it.
- The audit entry says which of the two lifted the lock, and for a token, who it was issued to.
- The start-up log line still mentions only the one-time code, by design.
Ask for one through your Curiosity support channel, saying which deployment it is for and how long you need. Treat it as a credential for the period it covers: unlike the one-time code it is not consumed by being used, and it keeps working until it expires.
Operational notes
- The state lives in the server process, in memory. A restart puts the protection back on, even in the middle of an unlock period. That is deliberate: a workspace comes up locked, always.
- Each process decides for itself. In a multi-server deployment, unlocking one server does not unlock the others — set the variable on every server that serves the admin interface, and unlock on the one you are working through. Read-only replicas reject writes anyway, so the variable matters on the primary.
- Everything is audited as an Admin Action: the code request (with the path it was written to), the unlock (with the period granted and which of the two mechanisms was used), every failed attempt with its reason, and the early re-lock. See the audit log.
- Plan for losing filesystem access. If the only person who can read the storage folder is unavailable, the workspace cannot be reconfigured until they are — that is the control working as intended, but it belongs in your runbook alongside the recovery paths (a Curiosity-signed token, or clearing the variable and restarting, which needs the same kind of access).
Related
- Security — the operational security baseline this sits inside.
- Permissions — the
SystemAdminandReadOnlySystemAdminroles. - Configuration reference
—
MSK_ADMIN_READ_ONLYalongside the other identity variables. - The admin console — where the page sits.