stable

Secret & keyring

The vault secret and single-user keyring: change / rotate / recover the secret, read the keyring, and the users home — list, update, directory toggle.

Every vault is unlocked by one secret.

What counts as a secret. noy-db never inspects what your secret is — only that it can derive a key from it. It may be a passphrase a person types, a generated code, a certificate, or anything else. Two properties are what matter:
  • Hard to guess. The secret is the whole of the vault's security; there is no server-side account to fall back on, and no one to appeal to if it is guessed.
  • Held in the owner's memory where possible — the one store nobody else can read, copy, or subpoena. Where memory isn't practical (machine access, long random values), the @noy-db/at-* hosts seal the secret to infrastructure you control, and recovery covers the case where it is lost.
The docs say secret throughout because that is the API's name for it (secret on createNoydb()) and because "passphrase" would narrow it to one of the forms it can take.

In standard mode that is the secret you pass as secret to createNoydb(); the hub derives a key-encryption key (KEK) from it in memory via PBKDF2 and never writes it to disk, so storage backends only ever see ciphertext (see Enclave). WebAuthn, OIDC and Shamir supply the unlocked keyring through the getKeyring option instead, so no secret is derived at all.

A vault's minimum is a single-owner keyring — the user you name at createNoydb() becomes the owner on the first open of a new vault. user is required configuration, but a personal vault needs nothing more: single-user is the default, and multi-user membership is opt-in team territory. Grant / revoke / rotate route through the team service and throw TeamNotEnabledError until you pass withTeam() (see Strategies); every primitive on this page works with zero strategies.

The keyring is the per-user record at _keyring/<userId> that is both the access-control list and the cryptographic gate — what you can read is exactly what your keyring can unwrap. It holds your role (owner / admin / custodian / operator / viewer / client), your per-collection permissions, the wrapped DEKs (one AES-KW-wrapped data-encryption key per collection you can decrypt), and the salt used to derive your KEK from the secret. The full grant/revoke key ceremony and role model live on Permissions & keyring.

Secret & keyring

Change, rotate, recover, and read the single-user keyring. Each is a single-user primitive — always available, no strategy required; the sensitive ones sit behind a policy gate, not a with*() opt-in.

changeSecret()

changeSecret(
  vault: string,
  newSecret: string,
  options?: SecretPolicy & {
    allowWeakSecret?: boolean
  }
): Promise<void>

Change the current user's secret for a vault. The new secret is validated against the strength rules unless you opt out.

  • vault — the vault whose secret to change.
  • newSecret — the replacement secret, validated against the strength rules.
  • options — a SecretPolicy (plus allowWeakSecret) tuning validation:
    • allowWeakSecret — skip strength validation entirely (typically only for fixtures and migrations).
    • pattern / customValidator — override the default character-class rule, or replace validation wholesale with a custom function.

rotateSecret()

rotateSecret(
  vault: string,
  input: RotateSecretInput,
  factors?: FactorProofBundle
): Promise<void>

Rotate the secret when the user still remembers the old one. A single-user primitive — it stays available with no strategy. Sensitive enough to sit behind the rotate-secret policy gate, so a tightened policy can demand fresh factor proofs.

  • vault — the vault whose secret to rotate.
  • input — a RotateSecretInput carrying the oldSecret and newSecret (plus optional secretPolicy / allowWeakSecret and per-slot slotCeremonies to preserve tier-2 authenticators across the rotation).
  • factors — factor proofs presented to satisfy the rotate-secret gate when a tightened policy demands them.

recoverSecret()

recoverSecret(
  vault: string,
  input: RecoverSecretInput,
  factors?: FactorProofBundle
): Promise<RecoverSecretResult>

Reset the secret using a recovery proof rather than the old secret. Also a single-user primitive. The 'paper' recovery profile is supported end-to-end; calling it against a profile that only ships its API shape throws RecoveryProfileNotImplementedError. Enrolling recovery profiles is a recovery config concern (see Configuration), and the recovery-profile lifecycle and flows are covered on Auth recovery.

  • vault — the vault whose secret to reset.
  • input — a RecoverSecretInput carrying the recoveryProof and newSecret (plus optional secretPolicy / allowWeakSecret, and rotateRemainingCodes — default true — to re-mint the remaining recovery codes after a successful paper recovery).
  • factors — factor proofs presented to satisfy the recover-secret gate when the policy demands them.

getKeyring()

getKeyring(vault: string): Promise<UnlockedKeyring>

Return a defensive copy of the unlocked keyring for a vault (roles, permissions, wrapped DEKs). Reading the owner keyring is a single-user primitive and stays ungated.

  • vault — the vault whose unlocked keyring to copy.

Users

The users home for a vault: enumerate members and rewrite a member's post-grant identity fields. listUsers and updateUser are always-on primitives; the multi-user grant / revoke / rotate ceremony is the opt-in team service (see Strategies).

listUsers()

listUsers(vault: string): Promise<UserInfo[]>

List every user with access to a vault.

  • vault — the vault whose users to list.

updateUser()

updateUser(
  vault: string,
  options: UpdateUserOptions,
  factors?: FactorProofBundle
): Promise<void>

Mutate post-grant identity fields on an existing keyring. It's a pure plaintext-header rewrite: no DEK re-wrap, no KEK needed, no authenticator or tier-2 slots touched, and — unlike db.revoke + db.grant — the same userId, DEK wrappings, and granted_by survive; only the specified header fields move (last-write-wins). Demoting a role does not cascade (that's a revoke concern). A role-elevation guard runs regardless of policy: owner can do anything, an admin manages admin/operator/viewer/client laterally but can neither promote to owner nor demote from owner. Gated by the update-user policy (a STRICT_POLICY demands a TOTP / email-OTP proof; PERSONAL_POLICY accepts a tier-1 unlock).

  • vault — the vault whose keyring to mutate.
  • options — an UpdateUserOptions naming the target and the fields to move:
    • userId — the principal whose keyring to update (required).
    • role — the new role, subject to the elevation guard above.
    • displayName — the new display name; null clears it.
    • permissions — the replacement permission map (full replacement at the map level).
  • factors — factor proofs presented to satisfy the update-user gate.

Throws NoAccessError when no keyring exists for the target, PermissionDeniedError when the hierarchy rejects, and ValidationError when no field is provided.

getDirectoryEnabled() · setDirectoryEnabled()

The vault-level user directory toggle decides whether this vault's members show up when you enumerate its team. It sits alongside the per-user hide flag and the user-envelope records those listings read — see directory and user-envelope for the full visibility model.

getDirectoryEnabled(vault: string): Promise<boolean>
setDirectoryEnabled(
  vault: string,
  enabled: boolean
): Promise<void>

getDirectoryEnabled reads the vault-level user-directory listing toggle, defaulting to on (true) when no _meta/directory document exists yet — no role gate, anyone who can open the vault may read it. setDirectoryEnabled flips it and is owner-only; when disabled, the hub-level user enumeration throws DirectoryDisabledError for callers who are neither owner nor admin. Honest caveat: this is a UX flag, not a privacy guarantee — the keyring and user-envelope records stay observable to anyone with direct store read access; only the hub-level enumeration is gated.

  • vault — the vault whose directory toggle to read (getDirectoryEnabled) or flip (setDirectoryEnabled).
  • enabled — the new toggle value for setDirectoryEnabled.