Skip to content

Permissions

Permissions in Bezial are file-is-truth, like everything else: a document’s access rules live in its own path and frontmatter, so they are inspectable and editable in the same place as the content. The unit of access control is the Markdown file.

  • Owner. The first segment of a document’s path is its owner (alice/notes.md is Alice’s). A caller owns their namespace and is its sole writer.
  • Visibility. A document is either:
    • private - readable only by its owner, or
    • space - readable by every principal in the space.
  • Grants. An explicit list of extra principal ids granted read access, recorded in the document’s frontmatter.

That is the whole model: private or space, plus a grant list. There is no everyone or public tier - sharing beyond the space is done by granting specific principals, not by a broad visibility level.

A document defaults to private, except in a configurable set of shared namespaces (default: shared), which default to space visibility and are collaboratively writable. The layman default follows intuition: your own folder is private, the shared area is visible to the space.

// Opt into ACL enforcement (the default is allow-all, for trusted single-tenant use).
const engine = new BezialEngine({ rootDir: './memory', permissions: 'acl' });
const alice = engine.as({ id: 'alice' });
await alice.write('team', 'alice/private-notes.md', '...'); // private by default
await alice.setVisibility('team', 'alice/roadmap.md', 'space'); // now space-visible
await alice.grant('team', 'alice/private-notes.md', 'bob'); // Bob may now read it
await alice.revoke('team', 'alice/private-notes.md', 'bob'); // and revoke it

grant, revoke, and setVisibility are ordinary owner-checked writes to the document’s own frontmatter - the only write path stays a file, and each is versioned and audited like any other change.

The point of a file ACL is that the derived memory obeys it too.

  • At ingest, every fact is stamped with its source file’s resolved ACL (owner, visibility, grants).
  • At recall, each candidate fact is filtered by the caller against its own stamped ACL - before anything is ranked or synthesized.
  • Because provenance is per fact, a merged entity that mixes a public and a private fact never leaks the private one: the private fact is filtered out for anyone who cannot read its source file.

This ordering - temporal selection, then the ACL filter, then ranking, then synthesis - is what makes recall zero cross-space and cross-author leak by construction. The synthesizer only ever sees already-filtered facts. See Recall for the full path.

Because an ACL lives in frontmatter, changing it is a plain content edit. It re-stamps the affected facts downstream with no LLM re-extraction - the facts already link back to the file, so only their stamp changes.

Read authorization is unified with the write path: the engine gates every read - read, list, history, acl, audit, recall, and both views - through the same permission checker the write path consults. There is no read that bypasses the model:

  • list is permission-filtered and never reveals a path the caller cannot read.
  • audit is filtered to the entries the caller may see (plus their own).
  • recall and the two views are filtered per fact inside the graph.

This holds identically across the library, the HTTP API, and MCP - one permission model, one place it is decided.