@noy-db/hub 0.1.0-pre.7 → 0.1.0-pre.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/blobs/index.cjs.map +1 -1
- package/dist/blobs/index.d.cts +2 -2
- package/dist/blobs/index.d.ts +2 -2
- package/dist/blobs/index.js +2 -2
- package/dist/bundle/index.d.cts +2 -2
- package/dist/bundle/index.d.ts +2 -2
- package/dist/bundle/index.js +3 -3
- package/dist/{chunk-NZ4XCIKS.js → chunk-2WGMYBYS.js} +3 -3
- package/dist/{chunk-3WCRU7TI.js → chunk-7XBQS42M.js} +2 -2
- package/dist/{chunk-CL37QSND.js → chunk-HC7Z5EQZ.js} +2 -2
- package/dist/{chunk-B6HF6NTZ.js → chunk-PJK6IOBC.js} +1 -1
- package/dist/chunk-PJK6IOBC.js.map +1 -0
- package/dist/{chunk-KPF2HHPI.js → chunk-R2ZTGEVP.js} +2 -2
- package/dist/{chunk-GILMPJXB.js → chunk-RSPLI376.js} +2 -2
- package/dist/{chunk-XCL3WP6J.js → chunk-SCZXXXU4.js} +2 -1
- package/dist/{chunk-XCL3WP6J.js.map → chunk-SCZXXXU4.js.map} +1 -1
- package/dist/{chunk-INSJBB5W.js → chunk-TOQK4KAN.js} +3 -3
- package/dist/{chunk-UFL4DUEV.js → chunk-VQBTTTUN.js} +1 -1
- package/dist/chunk-VQBTTTUN.js.map +1 -0
- package/dist/{chunk-FAAWLVTF.js → chunk-WN6UK7PM.js} +2 -2
- package/dist/{chunk-N2LMZKLR.js → chunk-Y4CMTMUW.js} +2 -2
- package/dist/{chunk-6IJQ27XN.js → chunk-YVFTBQHL.js} +14 -4
- package/dist/chunk-YVFTBQHL.js.map +1 -0
- package/dist/consent/index.d.cts +2 -2
- package/dist/consent/index.d.ts +2 -2
- package/dist/{delegation-XDJCBTI2.js → delegation-2DBS2EOH.js} +2 -2
- package/dist/{dev-unlock-Dk14V6lX.d.cts → dev-unlock-BZKx666y.d.cts} +1 -1
- package/dist/{dev-unlock-CcJ1qIi7.d.ts → dev-unlock-BygpnIWe.d.ts} +1 -1
- package/dist/{hash-1Xsqx1jl.d.ts → hash-B0eU2Qv9.d.ts} +1 -1
- package/dist/{hash-h_2U3TFb.d.cts → hash-CIyfmKsg.d.cts} +1 -1
- package/dist/history/index.cjs.map +1 -1
- package/dist/history/index.d.cts +3 -3
- package/dist/history/index.d.ts +3 -3
- package/dist/history/index.js +2 -2
- package/dist/i18n/index.cjs +11 -0
- package/dist/i18n/index.cjs.map +1 -1
- package/dist/i18n/index.d.cts +2 -2
- package/dist/i18n/index.d.ts +2 -2
- package/dist/i18n/index.js +3 -3
- package/dist/{index-DZn6Yick.d.ts → index-Dp4tKCjX.d.ts} +1 -1
- package/dist/{index-Cvb0efA_.d.cts → index-DsVbTDZI.d.cts} +1 -1
- package/dist/index.cjs +384 -57
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -5
- package/dist/index.d.ts +5 -5
- package/dist/index.js +377 -72
- package/dist/index.js.map +1 -1
- package/dist/{ledger-5V67MAIL.js → ledger-UQIMMKO5.js} +3 -3
- package/dist/periods/index.cjs.map +1 -1
- package/dist/periods/index.d.cts +2 -2
- package/dist/periods/index.d.ts +2 -2
- package/dist/periods/index.js +3 -3
- package/dist/{public-envelope-DFJZHXVH.js → public-envelope-3QTQADDW.js} +3 -3
- package/dist/session/index.cjs.map +1 -1
- package/dist/session/index.d.cts +3 -3
- package/dist/session/index.d.ts +3 -3
- package/dist/session/index.js +1 -1
- package/dist/shadow/index.d.cts +2 -2
- package/dist/shadow/index.d.ts +2 -2
- package/dist/store/index.d.cts +2 -2
- package/dist/store/index.d.ts +2 -2
- package/dist/sync/index.cjs.map +1 -1
- package/dist/sync/index.d.cts +1 -1
- package/dist/sync/index.d.ts +1 -1
- package/dist/sync/index.js +2 -2
- package/dist/team/index.cjs +11 -0
- package/dist/team/index.cjs.map +1 -1
- package/dist/team/index.d.cts +2 -2
- package/dist/team/index.d.ts +2 -2
- package/dist/team/index.js +4 -4
- package/dist/tx/index.d.cts +2 -2
- package/dist/tx/index.d.ts +2 -2
- package/dist/{types-D-6bmD2c.d.ts → types-DD9eKKNc.d.ts} +644 -72
- package/dist/{types-D3QLmhlk.d.cts → types-arFMsCtn.d.cts} +644 -72
- package/package.json +1 -1
- package/dist/chunk-6IJQ27XN.js.map +0 -1
- package/dist/chunk-B6HF6NTZ.js.map +0 -1
- package/dist/chunk-UFL4DUEV.js.map +0 -1
- /package/dist/{chunk-NZ4XCIKS.js.map → chunk-2WGMYBYS.js.map} +0 -0
- /package/dist/{chunk-3WCRU7TI.js.map → chunk-7XBQS42M.js.map} +0 -0
- /package/dist/{chunk-CL37QSND.js.map → chunk-HC7Z5EQZ.js.map} +0 -0
- /package/dist/{chunk-KPF2HHPI.js.map → chunk-R2ZTGEVP.js.map} +0 -0
- /package/dist/{chunk-GILMPJXB.js.map → chunk-RSPLI376.js.map} +0 -0
- /package/dist/{chunk-INSJBB5W.js.map → chunk-TOQK4KAN.js.map} +0 -0
- /package/dist/{chunk-FAAWLVTF.js.map → chunk-WN6UK7PM.js.map} +0 -0
- /package/dist/{chunk-N2LMZKLR.js.map → chunk-Y4CMTMUW.js.map} +0 -0
- /package/dist/{delegation-XDJCBTI2.js.map → delegation-2DBS2EOH.js.map} +0 -0
- /package/dist/{ledger-5V67MAIL.js.map → ledger-UQIMMKO5.js.map} +0 -0
- /package/dist/{public-envelope-DFJZHXVH.js.map → public-envelope-3QTQADDW.js.map} +0 -0
|
@@ -1770,6 +1770,49 @@ interface PassphrasePolicy {
|
|
|
1770
1770
|
readonly minWordLength?: number;
|
|
1771
1771
|
/** Reject adjacent identical words ("the the"). Default true. */
|
|
1772
1772
|
readonly rejectRepeatedAdjacent?: boolean;
|
|
1773
|
+
/**
|
|
1774
|
+
* Override the default character-class rule (`/^[a-z]+( [a-z]+)*$/`).
|
|
1775
|
+
*
|
|
1776
|
+
* The hub's strict default is lowercase-letters-and-single-spaces
|
|
1777
|
+
* because that's what the EFF wordlist generator emits and what
|
|
1778
|
+
* most attacker password lists are keyed on. Use this knob to allow
|
|
1779
|
+
* digits, uppercase, hyphens, or non-Latin scripts when the
|
|
1780
|
+
* consumer's audience needs them — e.g.:
|
|
1781
|
+
*
|
|
1782
|
+
* ```ts
|
|
1783
|
+
* // Thai + English mix with digits permitted
|
|
1784
|
+
* pattern: /^[\p{L}0-9 ]+( [\p{L}0-9 ]+)*$/u
|
|
1785
|
+
*
|
|
1786
|
+
* // Allow uppercase + hyphens (passphrase-with-hyphens style)
|
|
1787
|
+
* pattern: /^[A-Za-z]+([- ][A-Za-z]+)*$/
|
|
1788
|
+
* ```
|
|
1789
|
+
*
|
|
1790
|
+
* The OTHER structural rules still apply (min-words split by space,
|
|
1791
|
+
* min-word-length, repeated-adjacent, leading/trailing whitespace,
|
|
1792
|
+
* double-space). For non-space-delimited word semantics, use
|
|
1793
|
+
* {@link customValidator} instead.
|
|
1794
|
+
*
|
|
1795
|
+
* Added in pre.8 (#31).
|
|
1796
|
+
*/
|
|
1797
|
+
readonly pattern?: RegExp;
|
|
1798
|
+
/**
|
|
1799
|
+
* Replace ALL validation entirely with a custom function. When set,
|
|
1800
|
+
* none of the other PassphrasePolicy fields apply — the consumer
|
|
1801
|
+
* owns every rule (word splitting, character classes, entropy
|
|
1802
|
+
* thresholds, allowlist/denylist). Use sparingly; this is the
|
|
1803
|
+
* escape hatch for domain-specific phrase formats:
|
|
1804
|
+
*
|
|
1805
|
+
* - Localized wordlists with non-space word boundaries
|
|
1806
|
+
* - BIP-39 seed phrases (24 words, fixed wordlist, etc.)
|
|
1807
|
+
* - Organization-specific HR password policies
|
|
1808
|
+
*
|
|
1809
|
+
* The returned `PassphraseValidationResult` is what
|
|
1810
|
+
* {@link assertStrongPassphrase} dispatches on — `ok: true` accepts;
|
|
1811
|
+
* `ok: false` throws `WeakPassphraseError` with the supplied reason.
|
|
1812
|
+
*
|
|
1813
|
+
* Added in pre.8 (#31).
|
|
1814
|
+
*/
|
|
1815
|
+
readonly customValidator?: (phrase: string) => PassphraseValidationResult;
|
|
1773
1816
|
}
|
|
1774
1817
|
/** Result of a check. Discriminated union — compile-time exhaustive. */
|
|
1775
1818
|
type PassphraseValidationResult = {
|
|
@@ -1876,7 +1919,28 @@ interface UnlockedKeyring {
|
|
|
1876
1919
|
readonly role: Role;
|
|
1877
1920
|
readonly permissions: Permissions;
|
|
1878
1921
|
readonly deks: Map<string, CryptoKey>;
|
|
1879
|
-
|
|
1922
|
+
/**
|
|
1923
|
+
* The KEK, when this keyring was unlocked via tier 1 (passphrase) or
|
|
1924
|
+
* a wrap-KEK tier-2 method (WebAuthn / OIDC). `null` when the
|
|
1925
|
+
* keyring was opened via:
|
|
1926
|
+
*
|
|
1927
|
+
* - Unencrypted mode (no KEK exists)
|
|
1928
|
+
* - Tier-3 PIN quick-resume (`@noy-db/on-pin`)
|
|
1929
|
+
* - Wrap-DEKs tier-2 unlock (`@noy-db/on-password`'s
|
|
1930
|
+
* `verifyPasswordSlot` after #26 Path C)
|
|
1931
|
+
* - Session-state restore (`session/session.ts`)
|
|
1932
|
+
* - Dev-unlock fixture (`session/dev-unlock.ts`)
|
|
1933
|
+
*
|
|
1934
|
+
* Consumers performing tier-1 operations that need the KEK
|
|
1935
|
+
* (DEK rewrap, keyring persist, delegation issue/unwrap) must
|
|
1936
|
+
* null-check and throw a clear error if absent — re-authenticate
|
|
1937
|
+
* at tier 1 first to recover the KEK.
|
|
1938
|
+
*
|
|
1939
|
+
* Tightened from `CryptoKey` to `CryptoKey | null` in pre.8 (#41).
|
|
1940
|
+
* The runtime contract has always allowed null; the type now
|
|
1941
|
+
* matches reality.
|
|
1942
|
+
*/
|
|
1943
|
+
readonly kek: CryptoKey | null;
|
|
1880
1944
|
readonly salt: Uint8Array;
|
|
1881
1945
|
/**
|
|
1882
1946
|
* `@noy-db/as-*` export capability. Absent when the
|
|
@@ -2882,6 +2946,68 @@ declare class SyncEngine {
|
|
|
2882
2946
|
private persistMeta;
|
|
2883
2947
|
}
|
|
2884
2948
|
|
|
2949
|
+
/**
|
|
2950
|
+
* Tier-2 authenticator slot management — issue #11.
|
|
2951
|
+
*
|
|
2952
|
+
* Each slot independently wraps the SAME KEK under a method-specific
|
|
2953
|
+
* derived key (LUKS pattern). Enrolling adds a slot; removing drops
|
|
2954
|
+
* one. Both are constant-time keyring writes — no DEK re-keying.
|
|
2955
|
+
*
|
|
2956
|
+
* The crypto for each method lives in its `@noy-db/on-*` package
|
|
2957
|
+
* (`on-webauthn`, `on-oidc`, `on-password`); this module accepts the
|
|
2958
|
+
* package's `wrapped_kek` ciphertext + `meta` payload and persists it.
|
|
2959
|
+
*
|
|
2960
|
+
* @see docs/subsystems/session-tiers.md → Tier 2 — Authenticate
|
|
2961
|
+
*
|
|
2962
|
+
* @module
|
|
2963
|
+
*/
|
|
2964
|
+
|
|
2965
|
+
/** Fields shared across both wrap-KEK and wrap-DEKs enroll inputs. */
|
|
2966
|
+
interface EnrollAuthenticatorBase {
|
|
2967
|
+
readonly id: string;
|
|
2968
|
+
readonly method: KeyringAuthenticator['method'];
|
|
2969
|
+
/** Method-specific metadata (cred id, salt, …). */
|
|
2970
|
+
readonly meta: Record<string, unknown>;
|
|
2971
|
+
/** Tier the active session held when enrolling. Defaults to 1. */
|
|
2972
|
+
readonly enrolled_via_tier?: 1 | 2;
|
|
2973
|
+
}
|
|
2974
|
+
/** Wrap-KEK enroll input (WebAuthn, OIDC). */
|
|
2975
|
+
interface EnrollAuthenticatorWrappingKEKOptions extends EnrollAuthenticatorBase {
|
|
2976
|
+
/** Already-wrapped KEK ciphertext (base64) — produced by the on-* package. */
|
|
2977
|
+
readonly wrapped_kek: string;
|
|
2978
|
+
readonly wrapKind?: 'kek';
|
|
2979
|
+
}
|
|
2980
|
+
/** Wrap-DEKs enroll input (password, future on-* using the unified wrap-DEKs primitive). */
|
|
2981
|
+
interface EnrollAuthenticatorWrappingDEKsOptions extends EnrollAuthenticatorBase {
|
|
2982
|
+
readonly wrapKind: 'deks';
|
|
2983
|
+
/** Base64 AES-GCM ciphertext of `{ deks: { collection: base64rawDek } }`. */
|
|
2984
|
+
readonly wrapped_deks: string;
|
|
2985
|
+
/** Base64 AES-GCM IV used for the `wrapped_deks` ciphertext. */
|
|
2986
|
+
readonly iv: string;
|
|
2987
|
+
}
|
|
2988
|
+
/** Discriminated union over the two enroll input shapes. */
|
|
2989
|
+
type EnrollAuthenticatorOptions = EnrollAuthenticatorWrappingKEKOptions | EnrollAuthenticatorWrappingDEKsOptions;
|
|
2990
|
+
/**
|
|
2991
|
+
* Append a new authenticator slot to the keyring file. Throws
|
|
2992
|
+
* `ValidationError` if a slot with the same id already exists — the
|
|
2993
|
+
* caller decides whether to remove + re-enroll.
|
|
2994
|
+
*
|
|
2995
|
+
* Accepts either wrap-KEK (WebAuthn, OIDC) or wrap-DEKs (password)
|
|
2996
|
+
* input. The variant is preserved verbatim into `KeyringAuthenticator`.
|
|
2997
|
+
*/
|
|
2998
|
+
declare function enrollAuthenticator(store: NoydbStore, vault: string, keyring: UnlockedKeyring, options: EnrollAuthenticatorOptions): Promise<UnlockedKeyring>;
|
|
2999
|
+
/**
|
|
3000
|
+
* Drop a slot by id. No-op if the slot doesn't exist (idempotent —
|
|
3001
|
+
* removing a non-existent slot is a recoverable retry, not an error).
|
|
3002
|
+
*/
|
|
3003
|
+
declare function removeAuthenticator(store: NoydbStore, vault: string, keyring: UnlockedKeyring, slotId: string): Promise<UnlockedKeyring>;
|
|
3004
|
+
/**
|
|
3005
|
+
* Look up a slot by id. Returns `undefined` when no slot matches.
|
|
3006
|
+
* Used by tier-2 unlock dispatchers to fetch the wrapped KEK + meta
|
|
3007
|
+
* before invoking the method-specific verifier.
|
|
3008
|
+
*/
|
|
3009
|
+
declare function findAuthenticator(keyring: UnlockedKeyring, slotId: string): KeyringAuthenticator | undefined;
|
|
3010
|
+
|
|
2885
3011
|
/**
|
|
2886
3012
|
* Tier-1 change flows — `rotatePassphrase` (user remembers old) and
|
|
2887
3013
|
* `recoverPassphrase` (user supplies a recovery proof). Issue #10.
|
|
@@ -2901,24 +3027,86 @@ declare class SyncEngine {
|
|
|
2901
3027
|
* @module
|
|
2902
3028
|
*/
|
|
2903
3029
|
|
|
3030
|
+
/**
|
|
3031
|
+
* Context handed to a {@link SlotRewrapCeremony} when `rotatePassphrase`
|
|
3032
|
+
* preserves a tier-2 slot. The ceremony's job is to re-derive its
|
|
3033
|
+
* method-specific wrapping material (PRF assertion, PBKDF2 of a
|
|
3034
|
+
* daily-password, etc.) and wrap the freshly rewrapped DEK set under
|
|
3035
|
+
* the new wrapping key.
|
|
3036
|
+
*
|
|
3037
|
+
* Two surfaces are exposed:
|
|
3038
|
+
*
|
|
3039
|
+
* - `newDeks` — the rewrapped (extractable) DEK set the slot will
|
|
3040
|
+
* wrap. This is what `mintPaperRecoveryEntry` / `enrollPassword-
|
|
3041
|
+
* Authenticator` / `wrapKeyringSummary` (in `@noy-db/on-webauthn`)
|
|
3042
|
+
* all consume; effectively the canonical input for every
|
|
3043
|
+
* post-Path C tier-2 ceremony.
|
|
3044
|
+
*
|
|
3045
|
+
* - `newKek` — the freshly-derived KEK (extractable for the
|
|
3046
|
+
* ceremony scope only). Only relevant for forward-compatibility
|
|
3047
|
+
* with a hypothetical future on-* package that wants to wrap the
|
|
3048
|
+
* KEK itself under a method-derived key. None of the shipped
|
|
3049
|
+
* on-* packages need this; they all operate on `newDeks`.
|
|
3050
|
+
*
|
|
3051
|
+
* The ceremony MUST preserve `oldSlot.id` and `oldSlot.method` in the
|
|
3052
|
+
* returned `EnrollAuthenticatorOptions`. Hub validates these — a
|
|
3053
|
+
* mismatch throws `ValidationError` (prevents slot-type swap mid-
|
|
3054
|
+
* rotation, e.g. converting a webauthn slot to a password slot under
|
|
3055
|
+
* cover of preservation).
|
|
3056
|
+
*/
|
|
3057
|
+
interface SlotRewrapContext {
|
|
3058
|
+
readonly newKek: CryptoKey;
|
|
3059
|
+
readonly newDeks: Map<string, CryptoKey>;
|
|
3060
|
+
readonly oldSlot: KeyringAuthenticator;
|
|
3061
|
+
}
|
|
3062
|
+
/**
|
|
3063
|
+
* Callback that re-enrolls one tier-2 slot during `rotatePassphrase`.
|
|
3064
|
+
* Returns the new slot's `EnrollAuthenticatorOptions` — same shape
|
|
3065
|
+
* the consumer would pass to `db.enrollAuthenticator` for a fresh
|
|
3066
|
+
* enrollment. Hub persists the result atomically with the rotation.
|
|
3067
|
+
*/
|
|
3068
|
+
type SlotRewrapCeremony = (ctx: SlotRewrapContext) => Promise<EnrollAuthenticatorOptions>;
|
|
2904
3069
|
/** Caller payload for {@link rotatePassphrase}. */
|
|
2905
3070
|
interface RotatePassphraseInput {
|
|
2906
3071
|
readonly oldPassphrase: string;
|
|
2907
3072
|
readonly newPassphrase: string;
|
|
2908
3073
|
readonly passphrasePolicy?: PassphrasePolicy;
|
|
2909
3074
|
readonly allowWeakPassphrase?: boolean;
|
|
3075
|
+
/**
|
|
3076
|
+
* Map of slot id → re-enrolment ceremony. Slots whose id appears
|
|
3077
|
+
* here are PRESERVED across rotation (the ceremony re-derives the
|
|
3078
|
+
* method-specific wrapping under the new keyring); slots whose id
|
|
3079
|
+
* is absent are DROPPED (the pre-#29 behavior).
|
|
3080
|
+
*
|
|
3081
|
+
* Without this map, `rotatePassphrase` retains the pre-pre.8
|
|
3082
|
+
* behavior of wiping every tier-2 slot. Consumers building a
|
|
3083
|
+
* "rotate without losing my biometric" flow supply ceremonies for
|
|
3084
|
+
* each slot they want to keep.
|
|
3085
|
+
*
|
|
3086
|
+
* If a ceremony throws, the entire rotation throws — no partial
|
|
3087
|
+
* state. Callers wrap individual ceremonies in try/catch + return
|
|
3088
|
+
* a sentinel if they want graceful degradation per slot.
|
|
3089
|
+
*
|
|
3090
|
+
* Added in pre.8 (#29).
|
|
3091
|
+
*/
|
|
3092
|
+
readonly slotCeremonies?: {
|
|
3093
|
+
readonly [slotId: string]: SlotRewrapCeremony;
|
|
3094
|
+
};
|
|
2910
3095
|
}
|
|
2911
3096
|
/**
|
|
2912
3097
|
* Re-derive the user's KEK from `oldPassphrase`, rewrap every DEK
|
|
2913
3098
|
* under a freshly-derived KEK from `newPassphrase`, and persist.
|
|
2914
3099
|
*
|
|
2915
|
-
* Tier-2 authenticator slots are
|
|
2916
|
-
*
|
|
2917
|
-
*
|
|
2918
|
-
*
|
|
3100
|
+
* Tier-2 authenticator slots are dropped UNLESS the caller supplies
|
|
3101
|
+
* a `slotCeremonies` map (#29) — each ceremony re-derives its
|
|
3102
|
+
* method-specific wrapping under the new keyring, and hub persists
|
|
3103
|
+
* the rewrapped slots atomically with the rotation. Slots whose id
|
|
3104
|
+
* isn't in the map are still dropped (pre-pre.8 behavior).
|
|
2919
3105
|
*
|
|
2920
3106
|
* @throws `InvalidKeyError` if `oldPassphrase` does not unwrap the keyring.
|
|
2921
3107
|
* @throws `WeakPassphraseError` if `newPassphrase` fails the strength rule.
|
|
3108
|
+
* @throws `ValidationError` if a ceremony's result mismatches the
|
|
3109
|
+
* slot's id or method (anti-slot-swap guard).
|
|
2922
3110
|
*/
|
|
2923
3111
|
declare function rotatePassphrase(store: NoydbStore, vault: string, userId: string, input: RotatePassphraseInput): Promise<UnlockedKeyring>;
|
|
2924
3112
|
/** Caller payload for {@link recoverPassphrase}. */
|
|
@@ -2949,6 +3137,58 @@ interface RecoverPassphraseInput {
|
|
|
2949
3137
|
readonly recoveryProof: RecoveryProof;
|
|
2950
3138
|
readonly passphrasePolicy?: PassphrasePolicy;
|
|
2951
3139
|
readonly allowWeakPassphrase?: boolean;
|
|
3140
|
+
/**
|
|
3141
|
+
* After a successful paper-recovery, replace ALL remaining recovery
|
|
3142
|
+
* entries with freshly-minted ones. Defaults to `true` (defensive).
|
|
3143
|
+
*
|
|
3144
|
+
* Rationale (issue #36): the user just demonstrated they had access
|
|
3145
|
+
* to AT LEAST one code. The remaining codes from the same printed
|
|
3146
|
+
* sheet may also be compromised — photographed, leaked via a
|
|
3147
|
+
* screen-share slip, or in the hands of whoever stole the sheet.
|
|
3148
|
+
* Auto-rotation closes the window without requiring consumer action.
|
|
3149
|
+
*
|
|
3150
|
+
* Set to `false` to preserve the original behavior (only the matched
|
|
3151
|
+
* code is burned; the rest stay valid).
|
|
3152
|
+
*
|
|
3153
|
+
* Hub-side orchestration is non-atomic with the recovery itself:
|
|
3154
|
+
* if the rotation step fails after a successful burn, the user
|
|
3155
|
+
* falls back to the pre-rotation state (remaining codes still
|
|
3156
|
+
* valid). Strictly safer than the previous default — a failed
|
|
3157
|
+
* rotation degrades gracefully rather than leaving the vault
|
|
3158
|
+
* locked or codes dual-existing.
|
|
3159
|
+
*/
|
|
3160
|
+
readonly rotateRemainingCodes?: boolean;
|
|
3161
|
+
/**
|
|
3162
|
+
* Number of fresh codes to mint when `rotateRemainingCodes` is on.
|
|
3163
|
+
* Defaults to the count of remaining entries POST-burn (e.g. if
|
|
3164
|
+
* the user enrolled 8 originally and just consumed 1, defaults to
|
|
3165
|
+
* 7). Pass an explicit number to mint a different count — useful
|
|
3166
|
+
* when the consumer wants to refresh to a target N regardless of
|
|
3167
|
+
* how many were left.
|
|
3168
|
+
*/
|
|
3169
|
+
readonly newCodeCount?: number;
|
|
3170
|
+
/**
|
|
3171
|
+
* Override the default raw-code generator. The default is hub's
|
|
3172
|
+
* {@link generateULID} — uppercase Crockford-Base32, 26 chars,
|
|
3173
|
+
* passes through `normalizePaperCode` untouched.
|
|
3174
|
+
*
|
|
3175
|
+
* Pass `() => generateRawCode()` from `@noy-db/on-recovery` when
|
|
3176
|
+
* the consumer prefers the Base32 + checksum format with hyphenated
|
|
3177
|
+
* display. The `mintPaperRecoveryEntry` helper accepts any string —
|
|
3178
|
+
* the generator just needs to produce a high-entropy unique value.
|
|
3179
|
+
*/
|
|
3180
|
+
readonly codeGenerator?: () => string;
|
|
3181
|
+
}
|
|
3182
|
+
/**
|
|
3183
|
+
* Return shape of `db.recoverPassphrase`. `newCodes` is populated when
|
|
3184
|
+
* `rotateRemainingCodes` was enabled and at least one entry was
|
|
3185
|
+
* rotated; an empty array means no rotation happened (rotation
|
|
3186
|
+
* disabled, or no remaining codes after burn). Show the codes to the
|
|
3187
|
+
* user once — they are the canonical credential for future recovery
|
|
3188
|
+
* and CANNOT be retrieved again.
|
|
3189
|
+
*/
|
|
3190
|
+
interface RecoverPassphraseResult {
|
|
3191
|
+
readonly newCodes: readonly string[];
|
|
2952
3192
|
}
|
|
2953
3193
|
/**
|
|
2954
3194
|
* Reset the user's passphrase using a recovery proof. v0.1.0-pre.5
|
|
@@ -2961,6 +3201,172 @@ interface RecoverPassphraseInput {
|
|
|
2961
3201
|
*/
|
|
2962
3202
|
declare function recoverPassphrase(store: NoydbStore, vault: string, userId: string, input: RecoverPassphraseInput): Promise<UnlockedKeyring>;
|
|
2963
3203
|
|
|
3204
|
+
/**
|
|
3205
|
+
* Atomic peer-recovery primitive — issues #33 + #34.
|
|
3206
|
+
*
|
|
3207
|
+
* `recoverUser` is a SEPARATE operation from `revoke + grant`. It
|
|
3208
|
+
* exists because peer-recovery has different semantics than account
|
|
3209
|
+
* removal-then-reissue:
|
|
3210
|
+
*
|
|
3211
|
+
* 1. **Same identity preserved.** `userId`, `role`, `permissions`,
|
|
3212
|
+
* capability bits, user envelope (if any), policy override (if
|
|
3213
|
+
* any) all survive. Only the wrapping changes.
|
|
3214
|
+
* 2. **No key rotation.** The existing DEKs stay valid — every
|
|
3215
|
+
* OTHER principal in the vault keeps their access. Rotating
|
|
3216
|
+
* keys would invalidate every co-user's wrapping.
|
|
3217
|
+
* 3. **Atomic by construction.** A single `store.put` overwrites
|
|
3218
|
+
* `_keyring/<userId>` with the recovered file. No revoke step
|
|
3219
|
+
* means no partial-failure window.
|
|
3220
|
+
* 4. **Owner→owner natively allowed.** Two co-owners recovering
|
|
3221
|
+
* each other is the explicitly-intentional case (a partner
|
|
3222
|
+
* forgot the master phrase). The existing `canRevoke` rule that
|
|
3223
|
+
* blocks owner→owner is correct for `revoke` (which is account
|
|
3224
|
+
* *removal*) and intentionally NOT replicated here. The policy
|
|
3225
|
+
* gate `peer-recover-user` carries the freshness requirement.
|
|
3226
|
+
* 5. **Tier-2 slots dropped.** The slots wrap the OLD KEK under
|
|
3227
|
+
* method-derived keys; after recovery the KEK is re-derived
|
|
3228
|
+
* from the new temp passphrase. Match `rotatePassphrase`'s
|
|
3229
|
+
* precedent — the recovered user re-enrols slots after picking
|
|
3230
|
+
* their own phrase.
|
|
3231
|
+
*
|
|
3232
|
+
* Caller must be at least as privileged as the target. The hub
|
|
3233
|
+
* `db.recoverUser` method gates this with the `peer-recover-user`
|
|
3234
|
+
* policy gate (#33's factor-proof requirement); the function below
|
|
3235
|
+
* enforces only the role + anti-privilege-escalation invariants.
|
|
3236
|
+
*
|
|
3237
|
+
* @module
|
|
3238
|
+
*/
|
|
3239
|
+
|
|
3240
|
+
/** Input shape for {@link recoverUser}. */
|
|
3241
|
+
interface RecoverUserOptions {
|
|
3242
|
+
/** Target user id whose keyring is being recovered. */
|
|
3243
|
+
readonly userId: string;
|
|
3244
|
+
/**
|
|
3245
|
+
* Temporary passphrase under which the new keyring is wrapped.
|
|
3246
|
+
* The recipient should call `db.rotatePassphrase` immediately on
|
|
3247
|
+
* acceptance to choose their own phrase — this temp acts as a
|
|
3248
|
+
* single-use bridge in invite / peer-recovery flows.
|
|
3249
|
+
*/
|
|
3250
|
+
readonly passphrase: string;
|
|
3251
|
+
/** Override the target's role. Defaults to the existing target's role. */
|
|
3252
|
+
readonly role?: Role;
|
|
3253
|
+
/** Override the target's display name. Defaults to existing. */
|
|
3254
|
+
readonly displayName?: string;
|
|
3255
|
+
/** Validate phrase strength against the configured policy. */
|
|
3256
|
+
readonly validatePassphrase?: boolean;
|
|
3257
|
+
/**
|
|
3258
|
+
* Skip phrase strength validation even when `validatePassphrase` is
|
|
3259
|
+
* set. The escape hatch matches `grant`'s shape — used when the
|
|
3260
|
+
* temp phrase is a high-entropy one-shot string that doesn't need
|
|
3261
|
+
* to satisfy the human-typeable rules.
|
|
3262
|
+
*/
|
|
3263
|
+
readonly allowWeakPassphrase?: boolean;
|
|
3264
|
+
/**
|
|
3265
|
+
* Optional explicit phrase policy override (passed through to
|
|
3266
|
+
* `assertStrongPassphrase`). Mirrors how `grant` accepts a custom
|
|
3267
|
+
* `PassphrasePolicy` for app-specific tightening.
|
|
3268
|
+
*/
|
|
3269
|
+
readonly passphrasePolicy?: PassphrasePolicy;
|
|
3270
|
+
}
|
|
3271
|
+
/**
|
|
3272
|
+
* Atomically rewrap the target user's keyring under a fresh temp
|
|
3273
|
+
* passphrase. Single store write; no revoke step; no key rotation.
|
|
3274
|
+
*
|
|
3275
|
+
* Caller's responsibilities (NOT enforced here):
|
|
3276
|
+
* - Run the `peer-recover-user` policy gate first via
|
|
3277
|
+
* `Noydb.checkGate` to enforce the freshness factor proof.
|
|
3278
|
+
* - Communicate the temp passphrase to the recipient via a secure
|
|
3279
|
+
* channel (URL fragment, in-person, etc.) — the hub does not
|
|
3280
|
+
* transport secrets.
|
|
3281
|
+
*/
|
|
3282
|
+
declare function recoverUser(store: NoydbStore, vault: string, callerKeyring: UnlockedKeyring, options: RecoverUserOptions): Promise<void>;
|
|
3283
|
+
|
|
3284
|
+
/**
|
|
3285
|
+
* **Wrap-DEKs primitive (#44)** — a single canonical shape for the
|
|
3286
|
+
* pattern of "serialize a DEK set, encrypt it under a credential-derived
|
|
3287
|
+
* AES-GCM key." Used by:
|
|
3288
|
+
*
|
|
3289
|
+
* - **tier-0** — paper recovery entries (`_meta/recovery-paper`),
|
|
3290
|
+
* credential = the printed code.
|
|
3291
|
+
* - **tier-2** — password authenticator slots (`KeyringFile.authenticators`,
|
|
3292
|
+
* `wrapKind: 'deks'`), credential = the daily password.
|
|
3293
|
+
*
|
|
3294
|
+
* **Not** used by `@noy-db/on-pin` — tier-3 wraps the DEK set under
|
|
3295
|
+
* the same conceptual pattern but at **100,000 PBKDF2 iterations**
|
|
3296
|
+
* (vs the 600,000 here), because the protection window for a PIN
|
|
3297
|
+
* slot is short (idle-timeout-bounded, typically 15 min) and 600k
|
|
3298
|
+
* iterations would make every PIN-resume noticeably slow. The wire
|
|
3299
|
+
* formats are deliberately incompatible. See `@noy-db/on-pin`'s
|
|
3300
|
+
* `PIN_PBKDF2_ITERATIONS` and the threat-model rationale in its
|
|
3301
|
+
* module docstring.
|
|
3302
|
+
*
|
|
3303
|
+
* Before #44, the same crypto lived in two places: `mintPaperRecoveryEntry`
|
|
3304
|
+
* (in `team/recovery.ts`) and `enrollPasswordAuthenticator` (in
|
|
3305
|
+
* `@noy-db/on-password`). Both functions did identical work — PBKDF2
|
|
3306
|
+
* the credential, AES-GCM-encrypt the JSON-serialized DEK set — but
|
|
3307
|
+
* their implementations had drifted apart enough that fixing a bug
|
|
3308
|
+
* in one wouldn't fix the other.
|
|
3309
|
+
*
|
|
3310
|
+
* This module owns the canonical implementation. Consumers compose:
|
|
3311
|
+
*
|
|
3312
|
+
* - `mintPaperRecoveryEntry` is now a thin wrapper that calls
|
|
3313
|
+
* `mintWrappedDeksBlob` and adds `{ codeId, enrolledAt }`.
|
|
3314
|
+
* - `enrollPasswordAuthenticator` calls `mintWrappedDeksBlob` and
|
|
3315
|
+
* wraps the result in the slot envelope.
|
|
3316
|
+
*
|
|
3317
|
+
* @module
|
|
3318
|
+
*/
|
|
3319
|
+
/**
|
|
3320
|
+
* The wrap-DEKs primitive — a serialized + AES-GCM-encrypted DEK set
|
|
3321
|
+
* keyed under a credential-derived key.
|
|
3322
|
+
*
|
|
3323
|
+
* All three fields are base64-encoded so the blob is JSON-safe and
|
|
3324
|
+
* round-trips through `_meta/*` envelopes (which carry plaintext
|
|
3325
|
+
* JSON in `_data`).
|
|
3326
|
+
*
|
|
3327
|
+
* Composition: `PaperRecoveryEntry extends WrappedDeksBlob` plus
|
|
3328
|
+
* `{ codeId, enrolledAt }`. `KeyringAuthenticatorWrappingDEKs`
|
|
3329
|
+
* carries the same three fields with `salt` stored in `meta` for
|
|
3330
|
+
* slot-format back-compat (#44 defers moving it to top-level).
|
|
3331
|
+
*/
|
|
3332
|
+
interface WrappedDeksBlob {
|
|
3333
|
+
/** Base64 PBKDF2 salt for the credential-derived wrapping key. */
|
|
3334
|
+
readonly salt: string;
|
|
3335
|
+
/** Base64 AES-GCM IV used for the `wrappedDeks` ciphertext. */
|
|
3336
|
+
readonly iv: string;
|
|
3337
|
+
/** Base64 AES-GCM ciphertext of `{ deks: { collection: base64rawDek } }`. */
|
|
3338
|
+
readonly wrappedDeks: string;
|
|
3339
|
+
}
|
|
3340
|
+
/**
|
|
3341
|
+
* Mint a fresh `WrappedDeksBlob` from a DEK set + a string credential.
|
|
3342
|
+
*
|
|
3343
|
+
* Generates a random salt + IV, derives a 256-bit AES-GCM key via
|
|
3344
|
+
* PBKDF2-SHA256(credential, salt, 600K), serializes the DEK set as
|
|
3345
|
+
* `{ deks: { coll: rawBase64 } }`, and AES-GCM-encrypts.
|
|
3346
|
+
*
|
|
3347
|
+
* The `credential` is the user-typed string (recovery code, daily
|
|
3348
|
+
* password, PIN). Caller normalization rules apply (e.g. paper
|
|
3349
|
+
* recovery uppercase-strips the code before reaching this function).
|
|
3350
|
+
*
|
|
3351
|
+
* @param deks - DEK set to wrap. Each DEK must be exportable via
|
|
3352
|
+
* `subtle.exportKey('raw', dek)` (the hub mints DEKs
|
|
3353
|
+
* this way; consumers feeding non-extractable keys
|
|
3354
|
+
* will get `InvalidAccessError` from WebCrypto).
|
|
3355
|
+
* @param credential - String input the consumer minted (paper code,
|
|
3356
|
+
* password, PIN). Treated as opaque bytes by PBKDF2.
|
|
3357
|
+
*/
|
|
3358
|
+
declare function mintWrappedDeksBlob(deks: Map<string, CryptoKey>, credential: string): Promise<WrappedDeksBlob>;
|
|
3359
|
+
/**
|
|
3360
|
+
* Reverse of {@link mintWrappedDeksBlob}. Re-derives the wrapping key
|
|
3361
|
+
* from the credential + stored salt, AES-GCM-decrypts the wrapped DEK
|
|
3362
|
+
* set, and re-imports each DEK as an extractable AES-GCM CryptoKey.
|
|
3363
|
+
*
|
|
3364
|
+
* Throws (AES-GCM auth tag failure) when the credential doesn't
|
|
3365
|
+
* match the blob. Callers iterating over multiple blobs (e.g. paper
|
|
3366
|
+
* recovery's "try every entry until one matches") should catch.
|
|
3367
|
+
*/
|
|
3368
|
+
declare function unwrapDeksFromBlob(blob: WrappedDeksBlob, credential: string): Promise<Map<string, CryptoKey>>;
|
|
3369
|
+
|
|
2964
3370
|
/**
|
|
2965
3371
|
* Recovery profile persistence + dispatch — issue #10.
|
|
2966
3372
|
*
|
|
@@ -3000,15 +3406,15 @@ declare function recoverPassphrase(store: NoydbStore, vault: string, userId: str
|
|
|
3000
3406
|
* resume — the cryptographic guarantee is identical (AES-GCM with a
|
|
3001
3407
|
* PBKDF2-derived key), and it sidesteps the non-extractable-KEK
|
|
3002
3408
|
* constraint cleanly.
|
|
3409
|
+
*
|
|
3410
|
+
* Type-level composition (#44): `PaperRecoveryEntry extends
|
|
3411
|
+
* WrappedDeksBlob` — the three crypto fields (`salt`, `iv`,
|
|
3412
|
+
* `wrappedDeks`) come from the shared primitive; `codeId` and
|
|
3413
|
+
* `enrolledAt` are paper-recovery's own metadata. Wire format
|
|
3414
|
+
* unchanged.
|
|
3003
3415
|
*/
|
|
3004
|
-
interface PaperRecoveryEntry {
|
|
3416
|
+
interface PaperRecoveryEntry extends WrappedDeksBlob {
|
|
3005
3417
|
readonly codeId: string;
|
|
3006
|
-
/** Base64 PBKDF2 salt. */
|
|
3007
|
-
readonly salt: string;
|
|
3008
|
-
/** Base64 AES-GCM IV used for the wrapped-DEK ciphertext. */
|
|
3009
|
-
readonly iv: string;
|
|
3010
|
-
/** Base64 AES-GCM ciphertext — JSON `{ deks: Record<string, base64> }`. */
|
|
3011
|
-
readonly wrappedDeks: string;
|
|
3012
3418
|
readonly enrolledAt: string;
|
|
3013
3419
|
}
|
|
3014
3420
|
interface PaperRecoveryDoc {
|
|
@@ -3024,6 +3430,33 @@ declare function savePaperRecoveryEntries(store: NoydbStore, vault: string, entr
|
|
|
3024
3430
|
declare function burnPaperRecoveryEntry(store: NoydbStore, vault: string, codeId: string): Promise<void>;
|
|
3025
3431
|
/** Whether at least one recovery profile has any enrolled entries. */
|
|
3026
3432
|
declare function hasRecoveryEnrolled(store: NoydbStore, vault: string): Promise<boolean>;
|
|
3433
|
+
/**
|
|
3434
|
+
* Generate one paper-recovery entry from an unlocked DEK set.
|
|
3435
|
+
*
|
|
3436
|
+
* Returns the serializable entry (persisted via
|
|
3437
|
+
* {@link savePaperRecoveryEntries}). The recovery flow unwraps the
|
|
3438
|
+
* DEK set, then mints a fresh KEK from the user's new passphrase.
|
|
3439
|
+
*
|
|
3440
|
+
* Thin wrapper over {@link mintWrappedDeksBlob} (#44) — the crypto
|
|
3441
|
+
* lives in the shared primitive; this function just adds paper-
|
|
3442
|
+
* recovery's own metadata (`codeId`, `enrolledAt`).
|
|
3443
|
+
*
|
|
3444
|
+
* @param deks Map of collection-name → DEK (extractable).
|
|
3445
|
+
* @param code The plaintext recovery code (caller-supplied;
|
|
3446
|
+
* pair this with `@noy-db/on-recovery`'s code
|
|
3447
|
+
* generator/parser if available).
|
|
3448
|
+
* @param codeId Stable id used by `burnPaperRecoveryEntry`.
|
|
3449
|
+
*/
|
|
3450
|
+
declare function mintPaperRecoveryEntry(deks: Map<string, CryptoKey>, code: string, codeId: string): Promise<PaperRecoveryEntry>;
|
|
3451
|
+
/**
|
|
3452
|
+
* Decrypt a recovery entry to recover the raw DEK set. Used by the
|
|
3453
|
+
* `recoverPassphrase` flow after the user's code has been parsed.
|
|
3454
|
+
*
|
|
3455
|
+
* Thin wrapper over {@link unwrapDeksFromBlob} (#44).
|
|
3456
|
+
*
|
|
3457
|
+
* @throws when the code does not match the entry (AES-GCM auth tag fail).
|
|
3458
|
+
*/
|
|
3459
|
+
declare function unwrapDeksFromPaperEntry(entry: PaperRecoveryEntry, code: string): Promise<Map<string, CryptoKey>>;
|
|
3027
3460
|
|
|
3028
3461
|
/**
|
|
3029
3462
|
* Public envelope — owner-curated plaintext metadata, readable
|
|
@@ -3130,51 +3563,6 @@ declare function validatePublicEnvelopeInput(input: SetPublicEnvelopeInput, sche
|
|
|
3130
3563
|
*/
|
|
3131
3564
|
declare function isPublicEnvelope(x: unknown): x is PublicEnvelope;
|
|
3132
3565
|
|
|
3133
|
-
/**
|
|
3134
|
-
* Tier-2 authenticator slot management — issue #11.
|
|
3135
|
-
*
|
|
3136
|
-
* Each slot independently wraps the SAME KEK under a method-specific
|
|
3137
|
-
* derived key (LUKS pattern). Enrolling adds a slot; removing drops
|
|
3138
|
-
* one. Both are constant-time keyring writes — no DEK re-keying.
|
|
3139
|
-
*
|
|
3140
|
-
* The crypto for each method lives in its `@noy-db/on-*` package
|
|
3141
|
-
* (`on-webauthn`, `on-oidc`, `on-password`); this module accepts the
|
|
3142
|
-
* package's `wrapped_kek` ciphertext + `meta` payload and persists it.
|
|
3143
|
-
*
|
|
3144
|
-
* @see docs/subsystems/session-tiers.md → Tier 2 — Authenticate
|
|
3145
|
-
*
|
|
3146
|
-
* @module
|
|
3147
|
-
*/
|
|
3148
|
-
|
|
3149
|
-
/** Input shape for `enrollAuthenticator`. */
|
|
3150
|
-
interface EnrollAuthenticatorOptions {
|
|
3151
|
-
readonly id: string;
|
|
3152
|
-
readonly method: KeyringAuthenticator['method'];
|
|
3153
|
-
/** Already-wrapped KEK ciphertext (base64) — produced by the on-* package. */
|
|
3154
|
-
readonly wrapped_kek: string;
|
|
3155
|
-
/** Method-specific metadata (cred id, salt, …). */
|
|
3156
|
-
readonly meta: Record<string, unknown>;
|
|
3157
|
-
/** Tier the active session held when enrolling. Defaults to 1. */
|
|
3158
|
-
readonly enrolled_via_tier?: 1 | 2;
|
|
3159
|
-
}
|
|
3160
|
-
/**
|
|
3161
|
-
* Append a new authenticator slot to the keyring file. Throws
|
|
3162
|
-
* `ValidationError` if a slot with the same id already exists — the
|
|
3163
|
-
* caller decides whether to remove + re-enroll.
|
|
3164
|
-
*/
|
|
3165
|
-
declare function enrollAuthenticator(store: NoydbStore, vault: string, keyring: UnlockedKeyring, options: EnrollAuthenticatorOptions): Promise<UnlockedKeyring>;
|
|
3166
|
-
/**
|
|
3167
|
-
* Drop a slot by id. No-op if the slot doesn't exist (idempotent —
|
|
3168
|
-
* removing a non-existent slot is a recoverable retry, not an error).
|
|
3169
|
-
*/
|
|
3170
|
-
declare function removeAuthenticator(store: NoydbStore, vault: string, keyring: UnlockedKeyring, slotId: string): Promise<UnlockedKeyring>;
|
|
3171
|
-
/**
|
|
3172
|
-
* Look up a slot by id. Returns `undefined` when no slot matches.
|
|
3173
|
-
* Used by tier-2 unlock dispatchers to fetch the wrapped KEK + meta
|
|
3174
|
-
* before invoking the method-specific verifier.
|
|
3175
|
-
*/
|
|
3176
|
-
declare function findAuthenticator(keyring: UnlockedKeyring, slotId: string): KeyringAuthenticator | undefined;
|
|
3177
|
-
|
|
3178
3566
|
/**
|
|
3179
3567
|
* Per-vault tier-3 (PIN / quick-resume) state — issue #11.
|
|
3180
3568
|
*
|
|
@@ -3375,8 +3763,35 @@ declare function runTransaction<T>(db: Noydb, fn: (tx: TxContext) => Promise<T>
|
|
|
3375
3763
|
* @module
|
|
3376
3764
|
*/
|
|
3377
3765
|
|
|
3378
|
-
/**
|
|
3379
|
-
|
|
3766
|
+
/**
|
|
3767
|
+
* A single factor surface — the proof an actor presents at gate time.
|
|
3768
|
+
*
|
|
3769
|
+
* | Kind | Source | Off-device? |
|
|
3770
|
+
* |---|---|---|
|
|
3771
|
+
* | `totp` | RFC 6238 authenticator app (Google Auth, 1Password) | yes |
|
|
3772
|
+
* | `email-otp` | one-time code mailed to the user | yes |
|
|
3773
|
+
* | `recovery` | printable Base32 code (`@noy-db/on-recovery`) | yes (paper) |
|
|
3774
|
+
* | `shamir` | k-of-n threshold share (`@noy-db/on-shamir`) | yes |
|
|
3775
|
+
* | `webauthn-roaming` | hardware key (YubiKey, SoloKey, Titan) | yes (key portable) |
|
|
3776
|
+
* | `webauthn-platform` | platform passkey (Touch ID, Face ID, Hello) | no (device-bound) |
|
|
3777
|
+
* | `password` | tier-2 daily password (`@noy-db/on-password`) | no |
|
|
3778
|
+
* | `pin` | tier-3 quick-resume PIN (`@noy-db/on-pin`) | no |
|
|
3779
|
+
*
|
|
3780
|
+
* Off-device kinds (TOTP, email-OTP, recovery, shamir, roaming WebAuthn)
|
|
3781
|
+
* are the strongest factor proofs because they require something
|
|
3782
|
+
* separate from the device the user just unlocked. Platform / password /
|
|
3783
|
+
* PIN are useful for "fresh proof of *this* user" but don't bind across
|
|
3784
|
+
* devices — policies can require ANY of them or insist on a count of 2
|
|
3785
|
+
* to force a mix.
|
|
3786
|
+
*
|
|
3787
|
+
* Added in pre.8 (#30): `webauthn-platform`, `password`, `pin` —
|
|
3788
|
+
* previously consumers with no off-device infrastructure (no TOTP,
|
|
3789
|
+
* no email-OTP, paper recovery not enrolled) had to disable the
|
|
3790
|
+
* factor requirement entirely on `rotate-passphrase`. Now they can
|
|
3791
|
+
* pin "any second factor I have wired" without losing the freshness
|
|
3792
|
+
* guarantee.
|
|
3793
|
+
*/
|
|
3794
|
+
type FactorKind = 'totp' | 'email-otp' | 'recovery' | 'shamir' | 'webauthn-roaming' | 'webauthn-platform' | 'password' | 'pin';
|
|
3380
3795
|
/**
|
|
3381
3796
|
* One factor requirement entry. The default is "any one of the listed
|
|
3382
3797
|
* factors, fresh within the last 5 minutes". Bumping `count` requires N
|
|
@@ -3419,7 +3834,17 @@ type BuiltInGateName = 'rotate-passphrase' | 'recover-passphrase' | 'enroll-auth
|
|
|
3419
3834
|
/** Authorize a write to one's own user envelope (#22). */
|
|
3420
3835
|
| 'edit-own-profile'
|
|
3421
3836
|
/** Authorize reading other principals' user envelopes (#22). */
|
|
3422
|
-
| 'view-team-profiles'
|
|
3837
|
+
| 'view-team-profiles'
|
|
3838
|
+
/**
|
|
3839
|
+
* Authorize an atomic peer-recovery — `db.recoverUser` (#33, #34).
|
|
3840
|
+
* Distinct from `revoke-user` because peer-recovery is intentional
|
|
3841
|
+
* re-issuance of someone's keyring under a temp passphrase, NOT
|
|
3842
|
+
* removal. Allows owner→owner natively (matches the threat model:
|
|
3843
|
+
* a co-owner explicitly recovering another co-owner). Ships with a
|
|
3844
|
+
* factor-proof default in `STRICT_POLICY` so the issuer must
|
|
3845
|
+
* affirmatively prove identity at the moment of recovery.
|
|
3846
|
+
*/
|
|
3847
|
+
| 'peer-recover-user';
|
|
3423
3848
|
/** Either a built-in gate name or an `app:*` custom gate. */
|
|
3424
3849
|
type GateName = BuiltInGateName | `app:${string}`;
|
|
3425
3850
|
/**
|
|
@@ -3968,20 +4393,84 @@ declare class Noydb {
|
|
|
3968
4393
|
recoverPassphrase(vault: string, input: RecoverPassphraseInput, factors?: {
|
|
3969
4394
|
factors?: ReadonlyArray<FactorProof>;
|
|
3970
4395
|
sharedDevice?: boolean;
|
|
4396
|
+
}): Promise<RecoverPassphraseResult>;
|
|
4397
|
+
/**
|
|
4398
|
+
* Atomic peer-recovery — re-wraps an EXISTING user's keyring under
|
|
4399
|
+
* a fresh temp passphrase in a single store write. Closes #34's
|
|
4400
|
+
* partial-failure window (the previous compose-from-primitives
|
|
4401
|
+
* pattern was `db.revoke + db.grant`, two writes — if the issuer
|
|
4402
|
+
* cancelled between them the target was locked out entirely).
|
|
4403
|
+
*
|
|
4404
|
+
* Different from `db.revoke + db.grant`:
|
|
4405
|
+
*
|
|
4406
|
+
* - Same `userId`, role, permissions, capabilities preserved.
|
|
4407
|
+
* - DEKs unchanged → every other principal in the vault keeps
|
|
4408
|
+
* access. No key rotation.
|
|
4409
|
+
* - Allows owner→owner natively (#33). The existing
|
|
4410
|
+
* `db.revoke` retains its block — peer-recovery is a separate,
|
|
4411
|
+
* intentionally-named operation.
|
|
4412
|
+
* - Tier-2 slots dropped (they wrap the old KEK).
|
|
4413
|
+
*
|
|
4414
|
+
* Gated by `peer-recover-user`; `STRICT_POLICY` requires a
|
|
4415
|
+
* recovery / TOTP / email-OTP factor proof at the moment of
|
|
4416
|
+
* recovery, so the issuer affirmatively re-asserts identity.
|
|
4417
|
+
*
|
|
4418
|
+
* The recipient should call `db.rotatePassphrase` on first session
|
|
4419
|
+
* to choose their own phrase — the temp acts as a single-use
|
|
4420
|
+
* bridge.
|
|
4421
|
+
*
|
|
4422
|
+
* ```ts
|
|
4423
|
+
* await db.recoverUser('acme', {
|
|
4424
|
+
* userId: 'bob',
|
|
4425
|
+
* passphrase: 'temporary-correct-horse-battery-staple-printer',
|
|
4426
|
+
* }, { factors: [{ kind: 'recovery' }] })
|
|
4427
|
+
* // Bob opens createNoydb({ user: 'bob', secret: tempPhrase })
|
|
4428
|
+
* // and immediately calls db.rotatePassphrase to set his own.
|
|
4429
|
+
* ```
|
|
4430
|
+
*
|
|
4431
|
+
* @throws `NoAccessError` when no keyring exists for the target.
|
|
4432
|
+
* @throws `PermissionDeniedError` when the caller's role can't
|
|
4433
|
+
* recover the target's role (admin→owner is blocked even
|
|
4434
|
+
* under recovery).
|
|
4435
|
+
* @throws `PrivilegeEscalationError` when the caller lacks a DEK
|
|
4436
|
+
* the target previously had access to.
|
|
4437
|
+
*
|
|
4438
|
+
* @see #33 #34 — the issues this method closes.
|
|
4439
|
+
*/
|
|
4440
|
+
recoverUser(vault: string, options: RecoverUserOptions, factors?: {
|
|
4441
|
+
factors?: ReadonlyArray<FactorProof>;
|
|
4442
|
+
sharedDevice?: boolean;
|
|
3971
4443
|
}): Promise<void>;
|
|
3972
4444
|
/**
|
|
3973
4445
|
* Persist a recovery enrollment. v0.1.0-pre.5 accepts the `'paper'`
|
|
3974
|
-
* profile
|
|
3975
|
-
*
|
|
3976
|
-
*
|
|
3977
|
-
*
|
|
4446
|
+
* profile.
|
|
4447
|
+
*
|
|
4448
|
+
* The hub wraps the user's DEK set (not the KEK) under a code-derived
|
|
4449
|
+
* AES-GCM key — see `team/recovery.ts` for the rationale. The mint
|
|
4450
|
+
* helper {@link mintPaperRecoveryEntry} is the canonical primitive;
|
|
4451
|
+
* pair it with `db.getKeyring(vault)` to obtain the live DEK set:
|
|
3978
4452
|
*
|
|
3979
4453
|
* ```ts
|
|
3980
|
-
* import {
|
|
3981
|
-
*
|
|
4454
|
+
* import { mintPaperRecoveryEntry } from '@noy-db/hub'
|
|
4455
|
+
*
|
|
4456
|
+
* const keyring = await db.getKeyring('acme')
|
|
4457
|
+
* const codes: string[] = ['CORRECT-HORSE-1', 'BATTERY-STAPLE-2', ...]
|
|
4458
|
+
* const entries = await Promise.all(
|
|
4459
|
+
* codes.map((code, i) => mintPaperRecoveryEntry(keyring.deks, code, `code-${i}`)),
|
|
4460
|
+
* )
|
|
3982
4461
|
* await db.enrollRecovery('acme', { profile: 'paper', entries })
|
|
3983
4462
|
* showCodesToUser(codes)
|
|
3984
4463
|
* ```
|
|
4464
|
+
*
|
|
4465
|
+
* As of pre.8, `@noy-db/on-recovery`'s `generateRecoveryCodeSet`
|
|
4466
|
+
* delegates to `mintPaperRecoveryEntry` internally — its output is
|
|
4467
|
+
* fed directly to this API. Pick whichever fits your code-gen layer:
|
|
4468
|
+
*
|
|
4469
|
+
* ```ts
|
|
4470
|
+
* import { generateRecoveryCodeSet } from '@noy-db/on-recovery'
|
|
4471
|
+
* const { codes, entries } = await generateRecoveryCodeSet({ deks: keyring.deks, count: 8 })
|
|
4472
|
+
* await db.enrollRecovery('acme', { profile: 'paper', entries })
|
|
4473
|
+
* ```
|
|
3985
4474
|
*/
|
|
3986
4475
|
enrollRecovery(vault: string, enrollment: {
|
|
3987
4476
|
profile: 'paper';
|
|
@@ -4016,8 +4505,30 @@ declare class Noydb {
|
|
|
4016
4505
|
unlockViaPin(vault: string, resume: (state: QuickUnlockState) => Promise<UnlockedKeyring>): Promise<UnlockedKeyring | undefined>;
|
|
4017
4506
|
/** Drop the tier-3 state for a vault — explicit logout. */
|
|
4018
4507
|
clearQuickUnlock(vault: string): void;
|
|
4019
|
-
/**
|
|
4020
|
-
|
|
4508
|
+
/**
|
|
4509
|
+
* Public accessor for the unlocked keyring of a vault — issue #28.
|
|
4510
|
+
*
|
|
4511
|
+
* Returns the cached `UnlockedKeyring` (already in memory after
|
|
4512
|
+
* `createNoydb` + first vault touch); loads it on demand if absent.
|
|
4513
|
+
* Used by `@noy-db/on-*` ceremonies that need the live DEK set
|
|
4514
|
+
* (paper recovery via {@link mintPaperRecoveryEntry}, tier-3 PIN
|
|
4515
|
+
* enrolment via on-pin's `enrollPin`, custom on-* ceremonies that
|
|
4516
|
+
* don't have a hub-side wrapper).
|
|
4517
|
+
*
|
|
4518
|
+
* No new permission gate — this is an accessor over already-unlocked
|
|
4519
|
+
* state. The keyring is materialized only after the calling session
|
|
4520
|
+
* has unlocked the vault at tier 1, 2, or 3, so exposing it does not
|
|
4521
|
+
* widen access. Throws `ValidationError` when encryption is enabled
|
|
4522
|
+
* and no `secret` / `getKeyring` is configured.
|
|
4523
|
+
*
|
|
4524
|
+
* ```ts
|
|
4525
|
+
* const keyring = await db.getKeyring('acme')
|
|
4526
|
+
* // keyring.deks: Map<collection, CryptoKey>
|
|
4527
|
+
* // keyring.kek: CryptoKey (non-extractable; null for tier-3 sessions)
|
|
4528
|
+
* // keyring.role / .permissions / .authenticators
|
|
4529
|
+
* ```
|
|
4530
|
+
*/
|
|
4531
|
+
getKeyring(vault: string): Promise<UnlockedKeyring>;
|
|
4021
4532
|
}
|
|
4022
4533
|
/** Create a new NOYDB instance. */
|
|
4023
4534
|
declare function createNoydb(options: NoydbOptions): Promise<Noydb>;
|
|
@@ -7588,7 +8099,13 @@ type RecoveryEnrollment = {
|
|
|
7588
8099
|
*
|
|
7589
8100
|
* @see docs/subsystems/session-tiers.md → Tier 2 — Authenticate (multi-slot)
|
|
7590
8101
|
*/
|
|
7591
|
-
|
|
8102
|
+
/**
|
|
8103
|
+
* Shared fields across all authenticator slot variants. The variant
|
|
8104
|
+
* (`KeyringAuthenticatorWrappingKEK` vs `KeyringAuthenticatorWrappingDEKs`)
|
|
8105
|
+
* carries the actual wrapped material; everything below is identity +
|
|
8106
|
+
* metadata only.
|
|
8107
|
+
*/
|
|
8108
|
+
interface KeyringAuthenticatorBase {
|
|
7592
8109
|
/** Caller-chosen identifier — e.g. `'webauthn-yubikey-blue'`, `'oidc-google'`, `'password-daily'`. */
|
|
7593
8110
|
readonly id: string;
|
|
7594
8111
|
/** Method family — selects which `@noy-db/on-*` package handles unlock. */
|
|
@@ -7600,8 +8117,6 @@ interface KeyringAuthenticator {
|
|
|
7600
8117
|
* tier 2 may add a sibling slot when the active policy permits.
|
|
7601
8118
|
*/
|
|
7602
8119
|
readonly enrolled_via_tier: 1 | 2;
|
|
7603
|
-
/** Base64 wrapped-KEK ciphertext under the method-derived key. */
|
|
7604
|
-
readonly wrapped_kek: string;
|
|
7605
8120
|
/**
|
|
7606
8121
|
* Method-specific metadata: WebAuthn cred id, OIDC issuer/sub, PBKDF2
|
|
7607
8122
|
* salt for `on-password`, etc. The schema is open by design — the
|
|
@@ -7609,6 +8124,63 @@ interface KeyringAuthenticator {
|
|
|
7609
8124
|
*/
|
|
7610
8125
|
readonly meta: Record<string, unknown>;
|
|
7611
8126
|
}
|
|
8127
|
+
/**
|
|
8128
|
+
* Slot that wraps the KEK directly under a method-derived AES-KW key.
|
|
8129
|
+
* Used by ceremonies where the on-* package can produce/recover an
|
|
8130
|
+
* extractable KEK from its own credential — WebAuthn (PRF-derived
|
|
8131
|
+
* wrapping key) and split-key OIDC.
|
|
8132
|
+
*
|
|
8133
|
+
* `wrapKind` is optional/absent on slots written before pre.8 — those
|
|
8134
|
+
* legacy slots are treated as wrap-KEK by default at unlock time.
|
|
8135
|
+
*/
|
|
8136
|
+
interface KeyringAuthenticatorWrappingKEK extends KeyringAuthenticatorBase {
|
|
8137
|
+
readonly wrapKind?: 'kek';
|
|
8138
|
+
/** Base64 wrapped-KEK ciphertext under the method-derived key. */
|
|
8139
|
+
readonly wrapped_kek: string;
|
|
8140
|
+
/** XOR guard — wrap-KEK slots must NOT carry wrap-DEKs material. */
|
|
8141
|
+
readonly wrapped_deks?: never;
|
|
8142
|
+
/** XOR guard — wrap-KEK slots must NOT carry wrap-DEKs material. */
|
|
8143
|
+
readonly iv?: never;
|
|
8144
|
+
}
|
|
8145
|
+
/**
|
|
8146
|
+
* Slot that wraps the DEK set (not the KEK) under a method-derived
|
|
8147
|
+
* AES-GCM key — sidesteps the non-extractable-KEK constraint by
|
|
8148
|
+
* encrypting the serialized `{ deks: { collection: rawDekBase64 } }`
|
|
8149
|
+
* directly. Mirrors the format used by `mintPaperRecoveryEntry`
|
|
8150
|
+
* (`PaperRecoveryEntry`) and `@noy-db/on-pin`'s `PinResumeState` —
|
|
8151
|
+
* the unified wrap-DEKs primitive across tier-0 / tier-2 / tier-3.
|
|
8152
|
+
*
|
|
8153
|
+
* Trade-off: a slot of this kind reconstructs `UnlockedKeyring` with
|
|
8154
|
+
* `kek: null` after unlock. That is semantically correct for tier-2
|
|
8155
|
+
* (sensitive ops like `enrollAuthenticator` / `rotatePassphrase`
|
|
8156
|
+
* require a tier-1 unlock anyway) and matches how `@noy-db/on-pin`
|
|
8157
|
+
* already behaves at tier 3.
|
|
8158
|
+
*
|
|
8159
|
+
* @see `mintPaperRecoveryEntry` in `team/recovery.ts` — same shape on
|
|
8160
|
+
* a different on-disk path (`_meta/recovery-paper`).
|
|
8161
|
+
*/
|
|
8162
|
+
interface KeyringAuthenticatorWrappingDEKs extends KeyringAuthenticatorBase {
|
|
8163
|
+
readonly wrapKind: 'deks';
|
|
8164
|
+
/** Base64 AES-GCM ciphertext of `{ deks: { collection: base64rawDek } }`. */
|
|
8165
|
+
readonly wrapped_deks: string;
|
|
8166
|
+
/** Base64 AES-GCM IV used for the `wrapped_deks` ciphertext. */
|
|
8167
|
+
readonly iv: string;
|
|
8168
|
+
/** XOR guard — wrap-DEKs slots must NOT carry wrap-KEK material. */
|
|
8169
|
+
readonly wrapped_kek?: never;
|
|
8170
|
+
}
|
|
8171
|
+
/**
|
|
8172
|
+
* Discriminated union over the two wrap-format variants. Reads from
|
|
8173
|
+
* disk should always go through this type so the variant is preserved.
|
|
8174
|
+
*
|
|
8175
|
+
* Discriminator: `wrapKind`. Absent → wrap-KEK (legacy / WebAuthn /
|
|
8176
|
+
* OIDC). Present and `'deks'` → wrap-DEKs (password / future on-* that
|
|
8177
|
+
* want to sidestep extractable-KEK).
|
|
8178
|
+
*
|
|
8179
|
+
* The type-level XOR enforces "exactly one of `wrapped_kek` /
|
|
8180
|
+
* `wrapped_deks` is present" — a structural guarantee that the runtime
|
|
8181
|
+
* dispatch is safe.
|
|
8182
|
+
*/
|
|
8183
|
+
type KeyringAuthenticator = KeyringAuthenticatorWrappingKEK | KeyringAuthenticatorWrappingDEKs;
|
|
7612
8184
|
interface KeyringFile {
|
|
7613
8185
|
readonly _noydb_keyring: typeof NOYDB_KEYRING_VERSION;
|
|
7614
8186
|
readonly user_id: string;
|
|
@@ -8830,4 +9402,4 @@ interface DeleteManyResult {
|
|
|
8830
9402
|
}>;
|
|
8831
9403
|
}
|
|
8832
9404
|
|
|
8833
|
-
export { type ConsentAuditEntry as $, type BlobObject as A, type BlobStrategy as B, type BlobPutOptions as C, DICT_COLLECTION_PREFIX as D, type BlobResponseOptions as E, BlobSet as F, type BlobStrategyOpenArgs as G, type CompactRunOptions as H, type I18nStrategy as I, type CompactionContext as J, type CompactionResult as K, DEFAULT_CHUNK_SIZE as L, EXPORT_AUDIT_COLLECTION as M, ExportBlobsAbortedError as N, type ExportBlobsAuditEntry as O, PolicyEnforcer as P, type ExportBlobsHandle as Q, type ExportBlobsOptions as R, type SessionStrategy as S, type ExportedBlob as T, type SlotInfo as U, type SlotRecord as V, type VersionRecord as W, createExportBlobsHandle as X, runCompaction as Y, type ConsentStrategy as Z, CONSENT_AUDIT_COLLECTION as _, type DictEntry as a, type BuiltInGateName as a$, type ConsentAuditFilter as a0, type ConsentContext as a1, type ConsentOp as a2, loadConsentEntries as a3, writeConsentEntry as a4, type PeriodsStrategy as a5, type CarryForwardContext as a6, type ClosePeriodOptions as a7, type OpenPeriodOptions as a8, PERIODS_COLLECTION as a9, type DiffEntry as aA, type JsonPatch as aB, type JsonPatchOp as aC, type LedgerEntry as aD, LedgerStore as aE, type VaultEngine as aF, VaultInstant as aG, type VerifyResult as aH, applyPatch as aI, canonicalJson as aJ, computePatch as aK, diff as aL, formatDiff as aM, hashEntry as aN, paddedIndex as aO, parseIndex as aP, sha256Hex as aQ, type UserEnvelope as aR, type PublicEnvelope as aS, type GateName as aT, type GatePolicy as aU, type VaultPolicy as aV, type ActiveTier as aW, type FactorProof as aX, Vault as aY, type AccessibleVault as aZ, BUNDLE_STORE_POLICY as a_, type PeriodRecord as aa, type ReadOnlyCollection as ab, appendPeriodLedgerEntry as ac, assertTsWritable as ad, chainAnchor as ae, loadPeriods as af, validatePeriodName as ag, type ShadowStrategy as ah, CollectionFrame as ai, VaultFrame as aj, type TxStrategy as ak, TxCollection as al, TxContext as am, TxVault as an, runTransaction as ao, type SyncStrategy as ap, type Role as aq, type UnlockedKeyring as ar, type HistoryStrategy as as, type NoydbStore as at, type HistoryOptions as au, type EncryptedEnvelope as av, type PruneOptions as aw, type AppendInput as ax, type ChangeType as ay, CollectionInstant as az, type DictKeyDescriptor as b, type Permissions as b$, type BundleRecipient as b0, type CacheOptions as b1, type CacheStats as b2, type ChangeEvent as b3, Collection as b4, type CollectionChangeEvent as b5, type CollectionConflictResolver as b6, type Conflict as b7, type ConflictPolicy as b8, type ConflictStrategy as b9, type KeyringFile as bA, type ListAccessibleVaultsOptions as bB, type ListPageResult as bC, type LiveUserEnvelope as bD, type LocaleReadOptions as bE, Lru as bF, type LruOptions as bG, type LruStats as bH, MAGIC_LINK_CONTENT_INFO_PREFIX as bI, MAGIC_LINK_GRANTS_COLLECTION as bJ, MAGIC_LINK_KEK_INFO_PREFIX as bK, type MagicLinkGrantPayload as bL, type MagicLinkGrantRecord as bM, NOYDB_BACKUP_VERSION as bN, NOYDB_FORMAT_VERSION as bO, NOYDB_KEYRING_VERSION as bP, NOYDB_SYNC_VERSION as bQ, Noydb as bR, type NoydbBundleStore as bS, type NoydbEventMap as bT, type NoydbOptions as bU, PUBLIC_ENVELOPE_FIELDS as bV, type PaperRecoveryDoc as bW, type PaperRecoveryEntry as bX, type PassphrasePolicy as bY, type PassphraseValidationResult as bZ, type Permission as b_, type CrossTierAccessEvent as ba, DEFAULT_PUBLIC_ENVELOPE_SCHEMA as bb, DELEGATIONS_COLLECTION as bc, type DeepPartial as bd, type DelegationToken as be, type DeleteManyResult as bf, type DirtyEntry as bg, ELEVATION_AUDIT_COLLECTION as bh, ElevatedHandle as bi, type EnrollAuthenticatorOptions as bj, type ExportCapability as bk, type ExportChunk as bl, type ExportFormat as bm, type ExportStreamOptions as bn, type FactorKind as bo, type FactorRequirement as bp, type GhostRecord as bq, type GrantOptions as br, type HistoryConfig as bs, type HistoryEntry as bt, INDEXED_STORE_POLICY as bu, type ImportCapability as bv, type InferOutput as bw, type IssueDelegationOptions as bx, type IssueMagicLinkGrantOptions as by, type KeyringAuthenticator as bz, DictionaryHandle as c,
|
|
9405
|
+
export { type ConsentAuditEntry as $, type BlobObject as A, type BlobStrategy as B, type BlobPutOptions as C, DICT_COLLECTION_PREFIX as D, type BlobResponseOptions as E, BlobSet as F, type BlobStrategyOpenArgs as G, type CompactRunOptions as H, type I18nStrategy as I, type CompactionContext as J, type CompactionResult as K, DEFAULT_CHUNK_SIZE as L, EXPORT_AUDIT_COLLECTION as M, ExportBlobsAbortedError as N, type ExportBlobsAuditEntry as O, PolicyEnforcer as P, type ExportBlobsHandle as Q, type ExportBlobsOptions as R, type SessionStrategy as S, type ExportedBlob as T, type SlotInfo as U, type SlotRecord as V, type VersionRecord as W, createExportBlobsHandle as X, runCompaction as Y, type ConsentStrategy as Z, CONSENT_AUDIT_COLLECTION as _, type DictEntry as a, type BuiltInGateName as a$, type ConsentAuditFilter as a0, type ConsentContext as a1, type ConsentOp as a2, loadConsentEntries as a3, writeConsentEntry as a4, type PeriodsStrategy as a5, type CarryForwardContext as a6, type ClosePeriodOptions as a7, type OpenPeriodOptions as a8, PERIODS_COLLECTION as a9, type DiffEntry as aA, type JsonPatch as aB, type JsonPatchOp as aC, type LedgerEntry as aD, LedgerStore as aE, type VaultEngine as aF, VaultInstant as aG, type VerifyResult as aH, applyPatch as aI, canonicalJson as aJ, computePatch as aK, diff as aL, formatDiff as aM, hashEntry as aN, paddedIndex as aO, parseIndex as aP, sha256Hex as aQ, type UserEnvelope as aR, type PublicEnvelope as aS, type GateName as aT, type GatePolicy as aU, type VaultPolicy as aV, type ActiveTier as aW, type FactorProof as aX, Vault as aY, type AccessibleVault as aZ, BUNDLE_STORE_POLICY as a_, type PeriodRecord as aa, type ReadOnlyCollection as ab, appendPeriodLedgerEntry as ac, assertTsWritable as ad, chainAnchor as ae, loadPeriods as af, validatePeriodName as ag, type ShadowStrategy as ah, CollectionFrame as ai, VaultFrame as aj, type TxStrategy as ak, TxCollection as al, TxContext as am, TxVault as an, runTransaction as ao, type SyncStrategy as ap, type Role as aq, type UnlockedKeyring as ar, type HistoryStrategy as as, type NoydbStore as at, type HistoryOptions as au, type EncryptedEnvelope as av, type PruneOptions as aw, type AppendInput as ax, type ChangeType as ay, CollectionInstant as az, type DictKeyDescriptor as b, type Permissions as b$, type BundleRecipient as b0, type CacheOptions as b1, type CacheStats as b2, type ChangeEvent as b3, Collection as b4, type CollectionChangeEvent as b5, type CollectionConflictResolver as b6, type Conflict as b7, type ConflictPolicy as b8, type ConflictStrategy as b9, type KeyringFile as bA, type ListAccessibleVaultsOptions as bB, type ListPageResult as bC, type LiveUserEnvelope as bD, type LocaleReadOptions as bE, Lru as bF, type LruOptions as bG, type LruStats as bH, MAGIC_LINK_CONTENT_INFO_PREFIX as bI, MAGIC_LINK_GRANTS_COLLECTION as bJ, MAGIC_LINK_KEK_INFO_PREFIX as bK, type MagicLinkGrantPayload as bL, type MagicLinkGrantRecord as bM, NOYDB_BACKUP_VERSION as bN, NOYDB_FORMAT_VERSION as bO, NOYDB_KEYRING_VERSION as bP, NOYDB_SYNC_VERSION as bQ, Noydb as bR, type NoydbBundleStore as bS, type NoydbEventMap as bT, type NoydbOptions as bU, PUBLIC_ENVELOPE_FIELDS as bV, type PaperRecoveryDoc as bW, type PaperRecoveryEntry as bX, type PassphrasePolicy as bY, type PassphraseValidationResult as bZ, type Permission as b_, type CrossTierAccessEvent as ba, DEFAULT_PUBLIC_ENVELOPE_SCHEMA as bb, DELEGATIONS_COLLECTION as bc, type DeepPartial as bd, type DelegationToken as be, type DeleteManyResult as bf, type DirtyEntry as bg, ELEVATION_AUDIT_COLLECTION as bh, ElevatedHandle as bi, type EnrollAuthenticatorOptions as bj, type ExportCapability as bk, type ExportChunk as bl, type ExportFormat as bm, type ExportStreamOptions as bn, type FactorKind as bo, type FactorRequirement as bp, type GhostRecord as bq, type GrantOptions as br, type HistoryConfig as bs, type HistoryEntry as bt, INDEXED_STORE_POLICY as bu, type ImportCapability as bv, type InferOutput as bw, type IssueDelegationOptions as bx, type IssueMagicLinkGrantOptions as by, type KeyringAuthenticator as bz, DictionaryHandle as c, WeakPassphraseError as c$, type PlaintextTranslatorContext as c0, type PlaintextTranslatorFn as c1, PresenceHandle as c2, type PresencePeer as c3, type PublicEnvelopeField as c4, type PublicEnvelopeSchema as c5, type PublicEnvelopeText as c6, type PullMode as c7, type PullOptions as c8, type PullPolicy as c9, type StoreAuthKind as cA, type StoreCapabilities as cB, SyncEngine as cC, type SyncMetadata as cD, type SyncPolicy as cE, SyncScheduler as cF, type SyncSchedulerStatus as cG, type SyncStatus as cH, type SyncTarget as cI, type SyncTargetRole as cJ, SyncTransaction as cK, type SyncTransactionResult as cL, type TierMode as cM, type TranslatorAuditEntry as cN, type TxOp as cO, USER_ENVELOPE_COLLECTION as cP, USER_ENVELOPE_MAX_BYTES as cQ, type Unsubscribe as cR, UserApi as cS, type UserEnvelopeCheckGate as cT, UserEnvelopeOversizedError as cU, type UserEnvelopePresented as cV, type UserInfo as cW, type VaultBackup as cX, type VaultPolicyOnDisk as cY, type VaultSnapshot as cZ, type WarningRules as c_, type PullResult as ca, type PushMode as cb, type PushOptions as cc, type PushPolicy as cd, type PushResult as ce, type PutManyItemOptions as cf, type PutManyOptions as cg, type PutManyResult as ch, type QueryAcrossOptions as ci, type QueryAcrossResult as cj, type QuickUnlockState as ck, QuickUnlockStore as cl, type ReAuthOperation as cm, type RecoverPassphraseInput as cn, type RecoverPassphraseResult as co, type RecoverUserOptions as cp, type RecoveryProof as cq, type ResolvedPublicEnvelopeSchema as cr, type RevokeOptions as cs, type RotatePassphraseInput as ct, type SessionPolicy as cu, type SetPublicEnvelopeInput as cv, type StandardSchemaV1 as cw, type StandardSchemaV1Issue as cx, type StandardSchemaV1SyncResult as cy, type StoreAuth as cz, type DictionaryOptions as d, type WeakPassphraseReason as d0, type WrappedDeksBlob as d1, assertStrongPassphrase as d2, buildRecipientKeyringFile as d3, burnPaperRecoveryEntry as d4, createNoydb as d5, createStore as d6, deriveMagicLinkContentKey as d7, enrollAuthenticator as d8, estimateEntropy as d9, savePaperRecoveryEntries as dA, unwrapDeksFromBlob as dB, unwrapDeksFromPaperEntry as dC, unwrapMagicLinkGrant as dD, validatePassphrase as dE, validatePublicEnvelopeInput as dF, validateSchemaInput as dG, validateSchemaOutput as dH, writeMagicLinkGrant as dI, evaluateExportCapability as da, evaluateImportCapability as db, findAuthenticator as dc, hasExportCapability as dd, hasImportCapability as de, hasRecoveryEnrolled as df, isMagicLinkGrantExpired as dg, isPublicEnvelope as dh, issueDelegation as di, recoverPassphrase as dj, rotatePassphrase as dk, listMagicLinkGrants as dl, listUsers as dm, listUsersWithEnvelopes as dn, loadActiveDelegations as dp, loadPaperRecoveryEntries as dq, magicLinkGrantRecordId as dr, mintPaperRecoveryEntry as ds, mintWrappedDeksBlob as dt, readMagicLinkGrantRecord as du, recoverUser as dv, removeAuthenticator as dw, resolveSchema as dx, revokeDelegation as dy, revokeMagicLinkGrant as dz, type I18nTextDescriptor as e, type I18nTextOptions as f, applyI18nLocale as g, dictCollectionName as h, dictKey as i, i18nText as j, isDictCollectionName as k, isDictKeyDescriptor as l, isI18nTextDescriptor as m, createEnforcer as n, validateSessionPolicy as o, BLOB_CHUNKS_COLLECTION as p, BLOB_COLLECTION as q, resolveI18nText as r, BLOB_EVICTION_AUDIT_COLLECTION as s, BLOB_INDEX_COLLECTION as t, BLOB_SLOTS_PREFIX as u, validateI18nTextValue as v, BLOB_VERSIONS_PREFIX as w, type BlobEvictionEntry as x, type BlobFieldPolicy as y, type BlobFieldsConfig as z };
|