Administration
This page is for key administrators. Administration still uses the /v1
endpoints, which remain available after the retirement of the v1 data APIs.
How permissions work
| Term | Meaning |
|---|---|
| Principal | A person or a system. It has a grant (the maximum it may hold) and optionally a usage policy |
| Grant | The maximum data scope, capabilities and licences of a principal |
| Key | Belongs to a principal and has its own data scope and capabilities, inside the principal's grant. One principal can have several keys, for example one personal and one for an agent |
| Effective permission | The intersection of the key's scope and the principal's grant. A request that exceeds either is refused as a whole |
An administrator can only manage principals and keys whose permissions lie inside their own, and cannot issue permissions larger than their own.
Data scope
| Field | Type | Meaning |
|---|---|---|
kinds | string[] | Data kinds, for example ["book_snapshot"] |
reference | bool | Reference data |
venues, all_venues | string[], bool | Venues, or all |
instruments, all_instruments | int[], bool | Instrument ids, or all |
time | {start, end} | Event-time range; omitted means no limit |
history | bool | History reads and exports |
live | bool | Live subscriptions |
book_depth, all_book_depth | int, bool | Book levels, or all |
export | bool | Create exports |
venues and instruments are stored but are not yet applied to /v2
requests; use kinds, history, live, export and book depth to control
access today.
Capabilities
| Capability | Allows |
|---|---|
admin.principals | Create principals and change their grants and policies |
admin.keys | Issue, list, show, revoke and rotate keys; change a key's scope and expiry |
admin.quota | Create and change usage policies; assign one to a principal |
admin.data | Data management, derivations and live-bar jobs |
admin.reference | Reference data and bar specifications |
Give administration capabilities only to administrators. A normal user's grant has none.
Create a user
ADMIN='vmk_...'
API=https://data.cyberun.cloud
H=(-H "Authorization: Bearer $ADMIN" -H "Content-Type: application/json")
# 1. Optional: a usage policy shared by several principals
curl -s "${H[@]}" $API/v2/admin/quota/policies -d '{
"name": "trader", "requests_per_min": 600, "output_bytes_per_min": 536870912,
"jobs_per_hour": 20, "concurrency": 4,
"subscriptions": 4, "event_bytes_per_min": 16777216, "scanned_bytes_per_min": 1073741824}'
# -> {"id": "<policy-id>"}
# 2. Principal: grant is the ceiling; normal users get no capabilities
curl -s "${H[@]}" $API/v2/admin/principals -d '{
"name": "trader-alice",
"grant": {"data": {"kinds": ["book_snapshot"], "reference": true,
"all_venues": true, "all_instruments": true,
"history": true, "live": true, "export": true,
"all_book_depth": true},
"caps": []},
"policy": "<policy-id>"}'
# -> {"id": "<principal-id>"}
# 3. Key: the secret appears only in this answer
curl -s "${H[@]}" $API/v2/admin/keys -d '{
"owner": "<principal-id>",
"scope": {"data": {"kinds": ["book_snapshot"], "reference": true,
"all_venues": true, "all_instruments": true,
"history": true, "live": true, "export": true,
"all_book_depth": true},
"caps": []},
"expires_in": "2160h"}'
# -> {"key": "vmk_...", "record": {"id": "...", "principal": "...",
# "created": "...", "expires": "..."}}
For an agent, issue a second key to the same principal with a narrower scope,
for example without live and export.
v2 permissions
| Feature | Grant and key scope need |
|---|---|
/v2/catalog/*, /v2/schemas | Any valid key |
/v2/data, /v2/bars | book_snapshot (or bar for bars), history, and a book depth for snapshots |
/v2/exports | The snapshot permissions plus export |
/v2/stream | book_snapshot, live, and a book depth |
With veinmeshctl, change grants and key scopes by flag instead of rewriting
the whole JSON:
veinmeshctl principal grant <principal> --add-kinds book_snapshot --history --live --export --book-depth all
veinmeshctl key scope <key-id> --add-kinds book_snapshot --history --live --export --book-depth all
The CLI refuses to grant more than the caller holds and says which part was too large.
Endpoints
Principals
| Method and path | Body | Answer | Capability |
|---|---|---|---|
POST /v2/admin/principals | {"name", "grant", "policy"?} | {"id"} | admin.principals |
GET /v2/admin/principals | {"principals": [...]}, only within the caller's permissions | admin.principals | |
GET /v2/admin/principals/{id} | The principal with its grant | admin.principals | |
PUT /v2/admin/principals/{id}/grant | {"grant": {...}} | 204 | admin.principals |
PUT /v2/admin/principals/{id}/policy | {"policy": "<id>"}; empty removes it | 204 | admin.quota |
Reducing a grant immediately limits every key of that principal.
Keys
| Method and path | Body | Answer |
|---|---|---|
POST /v2/admin/keys | {"owner", "scope": {"data": ..., "caps": []}, "expires_in"?} | {"key", "record"} |
GET /v2/admin/keys?owner=<principal-id> | {"keys": [...]} without secrets | |
GET /v2/admin/keys/{id} | One record with its scope | |
GET /v2/whoami | The caller's key, principal and effective permissions | |
POST /v2/admin/keys/{id}/revoke | 204 | |
POST /v2/admin/keys/{id}/rotate | {"overlap"?: "24h"} | {"key", "record"} |
PUT /v2/admin/keys/{id}/scope | {"scope": {...}} | 204 |
PUT /v2/admin/keys/{id}/expiry | {"expires": "2027-01-01T00:00:00Z"} | 204 |
All except whoami need admin.keys.
expires_inis hours, for example2160h. Default and maximum lifetimes are in Limits.overlapdefaults to 0 (the old key expires immediately) and is at most 24 hours. The old key never outlives its original expiry.- Revocation takes effect within about a minute; live connections are told and closed.
- The key id is the
<id>invmk_<id>_<secret>; the secret is never shown again.
Usage policies
POST /v2/admin/quota/policies with admin.quota: without id it creates a policy,
with id it changes one; the answer is {"id"}.
| Field | Meaning | Default |
|---|---|---|
name | Name | required |
requests_per_min | Requests per minute (HTTP, MCP calls and WebSocket connections) | required; 0 = unlimited |
output_bytes_per_min | Response bytes per minute | required; 0 = unlimited |
jobs_per_hour | Jobs per hour, for example computations | required; 0 = unlimited |
concurrency | Concurrent requests | required; 0 = unlimited |
subscriptions | Concurrent WebSocket subscriptions | 4; 0 = unlimited |
event_bytes_per_min | WebSocket bytes per minute | 16 MiB; 0 = unlimited |
scanned_bytes_per_min | Bytes read by queries per minute | 1 GiB on create; unchanged when omitted on update; 0 = unlimited |
A principal without a policy has no usage limit. Policies currently govern the
v1 endpoints; /v2 does not yet enforce them. Assign one anyway so the account
behaves correctly when v2 enforcement arrives.
Over a limit answers quota_exceeded (429) with reset_after; a single request
larger than the limit answers resource_exhausted (413).