Scoped access
Every capability of this build runs through one dispatch authority. A scoped token is a link-sized string that carries its own limits: which tools, by which doors, how many times, until when, for which task, inside which folders, for whom. Whoever holds it can hand on a smaller copy. Every use is a row of the Ledger, and a token made for a task turns that task's page into its proof of work.
1. What this is
This build is one directory of capabilities (about 1,340 tools: your terminal, your browser, your phone, the cloud, search, mail, leads, the Ledger) and one authority that runs them: the tool runner behind POST /api/run. The build key opens all of it. That is the right key for you and the wrong key for anyone or anything else.
A scoped token is the key cut down. It is not a role or an account. It is a string that says what it allows, signed so nobody can widen it, checked on every single use, and counted. Three things follow from that:
- You can give anyone an exact ability. A friend, a lead, a client, a model, a helper agent: one tool or twenty, a link or an MCP server or a chat, three uses or unlimited, an hour or a month.
- Whoever holds it can only give less. Adding a limit is arithmetic anyone can do; removing one breaks the signature.
- Everything done with it is evidence. Each run is a Ledger row with the exact request and answer kept whole. A token tied to a task puts all of that on the task's page.
2. The execution contract
Every capability is called the same way underneath: the canonical call is {"tool": NAME, "args": {…}}. Every way of calling it (REST, a link, MCP, a command line, a tag, a Shortcut, a webhook, code) is that same call in an envelope. The envelopes differ in only three places: where the call is sent, where the credential goes, and how the name and arguments are wrapped. So the honest form of a contract is the canonical call plus the variance, and every full contract is generated from it. The one way that is not derived is upstream: the service's own call, which is the real information a tool row carries.
Here is the variance of NOW (it takes no arguments), read live from the build's own contract writer:
| Way | Method | Sent to | Credential | Name and arguments |
|---|---|---|---|---|
curl | POST | https://miscsubjects.com/api/run | header x-sheets-key | JSON body: tool, args |
named | POST | https://miscsubjects.com/api/run | header x-sheets-key | JSON body: tool, args |
url | GET | https://miscsubjects.com/api/run?tool=NOW&args=%7B%7D | none (the address is the credential) | the address: tool, args |
jsonrpc | POST | https://miscsubjects.com/mcp | header authorization | JSON-RPC tools/call: params.name, params.arguments |
claude_code | MCP | https://miscsubjects.com/mcp | header Authorization | MCP tools/call: name, arguments |
misc | CLI | misc run NOW '{}' | none (the address is the credential) | a command line |
console_app | CLI | console-app call NOW '{}' | none (the address is the credential) | a command line |
line | POST | https://miscsubjects.com/api/line | header x-sheets-key | JSON body: line |
tag | POST | https://miscsubjects.com/api/tag | header x-sheets-key | JSON body: text |
shortcut | CLI | printf '%s' '{"tool":"NOW","args":{}}' > /private/tmp/misc-sheets-shortcut-input.json && shortcuts run 'misc-sheets run' -i /private/tmp/mis | none (the address is the credential) | a command line |
webhook | POST | https://miscsubjects.com/hook/hk_runtools/NOW | none (the address is the credential) | JSON body: |
shell | CLI | curl -sS -X POST 'https://miscsubjects.com/api/run' -H 'x-sheets-key: $(cat ~/.misc-sheets-key)' -H 'content-type: application/json' --data | in the command | a command line |
run | POST | https://miscsubjects.com/api/run | header x-sheets-key | JSON body: tool, args |
mcp | MCP | https://miscsubjects.com/mcp | header Authorization | MCP tools/call: name, arguments |
oip | POST | https://miscsubjects.com/api/dispatch | header x-sheets-key | JSON body: key, args |
oip_shape | POST | https://miscsubjects.com/api/dispatch | header x-sheets-key | JSON body: key, args, shape |
function_call | POST | https://miscsubjects.com/api/tools/call | header x-sheets-key | JSON body: name, arguments |
js | CODE | | in the code | code in javascript |
python | CODE | | in the code | code in python |
sdk | CODE | | in the code | code in javascript |
function_direct | CODE | | in the code | code in javascript |
mcp_stdio | CLI | printf '%s\n%s\n%s\n' '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo" | in the command | a command line |
ssh | CLI | ssh -o BatchMode=yes -o ConnectTimeout=5 localhost 'cd /Users/cyrusmassoumi/misc-sheets && misc run NOW '\''{}'\''' | none (the address is the credential) | a command line |
graphql | POST | https://miscsubjects.com/api/graphql | header x-sheets-key | JSON body: query, variables |
websocket | CODE | | none (the address is the credential) | code in javascript |
sse | CLI | curl -sSN -H 'x-sheets-key: $(cat ~/.misc-sheets-key)' 'https://miscsubjects.com/api/run/sse?tool=NOW&args=%7B%7D' --max-time 30 | in the command | a command line |
And every one of those ways in full, exactly as it runs when pasted (the build key is the command that prints it on your Mac):
curl
{
"method": "POST",
"url": "https://miscsubjects.com/api/run",
"headers": {
"x-sheets-key": "$(cat ~/.misc-sheets-key)",
"content-type": "application/json"
},
"body": {
"tool": "NOW",
"args": {}
}
}named
{
"method": "POST",
"url": "https://miscsubjects.com/api/run",
"headers": {
"x-sheets-key": "$(cat ~/.misc-sheets-key)",
"content-type": "application/json"
},
"body": {
"tool": "NOW",
"args": {}
}
}url
{
"method": "GET",
"url": "https://miscsubjects.com/api/run?tool=NOW&args=%7B%7D",
"headers": {}
}jsonrpc
{
"method": "POST",
"url": "https://miscsubjects.com/mcp",
"headers": {
"authorization": "Bearer $(cat ~/.misc-sheets-key)",
"accept": "application/json, text/event-stream",
"content-type": "application/json"
},
"body": {
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "NOW",
"arguments": {
"args": {}
}
}
}
}claude_code
{
"method": "MCP",
"server": "https://miscsubjects.com/mcp",
"headers": {
"Authorization": "Bearer $(cat ~/.misc-sheets-key)"
},
"tool": "NOW",
"arguments": {
"args": {}
}
}misc
{
"method": "CLI",
"command": "misc run NOW '{}'"
}console_app
{
"method": "CLI",
"command": "console-app call NOW '{}'"
}line
{
"method": "POST",
"url": "https://miscsubjects.com/api/line",
"headers": {
"x-sheets-key": "$(cat ~/.misc-sheets-key)",
"content-type": "application/json"
},
"body": {
"line": "run NOW {}"
}
}tag
{
"method": "POST",
"url": "https://miscsubjects.com/api/tag",
"headers": {
"x-sheets-key": "$(cat ~/.misc-sheets-key)",
"content-type": "application/json"
},
"body": {
"text": "[NOW]{}[/NOW]"
}
}shortcut
{
"method": "CLI",
"command": "printf '%s' '{\"tool\":\"NOW\",\"args\":{}}' > /private/tmp/misc-sheets-shortcut-input.json && shortcuts run 'misc-sheets run' -i /private/tmp/misc-sheets-shortcut-input.json -o /private/tmp/misc-sheets-shortcut-output.txt; cat /private/tmp/misc-sheets-shortcut-output.txt"
}webhook
{
"method": "POST",
"url": "https://miscsubjects.com/hook/hk_runtools/NOW",
"headers": {
"content-type": "application/json"
},
"body": {}
}shell
{
"method": "CLI",
"command": "curl -sS -X POST 'https://miscsubjects.com/api/run' -H 'x-sheets-key: $(cat ~/.misc-sheets-key)' -H 'content-type: application/json' --data '{\"tool\":\"NOW\",\"args\":{}}'"
}run
{
"method": "POST",
"url": "https://miscsubjects.com/api/run",
"headers": {
"x-sheets-key": "$(cat ~/.misc-sheets-key)",
"content-type": "application/json"
},
"body": {
"tool": "NOW",
"args": {}
}
}mcp
{
"method": "MCP",
"server": "https://miscsubjects.com/mcp",
"headers": {
"Authorization": "Bearer $(cat ~/.misc-sheets-key)"
},
"tool": "NOW",
"arguments": {
"args": {}
}
}oip
{
"method": "POST",
"url": "https://miscsubjects.com/api/dispatch",
"headers": {
"x-sheets-key": "$(cat ~/.misc-sheets-key)",
"content-type": "application/json"
},
"body": {
"key": "NOW",
"args": {}
}
}oip_shape
{
"method": "POST",
"url": "https://miscsubjects.com/api/dispatch",
"headers": {
"x-sheets-key": "$(cat ~/.misc-sheets-key)",
"content-type": "application/json"
},
"body": {
"key": "NOW",
"args": {},
"shape": true
}
}function_call
{
"method": "POST",
"url": "https://miscsubjects.com/api/tools/call",
"headers": {
"x-sheets-key": "$(cat ~/.misc-sheets-key)",
"content-type": "application/json"
},
"body": {
"name": "NOW",
"arguments": "{}"
}
}js
{
"method": "CODE",
"language": "javascript",
"code": "const r = await fetch(\"https://miscsubjects.com/api/run\", {\n method: 'POST',\n headers: {\"x-sheets-key\":\"$(cat ~/.misc-sheets-key)\",\"content-type\":\"application/json\"},\n body: JSON.stringify({\"tool\":\"NOW\",\"args\":{}}),\n});\nconsole.log(r.status, await r.text());"
}python
{
"method": "CODE",
"language": "python",
"code": "import requests\nr = requests.post(\"https://miscsubjects.com/api/run\", headers={\"x-sheets-key\": \"$(cat ~/.misc-sheets-key)\", \"content-type\": \"application/json\"}, json={\"tool\": \"NOW\", \"args\": {}}, timeout=300)\nprint(r.status_code, r.text)"
}sdk
{
"method": "CODE",
"language": "javascript",
"code": "import { MiscSheets } from 'file:///Users/cyrusmassoumi/misc-sheets/sdk.mjs';\nconst misc = new MiscSheets(\"$(cat ~/.misc-sheets-key)\");\nconsole.log(JSON.stringify(await misc.run(\"NOW\", {})));"
}function_direct
{
"method": "CODE",
"language": "javascript",
"code": "import { run } from 'file:///Users/cyrusmassoumi/misc-sheets/sdk.mjs';\nconsole.log(JSON.stringify(await run(\"NOW\", {}, \"$(cat ~/.misc-sheets-key)\")));"
}mcp_stdio
{
"method": "CLI",
"command": "printf '%s\\n%s\\n%s\\n' '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"initialize\",\"params\":{\"protocolVersion\":\"2025-06-18\",\"capabilities\":{},\"clientInfo\":{\"name\":\"cli\",\"version\":\"1.0.0\"}}}' '{\"jsonrpc\":\"2.0\",\"method\":\"notifications/initialized\"}' '{\"jsonrpc\":\"2.0\",\"id\":2,\"method\":\"tools/call\",\"params\":{\"name\":\"NOW\",\"arguments\":{\"args\":{}}}}' | MISC_KEY='$(cat ~/.misc-sheets-key)' node /Users/cyrusmassoumi/misc-sheets/mcp-stdio.mjs"
}ssh
{
"method": "CLI",
"command": "ssh -o BatchMode=yes -o ConnectTimeout=5 localhost 'cd /Users/cyrusmassoumi/misc-sheets && misc run NOW '\\''{}'\\'''"
}graphql
{
"method": "POST",
"url": "https://miscsubjects.com/api/graphql",
"headers": {
"x-sheets-key": "$(cat ~/.misc-sheets-key)",
"content-type": "application/json"
},
"body": {
"query": "query($tool:String!,$args:JSON){ run(tool:$tool, args:$args) }",
"variables": {
"tool": "NOW",
"args": {}
}
}
}websocket
{
"method": "CODE",
"language": "javascript",
"code": "const ws = new WebSocket(\"wss://miscsubjects.com/api/run/ws\");\nws.addEventListener('open', () => ws.send(JSON.stringify({\"tool\":\"NOW\",\"args\":{}})));\nws.addEventListener('message', (e) => { console.log(typeof e.data === 'string' ? e.data : String(e.data)); ws.close(); process.exit(0); });\nsetTimeout(() => { console.log('ws timeout'); process.exit(1); }, 10000);"
}sse
{
"method": "CLI",
"command": "curl -sSN -H 'x-sheets-key: $(cat ~/.misc-sheets-key)' 'https://miscsubjects.com/api/run/sse?tool=NOW&args=%7B%7D' --max-time 30"
}With a token, the same call goes through the token's doors instead, and the token replaces the build key. These are the ways for a token, for a search of places:
| Way | Method | Sent to | Credential | Name and arguments |
|---|---|---|---|---|
link | GET | https://miscsubjects.com/g/run/LEADS_SEARCH_GOOGLE_PLACES?args=%7B%22query%22%3A%22plumber%20San%20Rafael%22%2C%22limit%22%3A5%7D&t=… | in the address (t=) | the address: args, t |
rest | POST | https://miscsubjects.com/g/run | header authorization | JSON body: tool, args |
curl | CLI | curl -sS -X POST 'https://miscsubjects.com/g/run' -H 'authorization: Bearer <TOKEN>' -H 'content-type: application/json' --data '{"tool":"LE | in the command | a command line |
mcp | MCP | https://miscsubjects.com/g/mcp | header Authorization | MCP tools/call: name, arguments |
mcp_jsonrpc | POST | https://miscsubjects.com/g/mcp | header authorization | JSON-RPC tools/call: params.name, params.arguments |
claude_code | CLI | claude mcp add --transport http scoped-leads-search-google-places 'https://miscsubjects.com/g/mcp' --header 'Authorization: Bearer <TOKEN>' | in the command | a command line |
chat | GET | https://miscsubjects.com/g/chat?t=… | in the address (t=) | the address: t |
link
{
"method": "GET",
"url": "https://miscsubjects.com/g/run/LEADS_SEARCH_GOOGLE_PLACES?args=%7B%22query%22%3A%22plumber%20San%20Rafael%22%2C%22limit%22%3A5%7D&t=%3CTOKEN%3E",
"headers": {}
}rest
{
"method": "POST",
"url": "https://miscsubjects.com/g/run",
"headers": {
"authorization": "Bearer <TOKEN>",
"content-type": "application/json"
},
"body": {
"tool": "LEADS_SEARCH_GOOGLE_PLACES",
"args": {
"query": "plumber San Rafael",
"limit": 5
}
}
}curl
{
"method": "CLI",
"command": "curl -sS -X POST 'https://miscsubjects.com/g/run' -H 'authorization: Bearer <TOKEN>' -H 'content-type: application/json' --data '{\"tool\":\"LEADS_SEARCH_GOOGLE_PLACES\",\"args\":{\"query\":\"plumber San Rafael\",\"limit\":5}}'"
}mcp
{
"method": "MCP",
"server": "https://miscsubjects.com/g/mcp",
"headers": {
"Authorization": "Bearer <TOKEN>"
},
"tool": "LEADS_SEARCH_GOOGLE_PLACES",
"arguments": {
"query": "plumber San Rafael",
"limit": 5
}
}mcp_jsonrpc
{
"method": "POST",
"url": "https://miscsubjects.com/g/mcp",
"headers": {
"authorization": "Bearer <TOKEN>",
"content-type": "application/json",
"accept": "application/json, text/event-stream"
},
"body": {
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "LEADS_SEARCH_GOOGLE_PLACES",
"arguments": {
"query": "plumber San Rafael",
"limit": 5
}
}
}
}claude_code
{
"method": "CLI",
"command": "claude mcp add --transport http scoped-leads-search-google-places 'https://miscsubjects.com/g/mcp' --header 'Authorization: Bearer <TOKEN>'"
}chat
{
"method": "GET",
"url": "https://miscsubjects.com/g/chat?t=%3CTOKEN%3E",
"headers": {}
}holder_page
{
"method": "GET",
"url": "https://miscsubjects.com/g?t=%3CTOKEN%3E",
"headers": {}
}narrow
{
"method": "POST",
"url": "https://miscsubjects.com/g/narrow",
"headers": {
"content-type": "application/json"
},
"body": {
"token": "<TOKEN>",
"add": [
"uses <= 1"
]
}
}For any tool, GET /api/contracts/<TOOL>?args=… answers its canonical call, the variance, every way with the key, and every way with a token (&token=g1… fills a real one in). The Console's Scoped access screen shows the same for the tools you pick.
3. The token
A token is three parts joined by dots: g1.<payload>.<signature>.
- g1: the version.
- payload: base64url of readable JSON,
{"i": id, "c": [caveat, …]}. Each caveat is one limit written as a line of text. Anyone can read what a token allows. - signature: 43 characters, the last link of a chain of HMAC-SHA256 signatures, one link per caveat.
The chain is the whole idea, borrowed from macaroons. The first link is HMAC(root, id), where the root is HMAC(build key, "misc-sheets grants v1"), so only this build can make a first link. Each next link is HMAC(previous link, caveat). To check a token, the build recomputes the chain from its root and compares the last link. Change, reorder or remove any caveat and the chain no longer matches. A new build key changes the root and ends every token at once.
A worked chain, computed with a demo root (real arithmetic; the token below does not work on this build because the root is not this build's):
| Link | Computed as | Value (first 16 bytes) |
|---|---|---|
| 0 | HMAC(root, "gr_9c1e0a7b3f21") | da2a36105715ea5fc8b9731799fe1d25 |
| 1 | HMAC(previous, "tools = LEADS_SEARCH_GOOGLE_PLACES,LEADS_LIST") | dcc4b7c50e193c796bdd6a1919317a49 |
| 2 | HMAC(previous, "ways = rest,mcp") | f54edacc162ca1aa470de5852114eb1d |
| 3 | HMAC(previous, "expires < 2026-10-10T00:00:00.000Z") | 138e38e073438227ccbab46d0fb880d6 |
| 4 | HMAC(previous, "uses <= 20") | 0e92f36ad30895b4f9a6c065f82a5889 |
| 5 | HMAC(previous, "task = T-0207") | 085358342389454fef5dfc32065eda54 |
{
"i": "gr_9c1e0a7b3f21",
"c": [
"tools = LEADS_SEARCH_GOOGLE_PLACES,LEADS_LIST",
"ways = rest,mcp",
"expires < 2026-10-10T00:00:00.000Z",
"uses <= 20",
"task = T-0207"
]
}The token:
g1.eyJpIjoiZ3JfOWMxZTBhN2IzZjIxIiwiYyI6WyJ0b29scyA9IExFQURTX1NFQVJDSF9HT09HTEVfUExBQ0VTLExFQURTX0xJU1QiLCJ3YXlzID0gcmVzdCxtY3AiLCJleHBpcmVzIDwgMjAyNi0xMC0xMFQwMDowMDowMC4wMDBaIiwidXNlcyA8PSAyMCIsInRhc2sgPSBULTAyMDciXX0.CFNYNCOJRU_vXfwyBl7aVGO8KLHYslj_tEuvJSNF5Oc
The record the build keeps for each token (state/grants/<id>.json) holds what a string cannot: when it was made and by whom, its name and purpose, how many times each uses limit has been spent, the narrower copies it has seen, and whether it or one of its copies was turned off. It also keeps the token itself, readable only through your own doors, so you can copy or send it again from the Console.
4. Every limit you control
These are all of them. A use must satisfy every caveat the token carries (they are joined by AND). A caveat this build does not know is a refusal, never a pass. A refusal says which caveat refused it, and is itself a Ledger row.
| Limit | Caveat | What it does | Applies to |
|---|---|---|---|
| Tools | tools = EXA_SEARCH,LEADS_* | Which tools of the Directory may run. A name ending in * covers every tool that starts with it (LEADS_*). | every tool run (link, REST, MCP, a chat model’s tool calls) |
| Ways | ways = link,mcp | Which doors the token opens: link (open as a link), rest (rest), mcp (mcp), chat (chat with a model), proof (proof page). | every use |
| Expires | expires < 2026-10-04T09:00:00.000Z | The moment the token stops working. | every use |
| Uses | uses <= 5 | How many tool runs and chat messages it allows. Counted along the chain: a limit on a token covers every narrower token made from it. | tool runs and chat messages (opening a page is not a use) |
| Task | task = T-0207 | Binds the token to one task: every run is recorded under that task, shows on its proof page, and the token stops working when the task is done. | every use |
| Areas | area = /Users/cyrusmassoumi/misc-sheets/src/ | Every argument that names a path (path, file, file_path, dir, cwd, folder, key, prefix) must start with one of these. | tool runs with a path argument (a shell command’s text is not a path argument: do not give shell tools to a token you limit by area) |
| Argument rules | arg EMAIL_SEND.to glob *@acme.com | One argument of one tool (or * for every tool) must match: = (exactly), prefix, glob (* anywhere), in (one of a list), <= or >= (a number). | tool runs whose tool and argument match |
| Person | person = p_k2x9qa | Only this person may use it, signed in to the build in the browser that uses it (by text from their phone). Without it, whoever holds the token can use it. | uses from a browser (a link, a chat, a proof page) |
| Whose keys | keys = p_k2x9qa | Every tool runs with this person’s own keys (their Gmail key, their iMessage line), never the build’s: a key they have not stored is a refusal that names it. It can only be set when the token is made; a narrower copy cannot change it. The holder adds and removes that person’s keys at /g/keys. | every tool run, and /g/keys |
| Model | model = openrouter/nvidia/nemotron-3-super-120b-a12b:free | The model the holder talks to in a chat. | the chat way |
| Hand on | delegate = no | no: the token cannot be narrowed and handed on; any caveat added after this one makes it invalid. | every use |
Two details matter. Uses are counted along the chain: each uses caveat is counted against the link it sits at, so a limit of 20 on a token covers every narrower copy made from it, and a copy's own limit of 2 covers only that copy. A use is one tool run or one chat message; opening a page is not a use, and in a chat each tool the model runs is a use too.
5. Handing a token on, smaller
Whoever holds a token can add caveats and compute the next links themselves: the next link is HMAC(the signature they hold, the new caveat). They never ask the build, and the build accepts the result because the chain checks out from its root. They cannot remove a caveat, because they cannot compute a link backwards. So a model can give a helper agent exactly the part of its own access the helper needs.
The demo token above, narrowed to one tool, two uses, and no further handing on:
[ "tools = LEADS_SEARCH_GOOGLE_PLACES,LEADS_LIST", "ways = rest,mcp", "expires < 2026-10-10T00:00:00.000Z", "uses <= 20", "task = T-0207", "tools = LEADS_LIST", "uses <= 2", "delegate = no" ]
g1.eyJpIjoiZ3JfOWMxZTBhN2IzZjIxIiwiYyI6WyJ0b29scyA9IExFQURTX1NFQVJDSF9HT09HTEVfUExBQ0VTLExFQURTX0xJU1QiLCJ3YXlzID0gcmVzdCxtY3AiLCJleHBpcmVzIDwgMjAyNi0xMC0xMFQwMDowMDowMC4wMDBaIiwidXNlcyA8PSAyMCIsInRhc2sgPSBULTAyMDciLCJ0b29scyA9IExFQURTX0xJU1QiLCJ1c2VzIDw9IDIiLCJkZWxlZ2F0ZSA9IG5vIl19._1M4nDYcPx0YPQmey_sUJ-Y1GfA6ayGprDcQ92MEKJ4
The same in a page, in JavaScript, with nothing but the token (this is all the arithmetic there is):
async function narrow(token, add) {
const [v, payload, sig] = token.split('.');
const p = JSON.parse(atob(payload.replace(/-/g, '+').replace(/_/g, '/')));
let s = Uint8Array.from(atob(sig.replace(/-/g, '+').replace(/_/g, '/') + '='), (c) => c.charCodeAt(0));
for (const caveat of add) {
const k = await crypto.subtle.importKey('raw', s, { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']);
s = new Uint8Array(await crypto.subtle.sign('HMAC', k, new TextEncoder().encode(caveat)));
}
const b64u = (u) => btoa(String.fromCharCode(...u)).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
return v + '.' + b64u(new TextEncoder().encode(JSON.stringify({ i: p.i, c: [...p.c, ...add] }))) + '.' + b64u(s);
}Or ask the build to do it: POST https://miscsubjects.com/g/narrow with {"token": "g1…", "add": ["uses <= 1"]}. Nothing is stored. delegate = no stops all of this: a token with any caveat after it is refused. Turning off a token turns off every copy narrowed from it; turning off one copy leaves the token it came from working.
6. Where a token works
| Way | Address | For |
|---|---|---|
Open as a link link | GET /g/run/<TOOL>?args=…&t=<token>: a browser, a Shortcut, or a model whose only tool is opening a page | |
REST rest | POST /g/run with Authorization: Bearer <token>: any program, curl, a script | |
MCP mcp | POST /g/mcp with Authorization: Bearer <token>: Claude, Codex, Cursor or any MCP client, which sees only the tools the token allows | |
Chat with a model chat | /g/chat?t=<token>: a page where the holder talks to a model that can run only the tools the token allows | |
Proof page proof | /w/<TASK>?t=<token>: the read-only page of a task, every call made for it and every answer | |
Every way goes through the same check and then through the build's own tool runner, so every call out and every answer is kept whole (ledger/exchanges/<call>.json) and is a Ledger row: grant used with the token, its chain, the caveats, the tool, the arguments and the result; or grant refused with the caveat that refused it. The token's holder page, https://miscsubjects.com/g?t=<token>, says in plain words what it allows and shows each of its ways filled in with the token. Reading it is not a use.
MCP clients connect to https://miscsubjects.com/g/mcp with the token as a bearer key, and see only the tools the token allows. In a chat, the model has two tools, find and call, and both see only the token's tools; with no tools on the token, the model has none.
7. Tasks and proof of work
A task is a row of the build's task list (/api/tasks): what must be done, written by you. Give a token the limit task = T-0207 and three things happen: every run made with it is recorded under the Ledger session task:T-0207; the task's page, https://miscsubjects.com/w/T-0207, lists it; and it stops working the moment the task is marked done.
The task's page is the proof of work: the task exactly as it was written, every token it was worked with and its limits in plain words, every call in order with when, which tool, by which door, and what came back, the exact request and answer of each call one tap away (credentials shown by their name only), and every chat turn. It is not something the worker wrote afterwards. The worker could not leave a call out, because it could not act without the token, and it could not add one, because the build writes the record. That is what makes it inspectable: whether your standards were followed is a question the page answers with the payloads themselves.
You open it signed in. To show it to someone else, make a token with task = T-0207 and ways = proof: it opens that page read-only and runs nothing.
8. Scoping unified dispatch
Unified dispatch gives one authority full control: your Mac's terminal (LOCAL_EXEC), your browsers (BROWSER_*, PLAYWRIGHT), your phone (PHONE_*, IMSG_*), the cloud (CLOUD_EXEC, the container agents), and every service the Directory names. Scoped access is how that authority is lent out per piece of work without lending all of it. A model doing a task is handed the task's token instead of the build key: it can reach the tools on the list and nothing else, write only inside its areas, call only within its argument rules, only for that task, and only until the task is done. Everything it does is on the task's page.
This is what is technically possible today. No running agent of this build has been switched to scoped tokens: that is your decision to make, agent by agent.
9. Examples
Let a friend run one search three times today
A person you text a link to
tools = EXA_SEARCH ways = link expires < 2026-10-20T08:55:23.437Z uses <= 3
- Runs only EXA_SEARCH.
- Works only by: open as a link.
- Stops working at Oct 20, 2026, 1:55 AM (Pacific).
- At most 3 uses.
Scoped access > Make a token: Tools EXA_SEARCH, Ways "Open as a link", Uses 3, Expires 1 day. Text it from the result. They open the holder page and run it from the link; the fourth try is refused and says why.
A model works one task, and may touch only one folder
A coding model given task T-0207
tools = FILE_GET,FILE_PUT,FILE_LIST,EXA_SEARCH ways = rest,mcp expires < 2026-10-26T08:55:23.437Z task = T-0207 area = /Users/cyrusmassoumi/misc-sheets/src/
- Runs only FILE_GET, FILE_PUT, FILE_LIST, EXA_SEARCH.
- Works only by: rest, mcp.
- Stops working at Oct 26, 2026, 1:55 AM (Pacific).
- For task T-0207: every run and every refusal is on its proof page; it stops running anything when the task is done (its proof page stays open).
- Paths only under /Users/cyrusmassoumi/misc-sheets/src/.
Every run is recorded under task T-0207, every file path must start with misc-sheets/src/, and it cannot send mail, text, run a shell or open a browser because those tools are not on its list. When the task is marked done the token stops.
A lead talks to a model that can look up only their own kind of business
A lead, from a link in a text
tools = LEADS_SEARCH_GOOGLE_PLACES ways = chat expires < 2026-11-02T08:55:23.437Z uses <= 30 arg LEADS_SEARCH_GOOGLE_PLACES.query glob *dentist* model = openrouter/nvidia/nemotron-3-super-120b-a12b:free
- Runs only LEADS_SEARCH_GOOGLE_PLACES.
- Works only by: chat with a model.
- Stops working at Nov 2, 2026, 12:55 AM (Pacific).
- At most 30 uses.
- LEADS_SEARCH_GOOGLE_PLACES: query must match *dentist*.
- Talks to the model openrouter/nvidia/nemotron-3-super-120b-a12b:free.
The chat page opens with no sign-in. The model sees one tool, and the search it may run must mention dentists. Each message and each search is a use.
Send someone the proof of a finished task
A client, a partner, anyone
ways = proof expires < 2026-11-18T08:55:23.437Z task = T-0207
- Works only by: proof page.
- Stops working at Nov 18, 2026, 12:55 AM (Pacific).
- For task T-0207: every run and every refusal is on its proof page; it stops running anything when the task is done (its proof page stays open).
The token opens /w/T-0207 read-only: the task as written, the tokens it was worked with, every call in order with its exact request and answer (credentials shown by name only), and every chat turn. It runs nothing.
A model hands a helper a smaller token
The model holding the task token, without asking the build
tools = LEADS_* task = T-0207 tools = LEADS_LIST uses <= 2 delegate = no
- Runs only LEADS_*.
- For task T-0207: every run and every refusal is on its proof page; it stops running anything when the task is done (its proof page stays open).
- Runs only LEADS_LIST.
- At most 2 uses.
- Cannot be narrowed and handed on.
It appends three caveats and computes the next three links of the signature itself (or calls POST /g/narrow). The helper can list leads twice for the same task and cannot hand it on again. Turning off the first token turns off this one too.
Only Ben, signed in on his phone
A person on your list
tools = NOW ways = link,chat person = p_ben
- Runs only NOW.
- Works only by: open as a link, chat with a model.
- Only for the person p_ben, signed in.
A link that works only in a browser signed in to the build as that person (by text from their number). Forwarded to someone else, it refuses them.
10. What it does not do
- A shell is a shell. Areas govern named path arguments. A tool that runs a command line (
LOCAL_EXEC,BASH,CLOUD_EXEC) can reach any path its command names. Do not give a shell tool to a token you limit by area. - No spending limit. The build does not record a reliable cost for every tool run, so there is no "at most $5" caveat. Uses and time are the limits.
- Only what passes through the build is on the record. A model's own reasoning traffic, and anything it does outside the build, is not captured unless it goes through the build.
- The holder can read the token. Its limits are plain text by design. Never put something private in a name or a purpose.
- A new build key ends every token, because the root is derived from it.
11. Where to do it
In the Console: Settings > Scoped access. Pick every limit, see the token's caveats as you go, make it, then copy it, text it, or open its holder page. Each token shows its uses, its narrower copies, every call made with it, and a switch to turn it off.
By API (with the build key):
[
{
"method": "GET",
"url": "https://miscsubjects.com/api/grants/axes",
"headers": {
"x-sheets-key": "$(cat ~/.misc-sheets-key)"
}
},
{
"method": "POST",
"url": "https://miscsubjects.com/api/grants",
"headers": {
"x-sheets-key": "$(cat ~/.misc-sheets-key)",
"content-type": "application/json"
},
"body": {
"label": "Marin leads for T-0207",
"purpose": "Find three plumbers in Marin",
"tools": [
"LEADS_*"
],
"ways": [
"rest",
"mcp"
],
"uses": 20,
"task": "T-0207",
"ttl_s": 604800,
"delegate": true
}
},
{
"method": "GET",
"url": "https://miscsubjects.com/api/grants",
"headers": {
"x-sheets-key": "$(cat ~/.misc-sheets-key)"
}
},
{
"method": "POST",
"url": "https://miscsubjects.com/api/grants/<id>/revoke",
"headers": {
"x-sheets-key": "$(cat ~/.misc-sheets-key)",
"content-type": "application/json"
},
"body": {}
},
{
"method": "GET",
"url": "https://miscsubjects.com/api/contracts/EXA_SEARCH?args=%7B%22query%22%3A%22x%22%7D",
"headers": {
"x-sheets-key": "$(cat ~/.misc-sheets-key)"
}
}
]The code: src/grants.js (the token, the check, the doors, the proof page) and this page, src/grants-article.js, which is written from it.
This page is written by the build from the code it describes, each time it is opened.