Permissions.
PAMbase gives one user a single shared memory (see the memory model). Permissions are how that user controls precisely which slices of their memory a given app may read, and which slices it may write. This page covers the permission grammar, how grants are carried, and how reads and writes are enforced.
Why it matters
Permissions are what make a shared memory safe to share. A user can let a newsletter app read their reading tastes without giving it their location or finances; they can let a reading app contribute highlights without letting it read everything else. Get your permission set right and users grant it without hesitation; ask for too much and they decline.
Scope grammar
A scope is a single permission string in the form resource:action:qualifier. The resource is what's being accessed (memory, identity, context, ai), the action is what you may do (read, write, host), and the qualifier narrows it (a memory namespace, a context intent). The full set:
| Scope | Grants |
|---|---|
identity:read | Read the user's light profile — optional display name + one-line tone note (no personas, mood, or voice). |
memory:read:<scope> | Read memories in a namespace, e.g. memory:read:note. |
memory:write:<scope> | Persist memories (via events) into that namespace. |
context:read:<intent> | Request a context bundle for one declared intent. |
ai:host:chat | Host the user's AI in a chat surface via the gateway. |
ai:host:companion | Embed the AI as a companion (e.g. a sidebar assistant). |
An intent (used by context:read) is a short label for why you're asking for context — for example app.session.start. It lets PAMbase tune what it returns to the moment. More on intents in Request context.
Wildcards and namespaces
Two wildcard forms expand a single grant: <ns>.* covers every sub-scope under a namespace (e.g. memory:read:note.* matches note.reading, note.work, and so on), and * covers everything readable — rare, and usually declined at consent.
Scope strings are free-form, but PAMbase publishes standard namespaces so apps interoperate: general, profile, preference, schedule, health, finance, goal, relationship, location, note, and world. Browse them in the Catalog.
How grants reach your app: the connection token
When a user approves your app on the consent screen, PAMbase issues a connection token — the credential your app sends with every API call. It carries the exact list of scopes the user approved, and nothing more. Every read and write is checked against that list. The full handshake is in Connect a user.
Margin's scope set
Here is the exact set of scopes our reading companion requests, with the reason for each. It's a good model for “ask for the minimum”:
| Scope | Why Margin needs it |
|---|---|
identity:read | Greet the user by their display name and honor their tone note. |
memory:read:note.* | Read back every highlight the user has saved, across sub-scopes. |
memory:write:note.* | Persist new highlights as note memories. |
memory:read:preference | Learn the user's reading tastes to inform recommendations. |
memory:write:preference | Record tastes it observes over time. |
context:read:app.session.start | Build the home screen with recent reading interests. |
ai:host:chat | Run the built-in 'what should I read next?' chat via the gateway. |
How writes are filtered
Writes are scope-checked just like reads. When your app calls remember(), each memory only persists if its scope is covered by a memory:write:<scope> you hold; memories you can't write are silently dropped (the returned memoryIds reflect what landed). So Margin's highlights persist in note.reading (covered by memory:write:note.*), and its observed tastes persist in preference (covered by memory:write:preference). Emitting a signal needs signal:emit; subscribing needs signal:subscribe.
memory:write:<scope> without the matching read scope. Some apps contribute signals they never need to read back, and asking only for write keeps your footprint smaller and your consent screen easier to approve. Request the minimum on each axis independently.Revocation
A user can revoke a connection from their hub at any time. There's no warning to your app and no refresh token — the very next API call simply returns 401. Handle it gracefully: clear the stored connection token and prompt the user to reconnect. Token handling and the reconnect path are covered in Auth & tokens.
The audit log
Every meaningful action your app takes — each read, write, and hosted turn — is recorded in the user's audit log, which they can inspect in their hub. Treat it as a receipt the user will read: don't request or do anything you'd be uncomfortable explaining there. See Security.
Discovering scopes at runtime
You don't have to guess what a user actually granted. GET /v1/scopes returns the scopes this connection holds, and the ecosystem catalog shows what other apps use. See Discover the vocabulary.
Next
- Connect a user — earn a connection token and the scopes behind it.
- Catalog — the standard scopes, namespaces, and intents.
- Discover the vocabulary — check what this user actually granted.