Read-only API Keys
Available from 1.4.3. Set read_only: true when creating a key. Verify that the creation response contains api_key.read_only: true and that GET /api/v1/settings with that key returns 403. This probe does not change settings on an older server. Older servers can ignore unknown request fields; a successful creation alone does not prove enforcement.
The restriction allows only these reviewed GET operations, in addition to unauthenticated public endpoints:
/networks, /hierarchy/tree
/devices, /devices/{deviceID}, /devices/{deviceID}/interfaces
/links, /links/{linkID}
/events, /alerts, /alerts/{alertID}
/reports/change-summary, /reports/congestion
Other operations return 403, including configuration changes, alert actions, terminal access, WebSockets, backups and operations added in the future. The role and owner's current permissions still apply; this restriction never grants additional access. Reads still generate ordinary access/usage bookkeeping and may consume computation resources. It is not a network/tenant scope restriction.
Existing keys and requests that omit read_only retain their previous role-based behavior. The creation form defaults the checkbox on. To change a key's restriction, create a replacement and revoke the old key. Before downgrading to a release without this feature, revoke restricted keys; older binaries do not enforce the restriction.
Denied-request audit: a valid, active, unexpired key rejected by this policy produces a sampled api_key.read_only_denied audit event and server warning. Entity type is api_key; entity ID is the key's UUID. Detail includes normalized HTTP method, status 403, whether an upgrade was requested, and suppressed_since_last_record (other denials suppressed across all keys in that API process, not a count belonging to this key). URLs, query strings, request bodies, key names, prefixes, hashes and raw keys are not recorded by this mechanism.
Sampling permits one event per key per minute and ten per minute globally (burst ten) per API process. Storage concurrency is capped at two and each write has a 250 ms context deadline. Persistence failure never permits the request; it emits a bounded server warning. Audit retention follows the normal audit-log policy (90 days by default); server-log retention is managed separately. This is best-effort misuse visibility, not a complete request history: suppression state resets on restart, multiple API processes have independent budgets, and saturated budgets can suppress a different key's first event. Denials do not update last_used_at. Invalid, expired or revoked keys do not enter this policy-specific audit path.