stable

Core concepts

The always-on foundation of every noy-db install — vaults and collections, client-side encryption, pluggable stores, keyrings, schema validation, and queries.

noy-db is an encrypted, offline-first document store for JavaScript/TypeScript applications: your application holds the encryption keys, and the storage backend — a file, a browser database, a cloud bucket — only ever sees ciphertext. That property is called zero-knowledge: whoever operates the storage can read nothing about your data. This section covers the core (the kernel): the part of the @noy-db/hub package that every install ships and that cannot be opted out of. Everything beyond it — history, sync, transactions, aggregation, and the rest — is a service you opt into with a with*() factory, and that tree-shakes out of your bundle when unused.

Four words you'll see everywhere

  • Hub — the npm package @noy-db/hub, and by extension the core library it ships. The hub is the trust boundary: everything inside it may handle plaintext, everything outside it never does.
  • Vault — a runtime data space (a tenant, a company, a user) that the hub opens for you. Not a package — the thing your records live in.
  • Collection — a typed record set inside a vault (invoices, clients).
  • Kernel (and its inner enclave) — the always-on part of the hub: the layer this section documents, which every install ships. The enclave is the kernel's innermost crypto region, the only code that ever touches keys.

So: the hub (a package) opens a vault (a data space) holding collections (typed records), and the kernel is the part of the hub that does this without any opt-ins. Full definitions live in the glossary.

Why a fixed core

The split exists for two reasons:

  • Zero-config completeness. Using only what this section documents — no services, no extra packages, no store option — you get a fully working encrypted document store: create a session, open a vault, put/get/query typed records. Persistence, multi-user access, and every advanced capability are additions, never prerequisites.
  • A small, non-negotiable trust surface. The core's storage guarantees — records are encrypted inside the hub before any store sees them, keys live only in memory, queries run after decryption — hold no matter which services you add, because services sit inside the hub while the store stays outside the encryption boundary.

The mental model

Everything in the core hangs off a three-layer hierarchy; every record is addressed by a (vault, collection, id) triple:

Noydb                     ← one unlocked session, from createNoydb()
  └── Vault               ← isolated namespace with its own keys ("acme")
        └── Collection<T> ← typed record set ("invoices")

Around that hierarchy, four core mechanisms do the work. When you put() a record, the hub validates it against your schema (if you declared one), encrypts it with a per-collection key from the vault's keyring, and hands the resulting ciphertext envelope to the store — the pluggable backend. Reads reverse the path. Queries filter decrypted records inside the hub; the store never evaluates a predicate — it is a dumb, untrusted ciphertext shelf.

The whole core in one round trip

Every line below exercises a different core concern — session, vault, typed collection, encrypted write, decrypted read, in-hub query, key wipe:

import { createNoydb } from '@noy-db/hub'

type Invoice = { amount: number; status: string }

// No `store` option → the built-in in-memory store; nothing persists
// past the session. Pass `store: …` to persist (see Stores below).
const db = await createNoydb({ user: 'alice', secret: 'a-strong-secret' })
const vault = await db.openVault('acme')
const invoices = vault.collection<Invoice>('invoices')

await invoices.put('inv-1', { amount: 100, status: 'paid' })
await invoices.put('inv-2', { amount: 2500, status: 'open' })

await invoices.get('inv-1')
// → { amount: 100, status: 'paid' }

invoices.query().where('status', '==', 'paid').toArray()
// → [{ amount: 100, status: 'paid' }]

db.close() // wipes the in-memory encryption keys

For a guided version of this snippet with installation steps, start at Getting started.

Reading order

The six pages, in a learning-path order — grasp the data model, then the three handles, then the storage and access layers:

  1. Overview — where a record lives: the Noydb → Vault → Collection<T> hierarchy, the session lifecycle, and how the three handles map to their pages.
  2. Client — the db handle: the Noydb session (open vaults, transactions, keyring management) and the client-side encryption it owns.
  3. Vault — the isolated, encrypted namespace: opening collections, backup/restore, and the methods that services unlock.
  4. Collection — the typed record set: get/put/query/scan, the chainable query builder, streaming scan(), and Standard Schema validation.
  5. Stores — the pluggable ciphertext backend: the NoydbStore contract, the essential stores, and the extended cloud/SQL/remote-FS family (per-store counts and capabilities: the generated store capabilities matrix).
  6. Permissions & Keyring — how a vault admits users: the per-user keyring file, roles, and per-collection rw/ro/none permissions (assumes the key hierarchy from the Client page above).

Relationships are not in the core. Foreign keys, joins, and link sets are always-on kernel code, but a single-collection document store needs none of them — so they're documented as Foreign refs and its siblings (joins, cross-join, links) in the query catalog, keeping this section relationship-free.

How it maps to the source tree

Each page names its source of truth in the noy-db repo, under packages/hub/src/:

ConcernSourcePage
Session, vault, collectionkernel/noydb.ts, kernel/vault.ts, kernel/collection.tsClient · Vault · Collection
Store contract + built-in memory storekernel/types.ts (NoydbStore), kernel/memory-store.tsStores
Schema validationkernel/schema.tsCollection → Schema & validation
Query + scan builderskernel/query/Collection → Querying
Encryption + data formatcapsule/enclave-aes/Client → Enclave
Keyring format + roleswith-party/team/keyring.ts (the single-owner keyring is always-on; multi-user management is the opt-in team service)Permissions & Keyring

Invariants

Four consequences of the design that hold for every noy-db application, with or without services:

  • Encryption precedes storage. A record is encrypted inside the hub before any store sees it; only ciphertext crosses the store boundary.
  • Keys are session-scoped. The key-encryption key and the per-collection data keys exist only in memory while the session is unlocked; db.close() wipes them.
  • The store never computes. Filtering, ordering, and pagination all happen inside the hub after decryption — the backend stores and returns envelopes, nothing more.
  • You pay only for what you opt into. An unused service tree-shakes out of the bundle; the core documented here is the entire mandatory footprint.

Next

Work through the six pages above in order, or jump into the Orientation map to pick capabilities and land on the exact pages you need. For the deeper structural view — how the core, the services, and the satellite packages compose — see Architecture.