Access control
The claims × visibility model — how a token's claims and a file's visibility decide what an agent can read.
Grepticon controls what an agent can read with one small rule, applied to every read. Two sides carry tags:
- Files carry
visibility— a set of tags set when you upload the file. Default:['*']. - Tokens carry
claims— a set of tags set when you mint the token. Default:['*'].
A read can see a file when the token's claims intersect the file's
visibility — or when either side is the wildcard '*'. In other words:
visible ⇔ '*' ∈ claims or '*' ∈ visibility or (claims ∩ visibility) ≠ ∅Default: everything is visible
Because both defaults are ['*'], a workspace with no tagging is fully
visible to any token you mint from it. Scoping is opt-in. You get isolation
between workspaces for free (a token only reaches its own workspace), and you
reach for claims and visibility only when you need finer control within a
workspace — for example, giving each agent session a view of just its user's
files.
A grp_sk_ management key always sees everything in the account, regardless
of visibility. The claims × visibility model governs the grp_at_ access
tokens you hand to agents. See Authentication.
Invisible means nonexistent
When a token can't see a file, the file doesn't just fail to open — it is
indistinguishable from a file that was never uploaded. cat of a
non-visible path returns the same not_found as a typo would; ls, find, and
grep prune non-visible files before they build their results. Nothing about a
hidden file — not its name, not its size, not a fragment of its contents — leaks
to a token that lacks the claims for it.
This is what makes claims safe to hand to an agent: a scoped token can't probe for what it isn't allowed to see.
Worked example
Say your handbook workspace holds an HR document only the HR group should
read. Tag it on upload:
import { GrepticonClient } from '@grepticon/sdk';
const client = new GrepticonClient({ apiKey: process.env.GREPTICON_API_KEY ?? '' });
// Tag the file so only tokens claiming group:hr can see it.
await client.files.upload(
'handbook',
'hr/salary-bands.md',
'# Compensation bands\n\nBand 5: …\n',
{ visibility: ['group:hr'], contentType: 'text/markdown' },
);Now mint two tokens with different claims and read the same path with each:
// HR agent: claims intersect the file's visibility → the read succeeds.
const { token: hrToken } = await client.tokens.mint('handbook', {
claims: ['group:hr'],
});
const hr = new GrepticonClient({ apiKey: hrToken }).session('handbook');
await hr.cat('hr/salary-bands.md'); // returns the file's text
// Everyone else: claims don't intersect → invisible ≡ nonexistent.
const { token: otherToken } = await client.tokens.mint('handbook', {
claims: ['user:other'],
});
const other = new GrepticonClient({ apiKey: otherToken }).session('handbook');
await other.cat('hr/salary-bands.md'); // not_found — as if it were never uploadedThe group:hr token reads the file; the user:other token gets not_found.
A grep for a term inside that document behaves the same way — the HR token
matches it, the other token never sees the line.
Claim and visibility tags
Tags are arbitrary strings; you choose a convention that mirrors your authorization model. The common shapes are:
user:<id>— a single principal (e.g.user:alice).group:<name>— a role or team (e.g.group:hr,group:eng).'*'— the wildcard: on a file, "anyone can see it"; on a token, "see everything".
A file or token can carry several tags at once — a file visible to
['group:hr', 'group:legal'] is seen by any token claiming either group.
Setting the tags
- File visibility is set per upload — the
visibilityoption infiles.upload, or the?visibility=query on the REST file routes. Omit it and the file defaults to['*']. - Token claims are set per mint — the
claimsoption intokens.mint, or the request body on/tokens. Omit it and the token defaults to['*'].
Because tokens are cheap and short-lived, the idiomatic pattern is to mint one per agent session with exactly the claims that session's user is entitled to — so the agent's reach is a mint-time decision, enforced on every read.
Where to go next
- Authentication — the credential planes these claims ride on.
- Tools — how visibility pruning shows up in
ls/find/grep. - Quickstart — the end-to-end loop this example slots into.