Security & permissions
Roles and what each can do, personal-access-token scopes, how authentication works, and good token hygiene.
This page explains who can do what in Data Hub: the two roles, the scope system that gates token-authenticated requests, and how to handle tokens safely. For the mechanics of creating and revoking tokens, see Managing tokens.
Authentication
The API accepts two kinds of credentials:
- Session cookies: used by the web dashboard (Google OAuth via NextAuth). Session callers implicitly hold every scope; scope checks only apply to token requests.
- Bearer tokens: personal access tokens (
dhub_…) used by the watcher, the Lambda, and MCP clients, sent asAuthorization: Bearer <token>.
Tokens are hashed with SHA-256 before storage; the plaintext is shown exactly once, at creation.
Roles
| Role | Can do |
|---|---|
| Member | Sign in to the dashboard; view instruments, runs, files, and the token audit list. Cannot mint or delete tokens, confirm/update instruments, or manage other users. |
| Admin | Everything members can, plus: confirm and update instruments, create and revoke any user's tokens, and promote/demote other users. |
The first admin is bootstrapped from the ADMIN_EMAILS environment variable
(comma-separated, case-insensitive); listed users are promoted on every sign-in.
After that, any admin can promote others from Settings → Members. Admins
cannot demote themselves: that guards against locking the workspace out of its
last admin.
Admin-gated operations
A subset of mutations require the admin role in addition to (or instead of) a scope check:
PATCH /api/v1/instruments/:id: session callers must be admin (bearer-token automation still usesinstruments:write, so watcher/Lambda flows are unaffected).POST /api/v1/tokensandDELETE /api/v1/tokens/:id: admin-only, session-only. Bearer tokens cannot manage other tokens.GET /api/v1/users,PATCH /api/v1/users/:userId: admin-only, session-only. Used by Settings → Members.
Token scopes
Every personal access token carries an array of permission scopes. A
token-authenticated request is rejected with 403 FORBIDDEN when the token's
scopes don't include the scope a route requires. Issue tokens with the least
privilege they need.
| Scope | Grants |
|---|---|
instruments:read | List/read instruments |
instruments:write | Create/update instruments |
runs:read | List/read runs and their comments |
runs:write | Create/update/delete runs, comments, attributions, and run-level upload/reprocess endpoints |
files:read | Read file metadata, download files, download run archives |
files:write | Create/update/delete file records and reprocess files |
watchers:read | Read watcher state (list, heartbeats, events, config, upload queue, update-check) |
watchers:write | Register/deregister watchers, post heartbeats and events, push config |
archive-jobs:read | Read archive job state (reserved for future endpoints) |
archive-jobs:write | Update archive jobs (Lambda callback) |
* | Wildcard: matches every scope. Reserved for legacy backfilled tokens; POST /api/v1/tokens rejects * from API callers. |
MCP tools enforce the same scopes as their REST counterparts: a token with
runs:read over REST also covers the read-side run tools over MCP. There's no
MCP-specific scope vocabulary.
A typical watcher token needs instruments:read, instruments:write,
runs:write, files:write, and watchers:write. A read-only MCP/analysis
token needs the :read scopes for the data you want to query.
Token hygiene
- Name tokens descriptively (e.g. “FPLC watcher – Lab 201”) so you can tell what each one is for.
- Set expirations for temporary setups.
- Scope to least privilege: don't hand out broad tokens for a single read-only integration.
- Revoke immediately when a watcher is decommissioned or a token may have been exposed. Revocation is instant; clients using the token start getting
401 Unauthorized. See After revoking a token.
Public pages, gated bodies
Some web pages (dashboard, instruments, run detail, settings) are reachable without
a session so link unfurlers can read page metadata. The page body renders a
sign-in prompt instead of real data when there's no session, and three independent
layers (robots metadata, robots.txt, and an X-Robots-Tag header) keep the
product app out of search indexes. The /api/v1/* surface always requires a
session cookie or bearer token.