HTTP API
The HTTP/REST surface is the language-agnostic way to call Bezial: authenticate with a Bearer token and call every verb over https.
It is a thin transport over the same engine the library uses - never a reimplementation - built on Node’s own http module with no web framework, keeping the dependency tree at zero vulnerabilities.
The only write path is still a file: there is deliberately no graph-write route, and none can be added.
Running the server
Section titled “Running the server”The server reads its configuration from the environment:
| Variable | Default | Purpose |
|---|---|---|
BEZIAL_ROOT |
./.bezial-data |
Object store + change logs root. |
BEZIAL_PORT |
8787 |
Listen port. |
BEZIAL_HOST |
127.0.0.1 |
Listen host. |
BEZIAL_PERMISSIONS |
acl |
The ACL model is enforced by default; set allow-all to disable enforcement (trusted single-tenant). |
BEZIAL_KEYS |
<root>/api-keys.json |
Where API-key records live (hashed, never plaintext). |
BEZIAL_SIGNING_SECRET |
- | If set, also accept signed-principal tokens. |
BEZIAL_BOOTSTRAP_PRINCIPAL |
- | If no keys exist, mint and print one key for this principal at startup. |
Authentication
Section titled “Authentication”Every request (except GET /v1/health) carries a bearer credential:
Authorization: Bearer bzk_<keyId>.<secret>The server resolves the token to a principal once, at the edge, then authorizes every call with that principal exactly like the library path.
An unresolvable token is 401 unauthorized.
Bezial-issued API keys have the form bzk_<keyId>.<secret> and are returned in plaintext exactly once at issue time; the store keeps only a salted SHA-256 hash, and every comparison is constant-time.
An application with its own auth can instead pass a signed principal (a bzs_ token) that Bezial verifies with BEZIAL_SIGNING_SECRET - no Bezial keys required.
See Identity for the seam.
Routes
Section titled “Routes”All routes are under /v1/spaces/:space/….
The document path travels as a query or body field (never a path segment), so nested paths are unambiguous.
| Method & path | Fields | Verb |
|---|---|---|
GET /v1/health |
- (unauthenticated) | health check |
POST /v1/spaces/:space/notes |
body text |
add |
PUT /v1/spaces/:space/document |
query path; body content |
write |
GET /v1/spaces/:space/document |
query path, version? |
read / readVersion |
GET /v1/spaces/:space/documents |
query prefix? |
list |
GET /v1/spaces/:space/document/history |
query path |
history |
POST /v1/spaces/:space/document/restore |
body path, version |
restore (version) |
POST /v1/spaces/:space/document/grant |
body path, granteeId |
grant |
POST /v1/spaces/:space/document/revoke |
body path, granteeId |
revoke |
PUT /v1/spaces/:space/document/visibility |
body path, visibility (private|space) |
setVisibility |
GET /v1/spaces/:space/document/acl |
query path |
acl |
GET /v1/spaces/:space/recall |
query q, asOf?, scope?, synthesize?, limit?, includeArchived? |
recall |
GET /v1/spaces/:space/views/file-map |
query asOf?, scope? |
fileMap |
GET /v1/spaces/:space/views/fact-graph |
query asOf?, scope? |
factGraph |
GET /v1/spaces/:space/audit |
query path?, principalId?, since? |
audit |
POST /v1/spaces/:space/facts/pin |
body factId |
pin |
POST /v1/spaces/:space/facts/unpin |
body factId |
unpin |
POST /v1/spaces/:space/facts/forget |
body factId, reason |
forget |
POST /v1/spaces/:space/facts/restore |
body factId |
restoreFact |
Every call is permission-enforced with the resolved principal: list is filtered, audit is filtered, and recall and both views are filtered per fact.
Zero cross-space and cross-author leak holds over HTTP exactly as in-process.
Example
Section titled “Example”# Add a note.curl -X POST http://localhost:8787/v1/spaces/team/notes \ -H "authorization: Bearer bzk_..." \ -H "content-type: application/json" \ -d '{"text":"Alice uses Obsidian."}'
# Recall, with a synthesized answer.curl "http://localhost:8787/v1/spaces/team/recall?q=What+does+Alice+use%3F&synthesize=true" \ -H "authorization: Bearer bzk_..."Errors
Section titled “Errors”Errors are always the same shape, with a matching HTTP status:
{ "error": { "code": "permission_denied", "message": "..." } }| Status | Code |
|---|---|
| 400 | bad_request |
| 401 | unauthorized |
| 403 | permission_denied |
| 404 | not_found (unknown document, fact, or route) |
| 405 | method_not_allowed |
| 413 | payload_too_large |
| 500 | internal (message is not exposed) |
Prefer a typed client? The TypeScript and Python SDKs both ship a BezialHttpClient that speaks this API and returns the same shapes as the in-process engine.