NOVUS / RESTAURANT

Documentation

Personal access tokens and what they can reach

How a token is minted, why it is shown only once, the quota on issuance, and the single endpoint it authenticates.

Last reviewed against the product on .

A personal access token is the credential an AI client uses to read this workspace. This page says what a token reaches, what it cannot reach, and why the site shows you the value exactly once.

Where tokens are made

On settings, under "Read-only API tokens". Name it after the thing that will use it, because the name is all you will have to go on when you decide months later whether it is still needed. The value appears once, immediately after creation, and is never shown again.

Why it can only be shown once

The database stores a SHA-256 hash of the token and a short prefix in clear. The prefix is what the settings list and the export show, so you can tell two tokens apart; the hash is what an incoming request is matched against. Nothing in the system holds the value itself, so nothing can reprint it. A lost token is replaced rather than recovered.

Prefix
nrv_
followed by 32 random bytes, base64url encoded
Stored in clear
12 characters
the rest exists only as a SHA-256 hash
Scope issued
read
the only scope this site mints
Expiry from the interface
90 days
set for you; the form offers no other value
Expiry the endpoint accepts
1 to 365 days
optional, and omitting it mints a token with no expiry at all

Measured from issuePat in src/lib/mcp/tokens.ts and the create form in src/components/settings/token-manager.tsx.

The endpoint behind the form

What each method on /api/tokens does
MethodWhat it doesDurable quota
GETLists your live tokens with name, prefix, scope, last use and expiry. Revoked tokens are not listed.None
POSTMints one token and returns it once. Accepts a name and an optional expiry in days.10 per user per hour
DELETERevokes one token by id.None

Measured from app/api/tokens/route.ts and DURABLE_POLICIES in src/lib/security/api-rate-limit.ts.

Issuance is the only one of the three that is quota-limited, and it carries the tightest quota in the repo. The reason is that minting is the single action here that produces a credential outliving the session that made it: a loop against it turns one borrowed session into an unbounded number of working bearer tokens. Ten an hour is far above any real use, since an operator mints one per client, once.

Revocation deliberately carries no quota. It only ever reduces privilege, and a quota on it would mean someone responding to a leaked token could be told to come back in an hour.

Burst brake
60 per minute
per address, on all three methods, applied before the session is looked up
Issuance quota
10 per hour
per user, counted in the database so it survives cold starts

Measured from RATE_POLICIES.publicRead in src/lib/shared/rate-limit.ts and the brake calls in app/api/tokens/route.ts.

The two limits do different jobs. The brake lives in each server's memory and exists to stop a flood reaching the database at all; the quota lives in the database and is the number that actually holds when several instances are warm. The quota is keyed by user rather than by address on purpose: keying it by address would let one restaurant office's staff exhaust each other's allowance, while an attacker with a stolen cookie simply moved.

What a token cannot do

A token authenticates the MCP server and nothing else. No other route on this site accepts one, so it is not a general API key and there is nothing it can be pointed at except the MCP endpoint.

It is read-only. The only scope ever issued is read, and none of the tools behind it writes, imports or changes anything.

It carries the organization it was made in, not your role. Every token reaches the same read-only tools, so a viewer's token and an owner's token see the same thing. The organization boundary still holds: there is no way to reach another organization's records with it.

You see and revoke only your own tokens. Another member's token is not in your list, and a delete naming it changes nothing.

Revoking, and knowing when to

Every authenticated request re-reads the token record and refuses one that has been revoked or has passed its expiry, so revocation takes effect on the next request rather than at the end of a session.

Each successful request also stamps the token's last-used time. That is the field to check before revoking something you no longer recognise: a token with no last use has never been used by anything.

There is no rotation control. Rotating means minting the replacement, moving your client onto it, and then revoking the old one, in that order.

Your token names and metadata, though never the values, are included in the personal-data export described in your data and privacy.