auth/bootstrap_account.ts

Bootstrap flow for creating the first account.

Uses an atomic bootstrap_lock table to prevent TOCTOU race conditions. Token verification and account creation happen in a single transaction.

view source

Declarations
#

6 declarations

bootstrap_account
#

auth/bootstrap_account.ts view source

(deps: BootstrapAccountDeps, provided_token: string, input: BootstrapAccountInput): Promise<BootstrapAccountResult> import {bootstrap_account} from '@fuzdev/fuz_app/auth/bootstrap_account.js';

Bootstrap the first account with keeper and admin privileges.

Uses an atomic bootstrap_lock UPDATE to prevent concurrent bootstrap attempts (TOCTOU). The full flow runs in a single transaction:

  1. Read and verify the bootstrap token (before transaction)
  2. Hash the password (CPU-intensive, before transaction)
  3. Acquire the bootstrap lock atomically (inside transaction)
  4. Create account + actor
  5. Grant keeper and admin role_grants (no expiry, granted_by = null)
  6. Delete the token file (after commit, reported via token_file_deleted)

deps

database, token path, filesystem callbacks, and password hashing

provided_token

the bootstrap token from the user

type string

input

username and password

returns

Promise<BootstrapAccountResult>

the created account, actor, and role_grants — or a bootstrap failure

mutates

  • bootstrap_lock row — flips `bootstrapped` to `true` atomically
  • account / actor / role_grant tables — inserts the bootstrap account, actor, and the keeper + admin role_grants
  • filesystem — deletes the bootstrap token file after commit (reported via `token_file_deleted`)

BootstrapAccountDeps
#

auth/bootstrap_account.ts view source

BootstrapAccountDeps import type {BootstrapAccountDeps} from '@fuzdev/fuz_app/auth/bootstrap_account.js';

Dependencies for bootstrap_account.

db

type Db

token_path

Path to the bootstrap token file on disk.

type string

read_secure_file

Hardened secret-file read (see FsSecureReadDeps.read_secure_file) — the token mints the keeper account, so a symlinked, non-regular, group/other-readable, or oversized file must refuse rather than be honored. Throws on refusal; every throw reads as TOKEN_FILE_MISSING upstream.

type (path: string): Promise<Uint8Array<ArrayBufferLike>>

path

type string
returns Promise<Uint8Array<ArrayBufferLike>>

delete_file

Delete a file.

type (path: string): Promise<void>

path

type string
returns Promise<void>

password

Only hashing is needed — verification happens separately during login.

type Pick<PasswordHashDeps, "hash_password">

log

Structured logger instance.

type Logger

BootstrapAccountFailure
#

auth/bootstrap_account.ts view source

BootstrapAccountFailure

type { ok: false; error: "already_bootstrapped"; status: 403; } | { ok: false; error: "token_file_missing"; status: 404; } | { ok: false; error: "invalid_token"; status: 401; }

import type {BootstrapAccountFailure} from '@fuzdev/fuz_app/auth/bootstrap_account.js';

Bootstrap failure result.

BootstrapAccountInput
#

auth/bootstrap_account.ts view source

BootstrapAccountInput import type {BootstrapAccountInput} from '@fuzdev/fuz_app/auth/bootstrap_account.js';

Input for the bootstrap account creation.

username

type string

password

type string

BootstrapAccountResult
#

BootstrapAccountSuccess
#

auth/bootstrap_account.ts view source

BootstrapAccountSuccess import type {BootstrapAccountSuccess} from '@fuzdev/fuz_app/auth/bootstrap_account.js';

Successful bootstrap result with the created entities.

ok

type true

account

type Account

actor

type Actor

role_grants

type { keeper: RoleGrant; admin: RoleGrant; }

token_file_deleted

Whether the bootstrap token file was successfully deleted after account creation.

type boolean

Depends on
#

Imported by
#