Concepts

Claims and permissions

The P W S R flags, Domain Claims, Event Broker and State Broker claims, how permissions resolve on a topic, and least-privilege recipes.

An App Key can do nothing on its own. What it may do comes from the claims linked to it: at most one Domain Claim, one Event Broker claim and one State Broker claim. Claims are reusable: link the same claim to every key that plays the same role.

Flags#

Every permission is a bitmask of four flags, with the same meaning everywhere:

FlagValueNameGrants
P8PublishPush data that notifies others: publish events; set, merge, delete, echo states.
W4WriteChange or delete stored things: purge event history; manage keys, claims, configs.
S2SubscribeReceive live notifications (sub).
R1ReadQuery: history, states, configs, tell.

Combine them by adding: P S R is 8 + 2 + 1 = 11; all four is 15; 0 denies. Flags never imply each other: S without R receives live data but cannot read history, and P without S can publish but not listen.

TypeScript
import { PUBLISH, WRITE, SUBSCRIBE, READ, combine, has } from '@thuum/client';

const flags = combine(PUBLISH, SUBSCRIBE, READ); // 11
has(flags, SUBSCRIBE); // true
has(flags, WRITE); //     false

Domain Claims: management#

A Domain Claim grants W and/or R per protocol:

ProtocolR lets the key…W lets the key…
domainlist App Keys and Domain Claimscreate and delete App Keys; create and edit Domain Claims
eventlist Event Broker claims and the key pickercreate, edit and delete Event Broker claims, link keys
statelist State Broker claims and the key pickercreate, edit and delete State Broker claims, link keys

Only W and R apply (values 0, 1, 4, 5). Keys that only move data need no Domain Claim at all.

Broker claims: data#

Event Broker and State Broker claims have the same shape:

FieldScopeFlagsGates
topic_flagsthe whole DomainW RTopic configs: retention (Event Broker), TTL and size (State Broker).
topicsone Main Topic each, and its familyP W S RData on that Main Topic and all its sub-topics.
fallback_data_flagsevery Main Topic not in topicsP W S RData everywhere else. 0 denies.

What each data flag allows:

FlagEvent BrokerState Broker
Ppublish (WebSocket pub, REST POST)set, merge, del, echo (WebSocket and REST)
Wpurge history (REST DELETE)not used
Ssubscribe (sub)subscribe to changes (sub)
Rread history (REST GET)read (REST GET, WebSocket tell)

Resolution#

For a topic, Thuum takes its Main Topic and looks it up in the claim's topics. If it is there, those flags apply; otherwise fallback_data_flags applies. Flags are never combined across claims or rows.

Text
claim:   topics { chat: 11 }   fallback_data_flags: 1
topic:   chat:support:42  →  main topic "chat"   →  11  (P S R)
topic:   orders:1042      →  main topic "orders" →   1  (R, the fallback)

The client does the same computation, for display or to disable a button before a round trip:

TypeScript
import { resolveDataFlags, has, SUBSCRIBE } from '@thuum/client';

const me = await acme.me();
const { flags, source } = resolveDataFlags(me.event_claim!, 'chat:lobby');
const canListen = has(flags, SUBSCRIBE);

REST requests resolve permissions on every request, so a claim change applies immediately. A WebSocket resolves them once, when it opens.

Recipes#

RoleClaim
Browser in a chatEvent Broker: chat = P S R (11). Fallback 0.
Browser presenceState Broker: presence = P S R (11). Fallback 0.
Live dashboard (read-only)Event Broker or State Broker: orders = S R (3). Fallback 0.
Backend publisherEvent Broker: orders = P (8). Fallback 0.
Backend with cleanup dutiesEvent Broker: orders = P W R (13), topic flags W R (5).
Operations console for one teamDomain Claim: event = R (1). Event Broker: fallback S R (3).
Full access (development only)Domain Claim: domain, event, state = W R (5). Broker claims: topic flags 5, fallback 15.

Linking rules#

  • A key links to 0 or 1 claim of each kind. Linking a key that already has another claim of that kind fails with 409: unlink it first.
  • A claim cannot be deleted while keys are linked to it (409).
  • Unlinking is idempotent, and so is linking a key to the claim it already has.

Who sees what you can do#

GET /api/{domain}/me returns the caller's key, token expiry and linked claims, so an app can show or hide features without probing for 403s. See Authentication.