clone

a deno framework for local-first web apps that open instantly, work offline and sync when users choose

feat/fix-sync-enrollment-startup
lofi / docs / reference / access.md
access.md 4.8K rendered
1# Account and access API23## Recoverable accounts45The root `@nzip/lofi` export owns principal replacement and runtime cleanup:67| API | Purpose |8| -------------------------------------------- | ------------------------------------------------------------------------------------- |9| `readAccountSession()` | Opens the runtime and returns the stable `user_id` and account state. |10| `createRecoverablePasskeyBackup()` | Backs up the active Jazz secret to a resident, user-verifying passkey. |11| `restoreFromPasskey(options)` | Restores that secret, saves it for the app ID, replaces the runtime, and elects sync. |12| `revealRecoveryPhrase()` | Returns the portable bearer-secret fallback. Do not persist or log it. |13| `restoreFromRecoveryPhrase(phrase, options)` | Performs the same replacement lifecycle from the phrase. |1415Both restore APIs require `confirmLocalReplacement: true` when a different local-only account would16be discarded. `RecoverablePasskeyError` maps unsupported browsers, cancellation, missing17credentials, RP-ID mismatch, and verification failure to non-secret, actionable messages.1819`createBackupPasskey()` and `confirmPhraseAccess()` are retained compatibility APIs for a local20phrase-reveal guard. That credential cannot restore an account.2122## Access templates2324Import from `@nzip/lofi/access`:2526- Schema helpers: `sharedGrantTable`, `groupMembershipTable`, `sharedFieldDirectoryTable`,27 `sharedFieldKeyTable`.28- Policy templates: `privateAccess`, `sharedAccess`, `groupAccess`, `sharedFieldAccess`,29 `defineAccessPolicies`.30- Identities: `sharingIdentity`, `encodeSharingIdentity`, `decodeSharingIdentity`.31- Operations: `createSharingOperations`, `createGroupOperations` (configured with32 `SharingOperationsConfig` and `GroupOperationsConfig`).33- Errors: `AccessError`/`isAccessError` and `SharedFieldError`/`isSharedFieldError`, the two34 catchable families of group and sharing operations.35- Pin remediation: `trustPeerKey`, the explicit re-trust that resolves a `peer-key-changed` refusal36 (also on the root `@nzip/lofi` export, next to `pinnedFingerprint`).37- Fixed roles: `reader`, `contributor`, `writer`, `admin` (exposed as `groupRoles`, with the38 role-to-capability mapping in `groupRoleCapabilities`).3940Sharing operations provide `share`, `revoke`, `listShares`, and `sharedWithMe`. Group operations41provide `createGroup`, `addMember`, `changeRole`, `removeMember`, `leaveGroup`, `listMembers`, and42`reconcileSharedFieldKeys`. Collaboration operations reject local-only use with43`AccessError.code === "sync-required"`.4445## Shared encrypted fields4647Columns declared with `s.sharedEncryptedText` and `s.sharedEncryptedJson` (from `@nzip/lofi/schema`)48hold ciphertext readable by every current group member. The access side hosts the key material:49`sharedFieldDirectoryTable` and `sharedFieldKeyTable` store the directory and per-member wrapped50keys, `sharedFieldAccess` (or the `fieldKeys` option of `groupAccess`) compiles their policies, and51group operations created with the same `fieldKeys`/`directory` tables mint, wrap, and rotate group52field keys automatically: `createGroup` bootstraps the first key, `addMember` delivers held53generations, `removeMember` rotates, and `reconcileSharedFieldKeys` repairs missing wraps. Key54failures raise `SharedFieldError` (narrow with `isSharedFieldError`). Rotation is lazy: removing a55member re-keys future writes, while content encrypted under generations the member already holds56remains readable to them. See [Permission templates](../permissions.md) for the hosting walkthrough.5758In pinned Jazz alpha.53, the permission authority does not expose a group inserted earlier in the59same transaction to the first membership's `allowedTo.update("groupId")` check. `createGroup`60therefore serializes the creator-owned group write and first-admin membership write, and attempts a61compensating group delete if the membership is rejected. The compensating delete is authorized by62the creator's direct delete authority on their own group rows, so a failed bootstrap does not strand63an orphan group. This is secure, but it is not database-transaction atomic. Do not describe it as64atomic until Jazz can validate that relationship against staged transaction rows.6566The group creator additionally holds a permanent superseat — see67[Permission templates](../permissions.md#fixed-group-roles) and68[the decision record](../decisions/group-creator-authority-alpha53.md).6970The templates compile ordinary Jazz policies and accept a final raw-policy callback. They do not71replace `s.table`, `s.defineApp`, or raw `s.definePermissions`; use those directly when the fixed72models do not fit.