跳到主要内容

rxdb-adapter-encrypted

Local field-level AES-GCM-256 envelope encryption for @aiao/rxdb. Plugs into the SQLite-core / PGlite / wa-sqlite / sqliteai adapters with zero-plaintext-at-rest guarantees on the structural database files, change log, query cache and history snapshots.


Install

pnpm add @aiao/rxdb-adapter-encrypted

Peer of any local SQLite-family adapter (@aiao/rxdb-adapter-wa-sqlite, @aiao/rxdb-adapter-pglite, @aiao/rxdb-adapter-sqlite-wasm, @aiao/rxdb-adapter-sqliteai). Not needed for Supabase or remote-only adapters.


Quickstart

import { Entity, Property, PropertyType } from '@aiao/rxdb';
import { WaSqliteAdapter } from '@aiao/rxdb-adapter-wa-sqlite';
import { RxDB } from '@aiao/rxdb';

@Entity({ tableName: 'users' })
class User {
@Property({ primaryKey: true }) id!: string;
@Property({ propertyType: PropertyType.STRING }) displayName!: string;

@Property({ propertyType: PropertyType.STRING, encrypted: true })
email!: string;
}

const adapter = await WaSqliteAdapter.create({ name: 'app.db' });
const db = await RxDB.create({ adapter, entities: [User] });

await adapter.encryption.unlock({
passphrase: 'correct horse battery staple'
// idleTimeoutMs: 10 * 60_000 // override default 5-minute auto-lock
// idleTimeoutMs: 0 // disable auto-lock
});

await db.repository(User).create({
id: 'u1',
displayName: 'Ada',
email: 'ada@example.com' // encrypted at rest
});

Public API

import {
// Keyring lifecycle
Keyring,
createKeyring,
type UnlockOptions,
type PassphraseUnlockOptions,
type KeyBytesUnlockOptions,
type CryptoKeyUnlockOptions,
type KeyProviderUnlockOptions,

// Envelope codec
encodeEnvelope,
decodeEnvelope,
isEnvelope,
buildAAD,
ENVELOPE_VERSION,
ENVELOPE_ALG,
type CryptoEnvelope,
type EnvelopeVersion,

// Schema + query validators
validateEncryptedPropertyMetadata,
validateFTSRegistrationAgainstEncryptedColumns,
validateQueryAgainstEncryptedColumns,
type EncryptedAwareEntity,

// Typed errors
EncryptedError,
EncryptedConfigurationError,
EncryptedDecryptError,
EncryptedLockedError,
EncryptedQueryError,
EncryptedUnlockError,
type EncryptedErrorCode,
type EncryptedErrorInit,

// Keyring persistence binding
type KeyringRow,
type KeyringStorageBinding,

// Verifier sentinel constant
VERIFIER_SENTINEL
} from '@aiao/rxdb-adapter-encrypted';

import { scanForPlaintext, type ScanHit } from '@aiao/rxdb-adapter-encrypted/testing';

Refer to the TSDoc on each export for its security and lifecycle contract.


Guarantees

SpecGuarantee
FR-001Schema validation rejects encrypted PK / FK / index / unique / sortable / FTS / computed columns
FR-002Envelope text form v|alg|kid|iv|ct|tag (6 base64url segments)
FR-003AES-GCM-256, unique 96-bit IV per write, AAD joins namespace, tableName, columnName, primaryKey, and kid with byte 0x1F
FR-004unlock() accepts exactly one of passphrase / keyBytes / key / keyProvider
FR-005All encrypted columns are emitted as TEXT regardless of logical type
FR-006Zero plaintext in DB files, rxdb_change patches, query cache, history snapshots
FR-007Filter / order / group / FTS over encrypted column throws EncryptedQueryError before SQL gen
FR-008Locked-state reads throw EncryptedLockedError; idle auto-lock after 5 min (configurable, 0 disables)
FR-009unlock() verifies passphrase against persisted verifier probe; wrong passphrase never retains key

What this package does NOT do (MVP)

  • No full-database encryption — only declared columns are sealed.
  • No native keychain / passkey / WebAuthn — bring your own passphrase or key bytes.
  • No searchable encryption — where / order / group / FTS on encrypted columns is rejected.
  • No key rotation — single kid per database for the MVP.
  • No audit log — decrypt failures throw, not persisted.
  • No automatic relock on tab visibility — subscribe to document.visibilitychange yourself.

License

MIT

Classes

ClassDescription
EncryptedConfigurationErrorSchema / initialisation / lifecycle misconfiguration.
EncryptedDecryptErrorEnvelope-level failure on read.
EncryptedErrorAbstract base of every error thrown by @aiao/rxdb-adapter-encrypted.
EncryptedLockedErrorRaised when an encrypt / decrypt path is reached while Keyring.isLocked.
EncryptedQueryErrorQuery references an encrypted column.
EncryptedUnlockErrorunlock() failed key validation or provider dispatch.
Keyring-

Interfaces

InterfaceDescription
CryptoEnvelope-
CryptoKeyUnlockOptions-
DecryptArgs-
EncryptArgs-
EncryptedAwareEntity-
EncryptedErrorInit-
KeyBytesUnlockOptions-
KeyProviderUnlockOptions-
KeyringRowThe single row persisted in the rxdb_db_keyring table.
KeyringStorageBindingImplemented by each adapter (sqlite-core, pglite, …) to read / write the keyring singleton row.
LockChangeEvent-
PassphraseUnlockOptions-
PatchWalkArgs-

Type Aliases

Type AliasDescription
EncryptedEntityResolver-
EncryptedErrorCodeUnion of every code literal across the concrete subclasses.
EnvelopeVersion-
UnlockOptions-

Variables

VariableDescription
ENVELOPE_ALGFrozen current algorithm tag.
ENVELOPE_VERSIONFrozen current envelope version.
VERIFIER_SENTINELFixed plaintext sentinel encrypted in the keyring verifier probe.

Functions

FunctionDescription
buildAAD-
createKeyring-
decodeEnvelope-
deserializeFromEnvelope-
encodeEnvelope-
envelopePlaintextPatchesWalk top-level keys of a plaintext patch; for each key present in entity.encryptedPropertyMap, replace the value with an envelope string produced by keyring.encrypt. Non-encrypted keys are copied through.
isEnvelope-
serializeForEnvelope-
unenvelopePlaintextPatchesInverse of envelopePlaintextPatches: decrypt top-level envelope strings back to plaintext. Used when applying undo/redo inversePatch.
validateEncryptedPropertyMetadata-
validateEncryptedQuery-
validateFTSRegistrationAgainstEncryptedColumns-
validateQueryAgainstEncryptedColumns-