vitrinka.docs

Storage and tenancy

Where bytes land — one database file and one object prefix per workspace, two blob backends, a ciphertext cache and fail-closed scanning.

One workspace, one file, one prefix

Each workspace is its own SQLite database under DATA_DIR and its own key prefix in the blob store. Isolation is physical rather than a WHERE clause: there is no shared table for a query to cross. The control database holds organizations, memberships, tokens, the wrapped workspace keys and the audit trail — never workspace content.

A service or agent token is minted for one workspace and refused if presented to another. A personal token and an AI client's OAuth grant act for their person instead: every request is re-scoped to the workspace it addresses, checked against that person's live membership there and capped at the member role, so a token can never carry more than the person's membership would.

The server keeps a pool of open tenant handles (VITRINKA_TENANT_POOL_MAX, 64 by default); a workspace is opened on first use and its key unwrapped at that moment.

Blob backends

BLOB_BACKENDWhere blobs liveNotes
fs (default)DATA_DIR/blobsOne volume holds everything; back it up with the databases.
s3The bucket named by S3_BUCKET at S3_ENDPOINTAll five S3_* variables are required and validated at boot; the bucket is created if missing.

Either way every object is written sealed under the workspace key (Encryption). With S3, a read-through cache under DATA_DIR/blobcache (BLOB_CACHE_MAX_GB, 20 by default, 0 disables) is pre-warmed at boot and evicted oldest-first; its entries are the same ciphertext the bucket holds.

Size limits

MAX_SET_MB (200 by default) caps both the request body and the unpacked size of a published set. Image and document decoding is limit-guarded, so a decompression bomb is refused rather than expanded.

Malware scanning

Set CLAMD_ADDR to a clamd daemon and every blob write is scanned before it is persisted, fail-closed: a scanner that cannot answer refuses the write rather than storing an unscanned file. CLAMD_MAX_MB (100 by default) is the largest file the scanner accepts; larger uploads are refused, never persisted unscanned. When CLAMD_ADDR is unset, no scanning happens.

The hosted service pins a clamd sidecar in its compose. A self-hosted deployment that wants the same posture runs its own daemon on the same network — the address is the only configuration.

On this page