.env, .config/…, private reports — out of normal listings, reads, and search. Hidden items are reachable only by a token that explicitly asks for them via an nb.hidden UCAN caveat. This is a policy layer on top of the existing namespace, path scope, and UCAN model — not a separate permission system.
When to use it
- Store per-domain secrets or configuration alongside user content without leaking them through generic list, search, or agent tools.
- Ship VFS-backed AI agents that only ever see the non-sensitive subset of a user’s tree, unless the caller mints a token that explicitly opts them in.
- Give the domain owner (or their client) a token with reveal-everything so they can manage their own hidden content, while every delegated agent stays blind by default.
Concept
- A file or folder whose own name starts with
.is hidden by default — the Unix dotfile convention. This default is overridable per item. - A hidden folder hides its entire subtree. An item is effectively hidden when it, or any ancestor folder, is hidden.
- Hidden items are invisible and unreadable on every read surface unless the caller’s UCAN carries an
nb.hiddenreveal caveat that covers them. - Safe by default: with no reveal caveat, nothing hidden leaks — not even to a whole-tree owner token. Owner clients mint tokens with
nb.hidden: ["*"]so the owner sees their own hidden content.
What “hidden” means on every surface
Without a matching reveal caveat, an effectively-hidden item is filtered from every read surface — REST and MCP alike:
Hiddenness never affects writes by explicit id or path. You can create, un-hide, or move an item you can address directly — even one you can’t yet list.
Set or unset the hidden flag
Every item can be forced hidden or visible regardless of its name. The dot-prefix is only the default. All calls requireAuthorization: Bearer <ucan> with fs/write capability and X-Auth-Type: ucan.
On upload — ?hidden=true|false
?hidden= to fall back to the dotfile convention (.env → hidden, report.pdf → visible).
Change a file’s flag afterwards — PATCH /api/fs/files/:id/hidden
The value comes from ?hidden=true|false (preferred) or a JSON body { "hidden": <bool> }:
<FILE_ID> from the upload response. To discover the id of an already-hidden file via GET /api/fs/files, the token needs a reveal caveat — but flipping the flag itself needs no reveal, since you supply the id directly.
Change a folder’s flag — PUT /api/fs/folders/hidden
Sets the folder’s own flag and cascades effective-hidden through its whole subtree. Value comes from ?hidden=true|false or a JSON body.
Quick reference
GET /api/fs/files/:id returns a hidden boolean on file metadata — the effective state, so it accounts for any hidden ancestor folder.
From an agent — MCP
Agents get the same behaviour. Every read, list, and search tool honours the session token’s reveal set, and a dedicated tool sets the flag on either a file or a folder:nb.hidden entry to reach hidden content by CID.
Reveal hidden items — the nb.hidden UCAN caveat
A hidden item becomes visible to a token whose UCAN capability carries an nb.hidden caveat that covers it. The caveat lives on the capability, alongside with and can:
Reveal entry forms
*— reveal all hidden items in scope. Owner clients typically mint this for the owner’s own tokens.- A path (
/secrets/.env,/reports) — reveal that item and its subtree. - A CID (
bafkrei…) — reveal files whose content id matches. Use withGET /api/fs/cid/:cidorvfs_read_cid.
Attenuation
Likenb.cids, the reveal set only narrows down a delegation chain — a delegate can never widen what an ancestor granted. * is the universal set, so the effective reveal is the intersection of the specific (non-*) sets down the chain:
Composition with other scopes
nb.hidden only lifts the hidden filter. It composes with, never overrides, the other UCAN scopes — the most restrictive wins:
- Namespace isolation still applies: a reveal-all token cannot see another user’s namespace.
- Path scope still applies:
nb.hidden: ["*"]on a token scoped to/reportsreveals only hidden items under/reports. - CID scope still applies: a CID-scoped token still needs a CID or
*innb.hiddento fetch matching hidden files.
Security properties
- Default-deny. No
nb.hiddencaveat means nothing hidden is visible or readable, on any surface, for any token — including a whole-tree*owner token. - No cross-surface leak. The same reveal check gates listings, grep, semantic search (vector candidates are re-validated against the metadata store with the hidden filter, so hidden content never escapes via embeddings), CID lookup, trash, metadata, and content.
- Attenuation only. A delegate can never widen the reveal set an ancestor granted.
- Composes, never bypasses. Reveal lifts only the hidden filter; namespace isolation, path scope, and CID scope still apply.
- Bounded. Reveal sets are capped, and the folder-toggle cascade is bounded by the subtree size.