@noy-db/hub 0.1.0-pre.4 → 0.1.0-pre.7
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 +3 -3
- package/dist/blobs/index.d.ts +3 -3
- package/dist/blobs/index.js +2 -2
- package/dist/bundle/index.cjs +26 -3
- package/dist/bundle/index.cjs.map +1 -1
- package/dist/bundle/index.d.cts +3 -3
- package/dist/bundle/index.d.ts +3 -3
- package/dist/bundle/index.js +3 -1
- package/dist/{chunk-LSZHBNDG.js → chunk-3WCRU7TI.js} +2 -2
- package/dist/{chunk-PSHTHSIX.js → chunk-6IJQ27XN.js} +213 -10
- package/dist/chunk-6IJQ27XN.js.map +1 -0
- package/dist/{chunk-O5GK62FJ.js → chunk-B6HF6NTZ.js} +1 -1
- package/dist/chunk-B6HF6NTZ.js.map +1 -0
- package/dist/{chunk-AVWFLPNR.js → chunk-CL37QSND.js} +2 -2
- package/dist/chunk-EMIGCR7X.js +39 -0
- package/dist/chunk-EMIGCR7X.js.map +1 -0
- package/dist/{chunk-GJILMRPO.js → chunk-FAAWLVTF.js} +42 -4
- package/dist/chunk-FAAWLVTF.js.map +1 -0
- package/dist/chunk-GILMPJXB.js +155 -0
- package/dist/chunk-GILMPJXB.js.map +1 -0
- package/dist/{chunk-L77MEFCH.js → chunk-INSJBB5W.js} +3 -3
- package/dist/{chunk-QZIACZZU.js → chunk-KPF2HHPI.js} +2 -2
- package/dist/{chunk-NK2NSXXK.js → chunk-N2LMZKLR.js} +2 -2
- package/dist/{chunk-EARQCIL7.js → chunk-NZ4XCIKS.js} +3 -3
- package/dist/{chunk-E445ICYI.js → chunk-UFL4DUEV.js} +5 -3
- package/dist/chunk-UFL4DUEV.js.map +1 -0
- package/dist/consent/index.d.cts +3 -3
- package/dist/consent/index.d.ts +3 -3
- package/dist/{dev-unlock-XOUecfQ9.d.ts → dev-unlock-CcJ1qIi7.d.ts} +1 -1
- package/dist/{dev-unlock-5SmCVGyx.d.cts → dev-unlock-Dk14V6lX.d.cts} +1 -1
- package/dist/{hash-Bxud16vM.d.ts → hash-1Xsqx1jl.d.ts} +1 -1
- package/dist/{hash-CvuKN2gH.d.cts → hash-h_2U3TFb.d.cts} +1 -1
- package/dist/history/index.cjs.map +1 -1
- package/dist/history/index.d.cts +4 -4
- package/dist/history/index.d.ts +4 -4
- package/dist/history/index.js +2 -2
- package/dist/i18n/index.cjs +3 -1
- package/dist/i18n/index.cjs.map +1 -1
- package/dist/i18n/index.d.cts +3 -3
- package/dist/i18n/index.d.ts +3 -3
- package/dist/i18n/index.js +3 -3
- package/dist/{index-DN-J-5wT.d.cts → index-6xNpPsxR.d.cts} +1 -1
- package/dist/{index-Cy-MKrdK.d.ts → index-Cvb0efA_.d.cts} +39 -5
- package/dist/{index-BRHBCmLt.d.ts → index-DJTf9yxn.d.ts} +1 -1
- package/dist/{index-BvUiM47h.d.cts → index-DZn6Yick.d.ts} +39 -5
- package/dist/index.cjs +2001 -58
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +315 -19
- package/dist/index.d.ts +315 -19
- package/dist/index.js +1503 -41
- package/dist/index.js.map +1 -1
- package/dist/{ledger-HWXYGUIQ.js → ledger-5V67MAIL.js} +3 -3
- package/dist/periods/index.cjs.map +1 -1
- package/dist/periods/index.d.cts +3 -3
- package/dist/periods/index.d.ts +3 -3
- package/dist/periods/index.js +3 -3
- package/dist/public-envelope-DFJZHXVH.js +31 -0
- package/dist/public-envelope-DFJZHXVH.js.map +1 -0
- package/dist/query/index.d.cts +1 -1
- package/dist/query/index.d.ts +1 -1
- package/dist/session/index.cjs +4 -2
- package/dist/session/index.cjs.map +1 -1
- package/dist/session/index.d.cts +4 -4
- package/dist/session/index.d.ts +4 -4
- package/dist/session/index.js +1 -1
- package/dist/shadow/index.d.cts +3 -3
- package/dist/shadow/index.d.ts +3 -3
- package/dist/store/index.d.cts +3 -3
- package/dist/store/index.d.ts +3 -3
- package/dist/sync/index.cjs.map +1 -1
- package/dist/sync/index.d.cts +2 -2
- package/dist/sync/index.d.ts +2 -2
- package/dist/sync/index.js +2 -2
- package/dist/team/index.cjs +3 -1
- package/dist/team/index.cjs.map +1 -1
- package/dist/team/index.d.cts +3 -3
- package/dist/team/index.d.ts +3 -3
- package/dist/team/index.js +4 -4
- package/dist/tx/index.d.cts +3 -3
- package/dist/tx/index.d.ts +3 -3
- package/dist/{types-Dmi7nrC9.d.ts → types-D-6bmD2c.d.ts} +1271 -3
- package/dist/{types-BVSfkYg6.d.cts → types-D3QLmhlk.d.cts} +1271 -3
- package/package.json +1 -1
- package/dist/chunk-E445ICYI.js.map +0 -1
- package/dist/chunk-GJILMRPO.js.map +0 -1
- package/dist/chunk-O5GK62FJ.js.map +0 -1
- package/dist/chunk-PSHTHSIX.js.map +0 -1
- /package/dist/{chunk-LSZHBNDG.js.map → chunk-3WCRU7TI.js.map} +0 -0
- /package/dist/{chunk-AVWFLPNR.js.map → chunk-CL37QSND.js.map} +0 -0
- /package/dist/{chunk-L77MEFCH.js.map → chunk-INSJBB5W.js.map} +0 -0
- /package/dist/{chunk-QZIACZZU.js.map → chunk-KPF2HHPI.js.map} +0 -0
- /package/dist/{chunk-NK2NSXXK.js.map → chunk-N2LMZKLR.js.map} +0 -0
- /package/dist/{chunk-EARQCIL7.js.map → chunk-NZ4XCIKS.js.map} +0 -0
- /package/dist/{ledger-HWXYGUIQ.js.map → ledger-5V67MAIL.js.map} +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { I as IndexStrategy, d as LazyQuery } from './lazy-builder-BwEoBQZ9.js';
|
|
2
2
|
import { A as AggregateStrategy } from './strategy-D-SrOLCl.js';
|
|
3
3
|
import { C as CrdtStrategy, a as CrdtMode, b as CrdtState } from './strategy-BSxFXGzb.js';
|
|
4
|
-
import { U as RefDescriptor, p as JoinableSource, Z as RefViolation, Q as Query, $ as ScanBuilder } from './index-
|
|
4
|
+
import { N as NoydbError, U as RefDescriptor, p as JoinableSource, Z as RefViolation, Q as Query, $ as ScanBuilder } from './index-DJTf9yxn.js';
|
|
5
5
|
import { I as IndexDef, C as CollectionIndexes } from './predicate-SBHmi6D0.js';
|
|
6
6
|
|
|
7
7
|
/**
|
|
@@ -1739,6 +1739,136 @@ declare class NoydbEventEmitter {
|
|
|
1739
1739
|
removeAllListeners(): void;
|
|
1740
1740
|
}
|
|
1741
1741
|
|
|
1742
|
+
/**
|
|
1743
|
+
* Passphrase validation — phrase format (per the three-tier session-tiers
|
|
1744
|
+
* design, locked 2026-05-04).
|
|
1745
|
+
*
|
|
1746
|
+
* Passphrases are **phrases**: multiple simple words, easy to remember,
|
|
1747
|
+
* structurally constrained so a weak choice cannot silently collapse the
|
|
1748
|
+
* security floor. The format is intentionally narrow: lowercase letters
|
|
1749
|
+
* and single spaces only, no punctuation, no symbols, no digits.
|
|
1750
|
+
*
|
|
1751
|
+
* - Default minimum: 6 words (~77 bits with the 7,776-word EFF list).
|
|
1752
|
+
* - Strict minimum: 8 words (~103 bits).
|
|
1753
|
+
* - Per-word minimum: 3 characters (excludes "a", "is", "of").
|
|
1754
|
+
* - Adjacent repeats rejected ("the the").
|
|
1755
|
+
*
|
|
1756
|
+
* The hub runs validation default-on at every passphrase ingress
|
|
1757
|
+
* (`createOwnerKeyring`, `grant`, `rotatePassphrase`); test fixtures and
|
|
1758
|
+
* CLI scripts override via `{ allowWeakPassphrase: true }`.
|
|
1759
|
+
*
|
|
1760
|
+
* @module
|
|
1761
|
+
*/
|
|
1762
|
+
|
|
1763
|
+
/** All reasons a phrase can be rejected. */
|
|
1764
|
+
type WeakPassphraseReason = 'empty' | 'invalid-chars' | 'leading-or-trailing-space' | 'double-space' | 'too-few-words' | 'word-too-short' | 'repeated-adjacent';
|
|
1765
|
+
/** Per-vault knobs. Aligns with `VaultPolicy.passphrase`. */
|
|
1766
|
+
interface PassphrasePolicy {
|
|
1767
|
+
/** Minimum number of words. Default 6. Strict policy uses 8. */
|
|
1768
|
+
readonly minWords?: number;
|
|
1769
|
+
/** Minimum characters per word. Default 3. */
|
|
1770
|
+
readonly minWordLength?: number;
|
|
1771
|
+
/** Reject adjacent identical words ("the the"). Default true. */
|
|
1772
|
+
readonly rejectRepeatedAdjacent?: boolean;
|
|
1773
|
+
}
|
|
1774
|
+
/** Result of a check. Discriminated union — compile-time exhaustive. */
|
|
1775
|
+
type PassphraseValidationResult = {
|
|
1776
|
+
readonly ok: true;
|
|
1777
|
+
readonly words: number;
|
|
1778
|
+
} | {
|
|
1779
|
+
readonly ok: false;
|
|
1780
|
+
readonly reason: WeakPassphraseReason;
|
|
1781
|
+
readonly minimum?: number;
|
|
1782
|
+
readonly got?: number;
|
|
1783
|
+
};
|
|
1784
|
+
/**
|
|
1785
|
+
* Thrown by `assertStrongPassphrase()` and by every hub ingress
|
|
1786
|
+
* point (`createOwnerKeyring`, `grant`, `rotatePassphrase`) when a
|
|
1787
|
+
* supplied phrase fails the structural rules above.
|
|
1788
|
+
*/
|
|
1789
|
+
declare class WeakPassphraseError extends NoydbError {
|
|
1790
|
+
readonly reason: WeakPassphraseReason;
|
|
1791
|
+
readonly suggestion: string;
|
|
1792
|
+
constructor(reason: WeakPassphraseReason, suggestion: string);
|
|
1793
|
+
}
|
|
1794
|
+
/**
|
|
1795
|
+
* Inspect a phrase against the format rules and return a structured
|
|
1796
|
+
* verdict. Never throws — callers either branch on `ok` or pass the
|
|
1797
|
+
* result to {@link assertStrongPassphrase} for the throwing flavour.
|
|
1798
|
+
*/
|
|
1799
|
+
declare function validatePassphrase(s: string, opts?: PassphrasePolicy): PassphraseValidationResult;
|
|
1800
|
+
/**
|
|
1801
|
+
* Throw {@link WeakPassphraseError} when the phrase fails. Used by
|
|
1802
|
+
* `createOwnerKeyring`, `grant`, and `rotatePassphrase` at ingress.
|
|
1803
|
+
*
|
|
1804
|
+
* Pass `{ allowWeakPassphrase: true }` to bypass — intended for test
|
|
1805
|
+
* fixtures, CLI scripts, and dev environments. The override never
|
|
1806
|
+
* loosens the cryptographic key derivation; it only relaxes the
|
|
1807
|
+
* structural-strength gate.
|
|
1808
|
+
*/
|
|
1809
|
+
declare function assertStrongPassphrase(s: string, opts?: PassphrasePolicy & {
|
|
1810
|
+
allowWeakPassphrase?: boolean;
|
|
1811
|
+
}): void;
|
|
1812
|
+
/**
|
|
1813
|
+
* Estimate the entropy of a phrase, given the EFF 7,776-word list as
|
|
1814
|
+
* the assumed wordlist. ~12.9 bits per word.
|
|
1815
|
+
*
|
|
1816
|
+
* Returns 0 for any input that fails the phrase format — character-class
|
|
1817
|
+
* estimates aren't comparable to phrase entropy, and surfacing 0 makes
|
|
1818
|
+
* weak inputs visible in any UI that displays an entropy meter.
|
|
1819
|
+
*/
|
|
1820
|
+
declare function estimateEntropy(passphrase: string): number;
|
|
1821
|
+
|
|
1822
|
+
/**
|
|
1823
|
+
* Type surface for the per-principal user envelope subsystem.
|
|
1824
|
+
*
|
|
1825
|
+
* @see docs/superpowers/specs/2026-05-05-user-envelope-design.md
|
|
1826
|
+
*
|
|
1827
|
+
* @module
|
|
1828
|
+
*/
|
|
1829
|
+
|
|
1830
|
+
/**
|
|
1831
|
+
* Thin reader view of a user envelope. The on-disk shape is the standard
|
|
1832
|
+
* {@link import('../../types.js').EncryptedEnvelope}; this is what callers
|
|
1833
|
+
* see after the storage layer has decrypted the payload.
|
|
1834
|
+
*
|
|
1835
|
+
* Hub commits to the `keyringId` ⇔ `userId` identity and the `_v` / `_ts`
|
|
1836
|
+
* envelope metadata. The `data` payload is fully app-defined — hub does
|
|
1837
|
+
* not introspect, validate, or reserve any keys inside it.
|
|
1838
|
+
*/
|
|
1839
|
+
interface UserEnvelope<T> {
|
|
1840
|
+
/** The principal id this envelope belongs to. Equals the keyring `user_id`. */
|
|
1841
|
+
readonly keyringId: string;
|
|
1842
|
+
/** App-owned payload. Opaque to hub. */
|
|
1843
|
+
readonly data: T;
|
|
1844
|
+
/** Optimistic-concurrency version. Increments on every write. */
|
|
1845
|
+
readonly _v: number;
|
|
1846
|
+
/** ISO timestamp of the last write. */
|
|
1847
|
+
readonly _ts: string;
|
|
1848
|
+
}
|
|
1849
|
+
/**
|
|
1850
|
+
* Soft cap on the JSON-serialized payload size. Generous (a typical
|
|
1851
|
+
* profile + preferences + small app annex is ~1 KiB); rejects accidental
|
|
1852
|
+
* "stuff app state in here" anti-patterns.
|
|
1853
|
+
*/
|
|
1854
|
+
declare const USER_ENVELOPE_MAX_BYTES: number;
|
|
1855
|
+
/**
|
|
1856
|
+
* Reserved store collection name for user envelopes. Starts with `_` so the
|
|
1857
|
+
* keyring grant machinery propagates the DEK to every granted user via the
|
|
1858
|
+
* existing system-collection DEK propagation path in `team/keyring.ts`.
|
|
1859
|
+
*/
|
|
1860
|
+
declare const USER_ENVELOPE_COLLECTION = "_users";
|
|
1861
|
+
/**
|
|
1862
|
+
* Thrown when a user-envelope payload exceeds {@link USER_ENVELOPE_MAX_BYTES}
|
|
1863
|
+
* after JSON-serialization. The error carries the actual size so callers
|
|
1864
|
+
* can decide whether to trim or split.
|
|
1865
|
+
*/
|
|
1866
|
+
declare class UserEnvelopeOversizedError extends NoydbError {
|
|
1867
|
+
readonly bytes: number;
|
|
1868
|
+
readonly limit: number;
|
|
1869
|
+
constructor(bytes: number, limit?: number);
|
|
1870
|
+
}
|
|
1871
|
+
|
|
1742
1872
|
/** In-memory representation of an unlocked keyring. */
|
|
1743
1873
|
interface UnlockedKeyring {
|
|
1744
1874
|
readonly userId: string;
|
|
@@ -1761,6 +1891,20 @@ interface UnlockedKeyring {
|
|
|
1761
1891
|
* bundle import granted, regardless of role).
|
|
1762
1892
|
*/
|
|
1763
1893
|
readonly importCapability?: ImportCapability;
|
|
1894
|
+
/**
|
|
1895
|
+
* Tier-2 authenticator slots — readonly snapshot loaded from the
|
|
1896
|
+
* keyring file. Mutations go through `enrollAuthenticator` /
|
|
1897
|
+
* `removeAuthenticator` (issue #11), which write back via
|
|
1898
|
+
* `persistKeyring`. Always defined; loads with an empty array for
|
|
1899
|
+
* keyrings written before the multi-slot extension landed.
|
|
1900
|
+
*/
|
|
1901
|
+
readonly authenticators: readonly KeyringAuthenticator[];
|
|
1902
|
+
/**
|
|
1903
|
+
* Reserved per-keyring policy override (forward-compat for Option C
|
|
1904
|
+
* — see {@link VaultPolicyOnDisk}). v1.0 round-trips this field but
|
|
1905
|
+
* never enforces it; the gate engine uses `_meta/policy` only.
|
|
1906
|
+
*/
|
|
1907
|
+
readonly policy?: VaultPolicyOnDisk;
|
|
1764
1908
|
}
|
|
1765
1909
|
/**
|
|
1766
1910
|
* Recipient slot in a re-keyed `.noydb` bundle. Each slot becomes its
|
|
@@ -1817,6 +1961,29 @@ interface BundleRecipient {
|
|
|
1817
1961
|
* @internal
|
|
1818
1962
|
*/
|
|
1819
1963
|
declare function buildRecipientKeyringFile(callerKeyring: UnlockedKeyring, recipient: BundleRecipient): Promise<KeyringFile>;
|
|
1964
|
+
/** List all users with access to a vault. */
|
|
1965
|
+
declare function listUsers(adapter: NoydbStore, vault: string): Promise<UserInfo[]>;
|
|
1966
|
+
/**
|
|
1967
|
+
* Joined enumeration: every keyring + its `_users/<keyringId>`
|
|
1968
|
+
* envelope side by side. Convenience for admin UIs that want to
|
|
1969
|
+
* render team-member lists with profile data ("Bob — operator —
|
|
1970
|
+
* 'Bob the Auditor' avatar X locale fr-FR") in a single pass.
|
|
1971
|
+
*
|
|
1972
|
+
* `userEnvelopeDek` is the vault's `_users` collection DEK
|
|
1973
|
+
* (`vault.getDEK('_users')`); used to decrypt every envelope.
|
|
1974
|
+
*
|
|
1975
|
+
* Principals without a persisted envelope (legacy keyrings predating
|
|
1976
|
+
* the user-envelope feature) come back with `envelope: null`. The
|
|
1977
|
+
* caller chooses how to render — usually "fall back to keyring's
|
|
1978
|
+
* `displayName`".
|
|
1979
|
+
*
|
|
1980
|
+
* Order matches `listUsers()` (store-defined; sort if you need a
|
|
1981
|
+
* stable display order).
|
|
1982
|
+
*/
|
|
1983
|
+
declare function listUsersWithEnvelopes<T = unknown>(adapter: NoydbStore, vault: string, userEnvelopeDek: CryptoKey): Promise<Array<{
|
|
1984
|
+
user: UserInfo;
|
|
1985
|
+
envelope: UserEnvelope<T> | null;
|
|
1986
|
+
}>>;
|
|
1820
1987
|
/**
|
|
1821
1988
|
* Check whether a keyring is authorised for a given `@noy-db/as-*`
|
|
1822
1989
|
* export tier.
|
|
@@ -2715,6 +2882,347 @@ declare class SyncEngine {
|
|
|
2715
2882
|
private persistMeta;
|
|
2716
2883
|
}
|
|
2717
2884
|
|
|
2885
|
+
/**
|
|
2886
|
+
* Tier-1 change flows — `rotatePassphrase` (user remembers old) and
|
|
2887
|
+
* `recoverPassphrase` (user supplies a recovery proof). Issue #10.
|
|
2888
|
+
*
|
|
2889
|
+
* The two flows share the post-verification half — fresh salt, fresh
|
|
2890
|
+
* KEK, rewrap every DEK — and differ only in how they re-derive the
|
|
2891
|
+
* old KEK:
|
|
2892
|
+
*
|
|
2893
|
+
* - **Rotate**: derive from the supplied `oldPassphrase`.
|
|
2894
|
+
* - **Recover (paper)**: unwrap from a `RecoveryCodeEntry` using a
|
|
2895
|
+
* user-supplied recovery code. The entry is burned on success.
|
|
2896
|
+
*
|
|
2897
|
+
* The non-paper recovery profiles (Shamir, multi-channel,
|
|
2898
|
+
* admin-mediated) are not yet wired — calling them throws
|
|
2899
|
+
* {@link RecoveryProfileNotImplementedError} with a tracking link.
|
|
2900
|
+
*
|
|
2901
|
+
* @module
|
|
2902
|
+
*/
|
|
2903
|
+
|
|
2904
|
+
/** Caller payload for {@link rotatePassphrase}. */
|
|
2905
|
+
interface RotatePassphraseInput {
|
|
2906
|
+
readonly oldPassphrase: string;
|
|
2907
|
+
readonly newPassphrase: string;
|
|
2908
|
+
readonly passphrasePolicy?: PassphrasePolicy;
|
|
2909
|
+
readonly allowWeakPassphrase?: boolean;
|
|
2910
|
+
}
|
|
2911
|
+
/**
|
|
2912
|
+
* Re-derive the user's KEK from `oldPassphrase`, rewrap every DEK
|
|
2913
|
+
* under a freshly-derived KEK from `newPassphrase`, and persist.
|
|
2914
|
+
*
|
|
2915
|
+
* Tier-2 authenticator slots are NOT preserved — each slot wraps the
|
|
2916
|
+
* old KEK and would need the user's per-slot derivation key to
|
|
2917
|
+
* re-wrap; the hub doesn't hold that. The user re-enrols any slots
|
|
2918
|
+
* after rotation. v0.1.0-pre.5 limitation.
|
|
2919
|
+
*
|
|
2920
|
+
* @throws `InvalidKeyError` if `oldPassphrase` does not unwrap the keyring.
|
|
2921
|
+
* @throws `WeakPassphraseError` if `newPassphrase` fails the strength rule.
|
|
2922
|
+
*/
|
|
2923
|
+
declare function rotatePassphrase(store: NoydbStore, vault: string, userId: string, input: RotatePassphraseInput): Promise<UnlockedKeyring>;
|
|
2924
|
+
/** Caller payload for {@link recoverPassphrase}. */
|
|
2925
|
+
type RecoveryProof = {
|
|
2926
|
+
readonly profile: 'paper';
|
|
2927
|
+
readonly payload: {
|
|
2928
|
+
readonly code: string;
|
|
2929
|
+
};
|
|
2930
|
+
} | {
|
|
2931
|
+
readonly profile: 'shamir';
|
|
2932
|
+
readonly payload: {
|
|
2933
|
+
readonly shares: ReadonlyArray<string>;
|
|
2934
|
+
};
|
|
2935
|
+
} | {
|
|
2936
|
+
readonly profile: 'multi-channel';
|
|
2937
|
+
readonly payload: {
|
|
2938
|
+
readonly proofs: ReadonlyArray<unknown>;
|
|
2939
|
+
};
|
|
2940
|
+
} | {
|
|
2941
|
+
readonly profile: 'admin-mediated';
|
|
2942
|
+
readonly payload: {
|
|
2943
|
+
readonly token: string;
|
|
2944
|
+
readonly factor?: unknown;
|
|
2945
|
+
};
|
|
2946
|
+
};
|
|
2947
|
+
interface RecoverPassphraseInput {
|
|
2948
|
+
readonly newPassphrase: string;
|
|
2949
|
+
readonly recoveryProof: RecoveryProof;
|
|
2950
|
+
readonly passphrasePolicy?: PassphrasePolicy;
|
|
2951
|
+
readonly allowWeakPassphrase?: boolean;
|
|
2952
|
+
}
|
|
2953
|
+
/**
|
|
2954
|
+
* Reset the user's passphrase using a recovery proof. v0.1.0-pre.5
|
|
2955
|
+
* supports the `'paper'` profile via `@noy-db/on-recovery` entries
|
|
2956
|
+
* persisted in `_meta/recovery-paper`. The other three profiles throw
|
|
2957
|
+
* {@link RecoveryProfileNotImplementedError}.
|
|
2958
|
+
*
|
|
2959
|
+
* On success, the used recovery entry is burned (deleted from the
|
|
2960
|
+
* stored set).
|
|
2961
|
+
*/
|
|
2962
|
+
declare function recoverPassphrase(store: NoydbStore, vault: string, userId: string, input: RecoverPassphraseInput): Promise<UnlockedKeyring>;
|
|
2963
|
+
|
|
2964
|
+
/**
|
|
2965
|
+
* Recovery profile persistence + dispatch — issue #10.
|
|
2966
|
+
*
|
|
2967
|
+
* v0.1.0-pre.5 wires the **paper** profile end-to-end through
|
|
2968
|
+
* `@noy-db/on-recovery`. The other three profiles (Shamir,
|
|
2969
|
+
* multi-channel, admin-mediated) ship the API surface and throw
|
|
2970
|
+
* {@link RecoveryProfileNotImplementedError} during use; per-profile
|
|
2971
|
+
* dispatch lands in follow-up issues.
|
|
2972
|
+
*
|
|
2973
|
+
* Storage layout:
|
|
2974
|
+
*
|
|
2975
|
+
* ```
|
|
2976
|
+
* _meta/recovery-paper — JSON { entries: RecoveryCodeEntry[] } produced by `on-recovery`.
|
|
2977
|
+
* _meta/recovery-shamir — reserved
|
|
2978
|
+
* _meta/recovery-multi — reserved
|
|
2979
|
+
* _meta/recovery-admin — reserved
|
|
2980
|
+
* ```
|
|
2981
|
+
*
|
|
2982
|
+
* Like `_meta/policy` and `_meta/handle`, the documents are plain JSON
|
|
2983
|
+
* with empty `_iv` — the recovery-code wrapping is what protects the
|
|
2984
|
+
* KEK; the entries themselves are inert without the user's code.
|
|
2985
|
+
*
|
|
2986
|
+
* @module
|
|
2987
|
+
*/
|
|
2988
|
+
|
|
2989
|
+
/**
|
|
2990
|
+
* One paper recovery code as persisted in `_meta/recovery-paper`.
|
|
2991
|
+
*
|
|
2992
|
+
* The hub's KEK is intentionally non-extractable (see `crypto.ts`),
|
|
2993
|
+
* so the recovery entry can't AES-KW-wrap the KEK directly. Instead
|
|
2994
|
+
* we wrap a serialized DEK set: the entry holds the AES-GCM
|
|
2995
|
+
* ciphertext of `{ deks: { collection: rawDekBase64 } }`. Recovery
|
|
2996
|
+
* deserializes the DEK set, then mints a fresh KEK from the new
|
|
2997
|
+
* passphrase and rewraps the DEKs under it.
|
|
2998
|
+
*
|
|
2999
|
+
* This is the same pattern `@noy-db/on-pin` uses for tier-3 quick
|
|
3000
|
+
* resume — the cryptographic guarantee is identical (AES-GCM with a
|
|
3001
|
+
* PBKDF2-derived key), and it sidesteps the non-extractable-KEK
|
|
3002
|
+
* constraint cleanly.
|
|
3003
|
+
*/
|
|
3004
|
+
interface PaperRecoveryEntry {
|
|
3005
|
+
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
|
+
readonly enrolledAt: string;
|
|
3013
|
+
}
|
|
3014
|
+
interface PaperRecoveryDoc {
|
|
3015
|
+
readonly _noydb_recovery: 1;
|
|
3016
|
+
readonly profile: 'paper';
|
|
3017
|
+
readonly entries: ReadonlyArray<PaperRecoveryEntry>;
|
|
3018
|
+
}
|
|
3019
|
+
/** Read the paper-recovery entries. Returns empty array when absent. */
|
|
3020
|
+
declare function loadPaperRecoveryEntries(store: NoydbStore, vault: string): Promise<ReadonlyArray<PaperRecoveryEntry>>;
|
|
3021
|
+
/** Replace the paper-recovery entries (used after burn-on-recovery). */
|
|
3022
|
+
declare function savePaperRecoveryEntries(store: NoydbStore, vault: string, entries: ReadonlyArray<PaperRecoveryEntry>): Promise<void>;
|
|
3023
|
+
/** Drop a single paper-recovery entry (burn-on-use). */
|
|
3024
|
+
declare function burnPaperRecoveryEntry(store: NoydbStore, vault: string, codeId: string): Promise<void>;
|
|
3025
|
+
/** Whether at least one recovery profile has any enrolled entries. */
|
|
3026
|
+
declare function hasRecoveryEnrolled(store: NoydbStore, vault: string): Promise<boolean>;
|
|
3027
|
+
|
|
3028
|
+
/**
|
|
3029
|
+
* Public envelope — owner-curated plaintext metadata, readable
|
|
3030
|
+
* before vault unlock or bundle decryption.
|
|
3031
|
+
*
|
|
3032
|
+
* @see docs/subsystems/public-envelope.md
|
|
3033
|
+
*
|
|
3034
|
+
* @module
|
|
3035
|
+
*/
|
|
3036
|
+
/**
|
|
3037
|
+
* Either a single string (used when the developer's app is
|
|
3038
|
+
* single-locale) or a locale → string map for i18n. Mirrors the
|
|
3039
|
+
* shape `@noy-db/hub/i18n` already uses for record fields, so the
|
|
3040
|
+
* existing `resolveI18nText` resolver applies.
|
|
3041
|
+
*/
|
|
3042
|
+
type PublicEnvelopeText = string | Record<string, string>;
|
|
3043
|
+
/**
|
|
3044
|
+
* Persisted shape — both `_meta/public-envelope` and the bundle
|
|
3045
|
+
* header carry this. The version number is monotonic per vault and
|
|
3046
|
+
* helps cache invalidators detect change without hashing the JSON.
|
|
3047
|
+
*/
|
|
3048
|
+
interface PublicEnvelope {
|
|
3049
|
+
readonly _noydb_public: 1;
|
|
3050
|
+
readonly version: number;
|
|
3051
|
+
readonly name?: PublicEnvelopeText;
|
|
3052
|
+
readonly description?: PublicEnvelopeText;
|
|
3053
|
+
/** Inline `data:` URL (`data:image/png;base64,…` or `data:image/svg+xml;base64,…`). */
|
|
3054
|
+
readonly icon?: string;
|
|
3055
|
+
/** ISO-8601 timestamp; auto-set on first envelope write, immutable thereafter. */
|
|
3056
|
+
readonly createdAt?: string;
|
|
3057
|
+
/** ISO-8601 timestamp; auto-updated on every `setPublicEnvelope` call. */
|
|
3058
|
+
readonly updatedAt?: string;
|
|
3059
|
+
/** BCP-47 fallback locale for renderers when the user's locale isn't covered. */
|
|
3060
|
+
readonly defaultLocale?: string;
|
|
3061
|
+
}
|
|
3062
|
+
/** Field names the developer can allow in `PublicEnvelopeSchema.fields`. */
|
|
3063
|
+
declare const PUBLIC_ENVELOPE_FIELDS: readonly ["name", "description", "icon", "createdAt", "updatedAt", "defaultLocale"];
|
|
3064
|
+
type PublicEnvelopeField = (typeof PUBLIC_ENVELOPE_FIELDS)[number];
|
|
3065
|
+
/**
|
|
3066
|
+
* Build-time schema. The developer enables the feature and bounds
|
|
3067
|
+
* what the owner can set. `true` is shorthand for "all defaults" —
|
|
3068
|
+
* gives the owner the full field set with the standard caps.
|
|
3069
|
+
*/
|
|
3070
|
+
interface PublicEnvelopeSchema {
|
|
3071
|
+
/**
|
|
3072
|
+
* Allowed field names. Setting `name`/`description`/`icon`/`defaultLocale` is
|
|
3073
|
+
* gated on the field being listed here. `createdAt` / `updatedAt` are managed
|
|
3074
|
+
* by the hub; including them is a no-op (the owner cannot set them
|
|
3075
|
+
* directly). Default: every field above.
|
|
3076
|
+
*/
|
|
3077
|
+
readonly fields?: ReadonlyArray<PublicEnvelopeField>;
|
|
3078
|
+
/**
|
|
3079
|
+
* Maximum icon size — measured as the length of the data-URL
|
|
3080
|
+
* string. Default 256 KB.
|
|
3081
|
+
*/
|
|
3082
|
+
readonly maxIconBytes?: number;
|
|
3083
|
+
/** Allowed icon MIME types. Default ['image/png', 'image/svg+xml']. */
|
|
3084
|
+
readonly iconMimeTypes?: ReadonlyArray<string>;
|
|
3085
|
+
/** Maximum length of `name` / `description` per locale. Default 200. */
|
|
3086
|
+
readonly maxStringChars?: number;
|
|
3087
|
+
}
|
|
3088
|
+
/** Default schema values; merged onto every developer-supplied schema. */
|
|
3089
|
+
declare const DEFAULT_PUBLIC_ENVELOPE_SCHEMA: {
|
|
3090
|
+
readonly fields: readonly ["name", "description", "icon", "createdAt", "updatedAt", "defaultLocale"];
|
|
3091
|
+
readonly maxIconBytes: number;
|
|
3092
|
+
readonly iconMimeTypes: readonly ["image/png", "image/svg+xml"];
|
|
3093
|
+
readonly maxStringChars: 200;
|
|
3094
|
+
};
|
|
3095
|
+
/** Resolved schema after merging developer override onto defaults. */
|
|
3096
|
+
interface ResolvedPublicEnvelopeSchema {
|
|
3097
|
+
readonly fields: ReadonlyArray<PublicEnvelopeField>;
|
|
3098
|
+
readonly maxIconBytes: number;
|
|
3099
|
+
readonly iconMimeTypes: ReadonlyArray<string>;
|
|
3100
|
+
readonly maxStringChars: number;
|
|
3101
|
+
}
|
|
3102
|
+
/**
|
|
3103
|
+
* Merge developer schema onto the defaults. The shorthand `true`
|
|
3104
|
+
* resolves to the full default schema; an explicit object only
|
|
3105
|
+
* overrides the keys it provides.
|
|
3106
|
+
*/
|
|
3107
|
+
declare function resolveSchema(schema: true | PublicEnvelopeSchema | undefined): ResolvedPublicEnvelopeSchema | undefined;
|
|
3108
|
+
|
|
3109
|
+
/** Owner-supplied input — the subset of {@link PublicEnvelope} the owner can set. */
|
|
3110
|
+
interface SetPublicEnvelopeInput {
|
|
3111
|
+
readonly name?: PublicEnvelopeText;
|
|
3112
|
+
readonly description?: PublicEnvelopeText;
|
|
3113
|
+
readonly icon?: string;
|
|
3114
|
+
readonly defaultLocale?: string;
|
|
3115
|
+
}
|
|
3116
|
+
/**
|
|
3117
|
+
* Validate an owner-supplied envelope input against the developer's
|
|
3118
|
+
* resolved schema. Throws `ValidationError` on the first violation;
|
|
3119
|
+
* returns void on success.
|
|
3120
|
+
*
|
|
3121
|
+
* The validator is deliberately strict: every fail mode is a hard
|
|
3122
|
+
* error rather than a silent drop, so the owner finds out immediately
|
|
3123
|
+
* which field they oversized rather than discovering a truncated
|
|
3124
|
+
* label months later.
|
|
3125
|
+
*/
|
|
3126
|
+
declare function validatePublicEnvelopeInput(input: SetPublicEnvelopeInput, schema: ResolvedPublicEnvelopeSchema): void;
|
|
3127
|
+
/**
|
|
3128
|
+
* Lightweight runtime predicate — used by the bundle header
|
|
3129
|
+
* validator to recognise a public envelope without requiring it.
|
|
3130
|
+
*/
|
|
3131
|
+
declare function isPublicEnvelope(x: unknown): x is PublicEnvelope;
|
|
3132
|
+
|
|
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
|
+
/**
|
|
3179
|
+
* Per-vault tier-3 (PIN / quick-resume) state — issue #11.
|
|
3180
|
+
*
|
|
3181
|
+
* The hub holds a `PinResumeState`-shaped record in memory, keyed by
|
|
3182
|
+
* vault. `enrollUnlock` populates it; `unlockViaPin` consumes it via
|
|
3183
|
+
* `@noy-db/on-pin`'s `resumePin`. The cached state is wiped when the
|
|
3184
|
+
* idle timer fires or `db.close()` is called.
|
|
3185
|
+
*
|
|
3186
|
+
* Importantly, this module does NOT depend on `@noy-db/on-pin` — the
|
|
3187
|
+
* caller passes the already-built state in. That keeps the hub's
|
|
3188
|
+
* `peerDependencies` empty for tier-3 and lets developers swap the
|
|
3189
|
+
* primitive (e.g. an OS biometric in place of a PIN).
|
|
3190
|
+
*
|
|
3191
|
+
* @module
|
|
3192
|
+
*/
|
|
3193
|
+
/**
|
|
3194
|
+
* Opaque `PinResumeState`-compatible record. Mirrored from
|
|
3195
|
+
* `@noy-db/on-pin/PinResumeState`. The hub treats the contents as
|
|
3196
|
+
* a black box.
|
|
3197
|
+
*/
|
|
3198
|
+
interface QuickUnlockState {
|
|
3199
|
+
readonly _noydb_on_pin: 1;
|
|
3200
|
+
readonly salt: string;
|
|
3201
|
+
readonly iv: string;
|
|
3202
|
+
readonly wrappedKeyring: string;
|
|
3203
|
+
readonly expiresAt: string;
|
|
3204
|
+
readonly maxAttempts: number;
|
|
3205
|
+
attempts: number;
|
|
3206
|
+
}
|
|
3207
|
+
/** In-memory store for tier-3 unlock state, keyed by vault. */
|
|
3208
|
+
declare class QuickUnlockStore {
|
|
3209
|
+
private readonly states;
|
|
3210
|
+
private readonly timers;
|
|
3211
|
+
/**
|
|
3212
|
+
* Register a quick-unlock state for a vault. Replaces any existing
|
|
3213
|
+
* state. Schedules an automatic clear when the state's `expiresAt`
|
|
3214
|
+
* elapses.
|
|
3215
|
+
*/
|
|
3216
|
+
set(vault: string, state: QuickUnlockState): void;
|
|
3217
|
+
/** Read the state for a vault. Returns undefined when none is registered. */
|
|
3218
|
+
get(vault: string): QuickUnlockState | undefined;
|
|
3219
|
+
/** Drop the state for a vault. Cancels the auto-clear timer. */
|
|
3220
|
+
delete(vault: string): void;
|
|
3221
|
+
/** Drop every cached state. Called on `db.close()`. */
|
|
3222
|
+
clear(): void;
|
|
3223
|
+
private clearTimer;
|
|
3224
|
+
}
|
|
3225
|
+
|
|
2718
3226
|
/**
|
|
2719
3227
|
* Multi-record atomic transactions.
|
|
2720
3228
|
*
|
|
@@ -2853,6 +3361,88 @@ declare class TxCollection<T> {
|
|
|
2853
3361
|
*/
|
|
2854
3362
|
declare function runTransaction<T>(db: Noydb, fn: (tx: TxContext) => Promise<T> | T): Promise<T>;
|
|
2855
3363
|
|
|
3364
|
+
/**
|
|
3365
|
+
* Policy gate DSL types — issue #9.
|
|
3366
|
+
*
|
|
3367
|
+
* Sensitive operations (rotate the passphrase, enroll an authenticator,
|
|
3368
|
+
* export plaintext, grant a user, …) are gated by a typed policy
|
|
3369
|
+
* object. The developer supplies a {@link VaultPolicy} at vault
|
|
3370
|
+
* creation; the hub merges it onto a built-in preset and persists the
|
|
3371
|
+
* merged document at `_meta/policy`.
|
|
3372
|
+
*
|
|
3373
|
+
* @see docs/subsystems/session-tiers.md → Policy gates DSL
|
|
3374
|
+
*
|
|
3375
|
+
* @module
|
|
3376
|
+
*/
|
|
3377
|
+
|
|
3378
|
+
/** A single off-device factor surface — the proof an actor presents at gate time. */
|
|
3379
|
+
type FactorKind = 'totp' | 'email-otp' | 'recovery' | 'shamir' | 'webauthn-roaming';
|
|
3380
|
+
/**
|
|
3381
|
+
* One factor requirement entry. The default is "any one of the listed
|
|
3382
|
+
* factors, fresh within the last 5 minutes". Bumping `count` requires N
|
|
3383
|
+
* distinct fresh proofs; bumping `freshnessMs` widens the acceptance
|
|
3384
|
+
* window.
|
|
3385
|
+
*/
|
|
3386
|
+
interface FactorRequirement {
|
|
3387
|
+
readonly anyOf: ReadonlyArray<FactorKind>;
|
|
3388
|
+
/** Number of distinct factors required. Default 1. */
|
|
3389
|
+
readonly count?: number;
|
|
3390
|
+
/** How recent each proof must be. Default 5 minutes. */
|
|
3391
|
+
readonly freshnessMs?: number;
|
|
3392
|
+
}
|
|
3393
|
+
/** Soft signals layered on top of the gate verdict — never block on their own. */
|
|
3394
|
+
interface WarningRules {
|
|
3395
|
+
/** Behavior on shared-device tier-1 ops. `'block'` raises a `PolicyDeniedError`. */
|
|
3396
|
+
readonly sharedDevice?: 'warn' | 'block';
|
|
3397
|
+
/** Behavior on weak tier-2 (e.g. password-only) for sensitive ops. */
|
|
3398
|
+
readonly weakAuthenticator?: 'warn' | 'block';
|
|
3399
|
+
}
|
|
3400
|
+
/**
|
|
3401
|
+
* Policy applied to one named gate. `enabled: false` disables the
|
|
3402
|
+
* action entirely (useful in managed-passphrase mode where rotation is
|
|
3403
|
+
* impossible by construction).
|
|
3404
|
+
*/
|
|
3405
|
+
interface GatePolicy {
|
|
3406
|
+
/** Minimum tier the active session must hold. */
|
|
3407
|
+
readonly minTier: 1 | 2 | 3;
|
|
3408
|
+
/** Extra freshness-bound proofs required at gate time. */
|
|
3409
|
+
readonly factors?: ReadonlyArray<FactorRequirement>;
|
|
3410
|
+
readonly warn?: WarningRules;
|
|
3411
|
+
readonly enabled?: boolean;
|
|
3412
|
+
}
|
|
3413
|
+
/**
|
|
3414
|
+
* Built-in gate names. App-defined gates live in the `app:*` namespace
|
|
3415
|
+
* and use the same engine; the engine treats unknown names with no
|
|
3416
|
+
* configured policy as "no gate" (no-op).
|
|
3417
|
+
*/
|
|
3418
|
+
type BuiltInGateName = 'rotate-passphrase' | 'recover-passphrase' | 'enroll-authenticator' | 'remove-authenticator' | 'rotate-unlock' | 'enroll-user' | 'revoke-user' | 'export-bundle' | 'export-plaintext' | 'view-user-auth'
|
|
3419
|
+
/** Authorize a write to one's own user envelope (#22). */
|
|
3420
|
+
| 'edit-own-profile'
|
|
3421
|
+
/** Authorize reading other principals' user envelopes (#22). */
|
|
3422
|
+
| 'view-team-profiles';
|
|
3423
|
+
/** Either a built-in gate name or an `app:*` custom gate. */
|
|
3424
|
+
type GateName = BuiltInGateName | `app:${string}`;
|
|
3425
|
+
/**
|
|
3426
|
+
* Top-level policy object. Persisted at `_meta/policy` once at vault
|
|
3427
|
+
* creation. The `passphrase` block configures the strength rules
|
|
3428
|
+
* applied at every passphrase ingress (issue #7); `gates` configures
|
|
3429
|
+
* the action-level requirements.
|
|
3430
|
+
*/
|
|
3431
|
+
interface VaultPolicy {
|
|
3432
|
+
readonly passphrase?: PassphrasePolicy;
|
|
3433
|
+
readonly gates: Partial<Record<GateName, GatePolicy>>;
|
|
3434
|
+
}
|
|
3435
|
+
/** Concrete proof an actor presents to {@link checkGate}. */
|
|
3436
|
+
interface FactorProof {
|
|
3437
|
+
readonly kind: FactorKind;
|
|
3438
|
+
/** ISO-8601 timestamp the proof was minted at. Compared against `freshnessMs`. */
|
|
3439
|
+
readonly mintedAt?: string;
|
|
3440
|
+
/** Method-specific payload. The engine treats it as opaque — verification is delegated. */
|
|
3441
|
+
readonly payload?: unknown;
|
|
3442
|
+
}
|
|
3443
|
+
/** Active session tier — what the engine compares against `gate.minTier`. */
|
|
3444
|
+
type ActiveTier = 1 | 2 | 3;
|
|
3445
|
+
|
|
2856
3446
|
/** The top-level NOYDB instance. */
|
|
2857
3447
|
declare class Noydb {
|
|
2858
3448
|
private readonly options;
|
|
@@ -2860,6 +3450,25 @@ declare class Noydb {
|
|
|
2860
3450
|
private readonly vaultCache;
|
|
2861
3451
|
private readonly keyringCache;
|
|
2862
3452
|
private readonly syncEngines;
|
|
3453
|
+
/**
|
|
3454
|
+
* Per-vault active session tier — defaults to `1` after a passphrase
|
|
3455
|
+
* unlock; tier-2 / tier-3 unlocks (issue #11) downgrade it. Used by
|
|
3456
|
+
* {@link checkGate} to evaluate `gate.minTier`.
|
|
3457
|
+
*/
|
|
3458
|
+
private readonly activeTier;
|
|
3459
|
+
/**
|
|
3460
|
+
* Per-vault loaded policy. Cached after the first
|
|
3461
|
+
* `_meta/policy` load; replaced by `db.updatePolicy()`.
|
|
3462
|
+
*/
|
|
3463
|
+
private readonly policyCache;
|
|
3464
|
+
/** Per-vault tier-3 (PIN / quick-resume) state — issue #11. */
|
|
3465
|
+
private readonly quickUnlock;
|
|
3466
|
+
/**
|
|
3467
|
+
* Resolved public-envelope schema. Lazily computed once from
|
|
3468
|
+
* `NoydbOptions.publicEnvelope`; `undefined` when the developer
|
|
3469
|
+
* didn't opt in.
|
|
3470
|
+
*/
|
|
3471
|
+
private readonly publicEnvelopeSchema;
|
|
2863
3472
|
private closed;
|
|
2864
3473
|
private sessionTimer;
|
|
2865
3474
|
/** Per-vault policy enforcers. */
|
|
@@ -3095,6 +3704,28 @@ declare class Noydb {
|
|
|
3095
3704
|
private getSyncEngine;
|
|
3096
3705
|
on<K extends keyof NoydbEventMap>(event: K, handler: (data: NoydbEventMap[K]) => void): void;
|
|
3097
3706
|
off<K extends keyof NoydbEventMap>(event: K, handler: (data: NoydbEventMap[K]) => void): void;
|
|
3707
|
+
/**
|
|
3708
|
+
* Soft-lock a single vault: clear its in-memory keyring, DEKs, vault
|
|
3709
|
+
* instance, sync engine, policy enforcer, and active-tier entry —
|
|
3710
|
+
* WITHOUT destroying the `Noydb` instance.
|
|
3711
|
+
*
|
|
3712
|
+
* Designed for "lock screen" UX: the user taps **Lock** and DEKs are
|
|
3713
|
+
* scrubbed from memory immediately, but the same `Noydb` instance can
|
|
3714
|
+
* be re-unlocked via {@link unlockViaAuthenticator} (tier 2) or
|
|
3715
|
+
* {@link unlockViaPin} (tier 3) without re-running `createNoydb`.
|
|
3716
|
+
*
|
|
3717
|
+
* **QuickUnlock state is preserved.** That's the whole point — the
|
|
3718
|
+
* user can still resume via PIN without a full credential re-prompt.
|
|
3719
|
+
* The on-disk `_meta/policy` document is also kept in cache (it
|
|
3720
|
+
* survives lock; nothing about it changes when DEKs are scrubbed).
|
|
3721
|
+
*
|
|
3722
|
+
* No-op when `vault` is not currently in cache (idempotent).
|
|
3723
|
+
*
|
|
3724
|
+
* Unblocks vLannaAi/niwat#33.
|
|
3725
|
+
*
|
|
3726
|
+
* @see #17
|
|
3727
|
+
*/
|
|
3728
|
+
lockVault(vault: string): void;
|
|
3098
3729
|
close(): void;
|
|
3099
3730
|
/**
|
|
3100
3731
|
* Returns a snapshot of all translator invocations since the last
|
|
@@ -3113,6 +3744,278 @@ declare class Noydb {
|
|
|
3113
3744
|
* @internal — not part of the public API surface
|
|
3114
3745
|
*/
|
|
3115
3746
|
invokeTranslator(text: string, from: string, to: string, field: string, collection: string): Promise<string>;
|
|
3747
|
+
/**
|
|
3748
|
+
* Read the active policy for a vault. Loads from `_meta/policy` on
|
|
3749
|
+
* first call; subsequent calls hit the in-memory cache. Throws
|
|
3750
|
+
* `ValidationError` if the vault has not been opened.
|
|
3751
|
+
*/
|
|
3752
|
+
getPolicy(vault: string): Promise<VaultPolicy>;
|
|
3753
|
+
/**
|
|
3754
|
+
* Replace the policy document at `_meta/policy` and update the
|
|
3755
|
+
* in-memory cache. Gated by the `enroll-user` policy (a policy
|
|
3756
|
+
* change is fundamentally a privilege-management action).
|
|
3757
|
+
*/
|
|
3758
|
+
updatePolicy(vault: string, override: Partial<VaultPolicy>): Promise<VaultPolicy>;
|
|
3759
|
+
/**
|
|
3760
|
+
* Evaluate a policy gate against the active session tier and the
|
|
3761
|
+
* presented factor proofs. Throws {@link PolicyDeniedError} on
|
|
3762
|
+
* denial; resolves with `void` on success.
|
|
3763
|
+
*
|
|
3764
|
+
* @param vault The vault whose policy applies.
|
|
3765
|
+
* @param gate Gate name — built-in (e.g. `'rotate-passphrase'`)
|
|
3766
|
+
* or app-defined (`app:*`).
|
|
3767
|
+
* @param presented Caller-supplied factor proofs.
|
|
3768
|
+
*/
|
|
3769
|
+
checkGate(vault: string, gate: GateName, presented?: {
|
|
3770
|
+
factors?: ReadonlyArray<FactorProof>;
|
|
3771
|
+
sharedDevice?: boolean;
|
|
3772
|
+
}): Promise<void>;
|
|
3773
|
+
/** Read or persist the vault policy at `_meta/policy` on first open. */
|
|
3774
|
+
private bootstrapPolicy;
|
|
3775
|
+
/**
|
|
3776
|
+
* Throw {@link RecoveryNotEnrolledError} when the developer
|
|
3777
|
+
* explicitly opts into strict mandatory-recovery enforcement
|
|
3778
|
+
* (`createNoydb({ requireRecovery: true })`) and no recovery
|
|
3779
|
+
* entries are persisted.
|
|
3780
|
+
*
|
|
3781
|
+
* The default behavior is lenient — `recover-passphrase` is enabled
|
|
3782
|
+
* in `PERSONAL_POLICY` but the hub does not block vault open on
|
|
3783
|
+
* missing enrollment. v1.0 will flip the default to strict; for now,
|
|
3784
|
+
* apps that want the spec-mandated check turn it on per-vault.
|
|
3785
|
+
*/
|
|
3786
|
+
private assertRecoveryEnrolled;
|
|
3787
|
+
/**
|
|
3788
|
+
* Internal accessor used by tier-2/tier-3 unlock paths (issue #11)
|
|
3789
|
+
* to mark the active session tier.
|
|
3790
|
+
* @internal
|
|
3791
|
+
*/
|
|
3792
|
+
_setActiveTier(vault: string, tier: ActiveTier): void;
|
|
3793
|
+
/**
|
|
3794
|
+
* Add a tier-2 authenticator slot to the calling user's keyring.
|
|
3795
|
+
* Each slot independently wraps the SAME KEK under a method-specific
|
|
3796
|
+
* key — adding a slot is a constant-time keyring write.
|
|
3797
|
+
*
|
|
3798
|
+
* The wrapping ciphertext is produced by the corresponding
|
|
3799
|
+
* `@noy-db/on-*` package (e.g. `enrollPasswordAuthenticator` from
|
|
3800
|
+
* `@noy-db/on-password`); the hub persists the result.
|
|
3801
|
+
*
|
|
3802
|
+
* Gated by `enroll-authenticator`; `presented` carries any factor
|
|
3803
|
+
* proofs the active policy demands.
|
|
3804
|
+
*/
|
|
3805
|
+
enrollAuthenticator(vault: string, options: EnrollAuthenticatorOptions, presented?: {
|
|
3806
|
+
factors?: ReadonlyArray<FactorProof>;
|
|
3807
|
+
sharedDevice?: boolean;
|
|
3808
|
+
}): Promise<void>;
|
|
3809
|
+
/**
|
|
3810
|
+
* Remove a tier-2 authenticator slot. Idempotent — removing a
|
|
3811
|
+
* non-existent slot is a successful no-op. Gated by
|
|
3812
|
+
* `remove-authenticator`.
|
|
3813
|
+
*/
|
|
3814
|
+
removeAuthenticator(vault: string, slotId: string, presented?: {
|
|
3815
|
+
factors?: ReadonlyArray<FactorProof>;
|
|
3816
|
+
sharedDevice?: boolean;
|
|
3817
|
+
}): Promise<void>;
|
|
3818
|
+
/** Read the slot list for a vault. Internal — `describeAuthConfig` (#13) consumes this. */
|
|
3819
|
+
listAuthenticators(vault: string): Promise<ReadonlyArray<KeyringAuthenticator>>;
|
|
3820
|
+
/**
|
|
3821
|
+
* Native WebAuthn enrollment using the **real** internal keyring (#16).
|
|
3822
|
+
*
|
|
3823
|
+
* Why this exists: when a consumer is using `createNoydb({ secret })`,
|
|
3824
|
+
* they cannot reach the live `UnlockedKeyring` to feed it to
|
|
3825
|
+
* `enrollWebAuthn(keyring, vault, opts)` from `@noy-db/on-webauthn`.
|
|
3826
|
+
* Constructing a synthetic keyring (the previous workaround) produces
|
|
3827
|
+
* a slot whose `wrapped_kek` references the synthetic payload, not
|
|
3828
|
+
* the live session — so `unlockViaAuthenticator()` later replaces the
|
|
3829
|
+
* live DEK map with stale wrapped DEKs and every decrypt fails.
|
|
3830
|
+
*
|
|
3831
|
+
* This method runs `ceremony` with the REAL keyring (still in
|
|
3832
|
+
* `keyringCache`). The ceremony performs the WebAuthn enrollment and
|
|
3833
|
+
* returns the slot options that hub then persists via the standard
|
|
3834
|
+
* tier-2 enrollAuthenticator path.
|
|
3835
|
+
*
|
|
3836
|
+
* Layering note: hub does not import `@noy-db/on-webauthn` (that
|
|
3837
|
+
* would invert the dep graph). The consumer wires it in:
|
|
3838
|
+
*
|
|
3839
|
+
* ```ts
|
|
3840
|
+
* import { enrollWebAuthn } from '@noy-db/on-webauthn'
|
|
3841
|
+
*
|
|
3842
|
+
* await db.enrollWebAuthn('demo', async (keyring) => {
|
|
3843
|
+
* const e = await enrollWebAuthn(keyring, 'demo', { rp: {...} })
|
|
3844
|
+
* return {
|
|
3845
|
+
* id: `webauthn-${e.credentialId.slice(0, 8)}`,
|
|
3846
|
+
* method: 'webauthn',
|
|
3847
|
+
* wrapped_kek: e.wrappedPayload,
|
|
3848
|
+
* meta: {
|
|
3849
|
+
* credentialId: e.credentialId,
|
|
3850
|
+
* wrapIv: e.wrapIv,
|
|
3851
|
+
* prfUsed: e.prfUsed,
|
|
3852
|
+
* beFlag: e.beFlag,
|
|
3853
|
+
* requireSingleDevice: e.requireSingleDevice,
|
|
3854
|
+
* },
|
|
3855
|
+
* }
|
|
3856
|
+
* })
|
|
3857
|
+
* ```
|
|
3858
|
+
*
|
|
3859
|
+
* Returns the WebAuthn `credentialId` (extracted from `meta.credentialId`)
|
|
3860
|
+
* for the caller's lookup index (a bootstrap vault, a PublicEnvelope,
|
|
3861
|
+
* a server-side allowlist).
|
|
3862
|
+
*
|
|
3863
|
+
* Gated by `enroll-authenticator` like `enrollAuthenticator()` itself.
|
|
3864
|
+
*
|
|
3865
|
+
* @see #16
|
|
3866
|
+
*/
|
|
3867
|
+
enrollWebAuthn(vault: string, ceremony: (keyring: UnlockedKeyring) => Promise<EnrollAuthenticatorOptions>, presented?: {
|
|
3868
|
+
factors?: ReadonlyArray<FactorProof>;
|
|
3869
|
+
sharedDevice?: boolean;
|
|
3870
|
+
}): Promise<{
|
|
3871
|
+
credentialId: string;
|
|
3872
|
+
}>;
|
|
3873
|
+
/**
|
|
3874
|
+
* Filter the slot list to webauthn-method slots only. Useful for
|
|
3875
|
+
* "you have N WebAuthn credentials enrolled" UI surfaces and for
|
|
3876
|
+
* deciding when a new device prompt should appear. Identity is
|
|
3877
|
+
* `id` + `enrolled_at`; the `meta.credentialId` (base64) is used by
|
|
3878
|
+
* `allowCredentials` at unlock time.
|
|
3879
|
+
*
|
|
3880
|
+
* @see #16
|
|
3881
|
+
*/
|
|
3882
|
+
listWebAuthnSlots(vault: string): Promise<ReadonlyArray<{
|
|
3883
|
+
id: string;
|
|
3884
|
+
enrolledAt: string;
|
|
3885
|
+
credentialId: string;
|
|
3886
|
+
}>>;
|
|
3887
|
+
/**
|
|
3888
|
+
* Resolve a slot by id, then hand the wrapped-KEK ciphertext + meta
|
|
3889
|
+
* to the caller-supplied verifier. The verifier is the
|
|
3890
|
+
* `unlockWith*` function from the corresponding `@noy-db/on-*`
|
|
3891
|
+
* package, e.g. `unlockWithPassword(slot, password)`.
|
|
3892
|
+
*
|
|
3893
|
+
* On success, mark the active session tier as 2 — subsequent
|
|
3894
|
+
* `checkGate` calls see a tier-2 unlock.
|
|
3895
|
+
*/
|
|
3896
|
+
unlockViaAuthenticator(vault: string, slotId: string, verify: (slot: KeyringAuthenticator) => Promise<UnlockedKeyring>): Promise<UnlockedKeyring>;
|
|
3897
|
+
/**
|
|
3898
|
+
* Set the owner-curated public envelope for a vault. Throws
|
|
3899
|
+
* `ValidationError` if the developer did not opt the hub into
|
|
3900
|
+
* `publicEnvelope` via `NoydbOptions`, or if the input violates
|
|
3901
|
+
* the resolved schema (oversized icon, disallowed MIME, oversized
|
|
3902
|
+
* string, unknown field).
|
|
3903
|
+
*
|
|
3904
|
+
* `createdAt` is set on the first write and preserved on every
|
|
3905
|
+
* subsequent write. `updatedAt` is refreshed on every write.
|
|
3906
|
+
* `version` is monotonic — increments on every successful write.
|
|
3907
|
+
*/
|
|
3908
|
+
setPublicEnvelope(vault: string, input: SetPublicEnvelopeInput): Promise<PublicEnvelope>;
|
|
3909
|
+
/**
|
|
3910
|
+
* Read the public envelope for a vault. Returns `undefined` when
|
|
3911
|
+
* none has been written. Pass `locale` to resolve any locale-map
|
|
3912
|
+
* fields to plain strings; omitting `locale` returns the raw map.
|
|
3913
|
+
*
|
|
3914
|
+
* Works even when the developer didn't enable
|
|
3915
|
+
* `publicEnvelope` — reads are passive and never throw on a
|
|
3916
|
+
* missing schema (the envelope is plaintext and exists on disk
|
|
3917
|
+
* regardless).
|
|
3918
|
+
*/
|
|
3919
|
+
getPublicEnvelope(vault: string, opts?: {
|
|
3920
|
+
readonly locale?: string;
|
|
3921
|
+
}): Promise<PublicEnvelope | undefined>;
|
|
3922
|
+
/** English summary of the configured auth model. */
|
|
3923
|
+
describeAuthConfig(vault: string): Promise<string>;
|
|
3924
|
+
/** Mermaid `flowchart TB` source for the auth graph. */
|
|
3925
|
+
diagramAuthConfig(vault: string): Promise<string>;
|
|
3926
|
+
/**
|
|
3927
|
+
* Per-user enrollment summary. Gated by `view-user-auth` (default:
|
|
3928
|
+
* disabled). Sanitization is allowlist-based — never renders cred
|
|
3929
|
+
* ids, password hashes, secrets, or any field outside the allowlist.
|
|
3930
|
+
*/
|
|
3931
|
+
describeUserAuth(vault: string, userId: string, factors?: {
|
|
3932
|
+
factors?: ReadonlyArray<FactorProof>;
|
|
3933
|
+
sharedDevice?: boolean;
|
|
3934
|
+
}): Promise<string>;
|
|
3935
|
+
/** Bulk variant for owner dashboards. Gated by `view-user-auth`. */
|
|
3936
|
+
describeAllUsersAuth(vault: string, factors?: {
|
|
3937
|
+
factors?: ReadonlyArray<FactorProof>;
|
|
3938
|
+
sharedDevice?: boolean;
|
|
3939
|
+
}): Promise<Array<{
|
|
3940
|
+
userId: string;
|
|
3941
|
+
description: string;
|
|
3942
|
+
}>>;
|
|
3943
|
+
/**
|
|
3944
|
+
* Rotate the user's passphrase (user remembers old). Validates the
|
|
3945
|
+
* new phrase against the configured `passphrase` policy, runs the
|
|
3946
|
+
* `rotate-passphrase` gate, then re-derives + re-wraps every DEK.
|
|
3947
|
+
*
|
|
3948
|
+
* Tier-2 authenticator slots are dropped — each slot wraps the old
|
|
3949
|
+
* KEK and would need its derivation key to be re-presented. Re-enrol
|
|
3950
|
+
* via `db.enrollAuthenticator` after rotation. Tracked as a
|
|
3951
|
+
* v0.1.0-pre.5 limitation.
|
|
3952
|
+
*
|
|
3953
|
+
* @throws `WeakPassphraseError` on a weak new phrase.
|
|
3954
|
+
* @throws `PolicyDeniedError` when the gate denies (missing factor, …).
|
|
3955
|
+
* @throws `InvalidKeyError` when `oldPassphrase` is wrong.
|
|
3956
|
+
*/
|
|
3957
|
+
rotatePassphrase(vault: string, input: RotatePassphraseInput, factors?: {
|
|
3958
|
+
factors?: ReadonlyArray<FactorProof>;
|
|
3959
|
+
sharedDevice?: boolean;
|
|
3960
|
+
}): Promise<void>;
|
|
3961
|
+
/**
|
|
3962
|
+
* Reset the passphrase using a recovery proof (user forgot the old).
|
|
3963
|
+
* v0.1.0-pre.5 supports the `'paper'` profile end-to-end; the
|
|
3964
|
+
* other three profiles throw {@link RecoveryProfileNotImplementedError}.
|
|
3965
|
+
*
|
|
3966
|
+
* Burns the used recovery entry on success.
|
|
3967
|
+
*/
|
|
3968
|
+
recoverPassphrase(vault: string, input: RecoverPassphraseInput, factors?: {
|
|
3969
|
+
factors?: ReadonlyArray<FactorProof>;
|
|
3970
|
+
sharedDevice?: boolean;
|
|
3971
|
+
}): Promise<void>;
|
|
3972
|
+
/**
|
|
3973
|
+
* Persist a recovery enrollment. v0.1.0-pre.5 accepts the `'paper'`
|
|
3974
|
+
* profile — the developer first calls
|
|
3975
|
+
* `@noy-db/on-recovery/generateRecoveryCodeSet` to mint codes +
|
|
3976
|
+
* entries, shows the codes to the user once, then hands the entries
|
|
3977
|
+
* here.
|
|
3978
|
+
*
|
|
3979
|
+
* ```ts
|
|
3980
|
+
* import { generateRecoveryCodeSet } from '@noy-db/on-recovery'
|
|
3981
|
+
* const { codes, entries } = await generateRecoveryCodeSet({ kek, count: 10 })
|
|
3982
|
+
* await db.enrollRecovery('acme', { profile: 'paper', entries })
|
|
3983
|
+
* showCodesToUser(codes)
|
|
3984
|
+
* ```
|
|
3985
|
+
*/
|
|
3986
|
+
enrollRecovery(vault: string, enrollment: {
|
|
3987
|
+
profile: 'paper';
|
|
3988
|
+
entries: ReadonlyArray<PaperRecoveryEntry>;
|
|
3989
|
+
}): Promise<void>;
|
|
3990
|
+
/** Read the persisted paper-recovery entries. Used by `describeAuthConfig` (#13). */
|
|
3991
|
+
listRecoveryEntries(vault: string): Promise<{
|
|
3992
|
+
paper: ReadonlyArray<PaperRecoveryEntry>;
|
|
3993
|
+
}>;
|
|
3994
|
+
/**
|
|
3995
|
+
* Register a tier-3 quick-unlock state for the vault. The state is
|
|
3996
|
+
* an opaque blob produced by `@noy-db/on-pin/enrollPin` (or any
|
|
3997
|
+
* compatible primitive). It is held in memory only — never persisted
|
|
3998
|
+
* — and auto-clears when its `expiresAt` elapses.
|
|
3999
|
+
*
|
|
4000
|
+
* Gated by `rotate-unlock` (the same gate covers "set" and "rotate"
|
|
4001
|
+
* because tier-3 is a single-slot rolling secret).
|
|
4002
|
+
*/
|
|
4003
|
+
enrollUnlock(vault: string, state: QuickUnlockState, presented?: {
|
|
4004
|
+
factors?: ReadonlyArray<FactorProof>;
|
|
4005
|
+
sharedDevice?: boolean;
|
|
4006
|
+
}): Promise<void>;
|
|
4007
|
+
/**
|
|
4008
|
+
* Resume a session via the registered tier-3 state. The verifier is
|
|
4009
|
+
* `@noy-db/on-pin/resumePin` (or compatible). On success, mark the
|
|
4010
|
+
* active session tier as 3 — every operation must re-authenticate at
|
|
4011
|
+
* tier 2 to elevate.
|
|
4012
|
+
*
|
|
4013
|
+
* Returns `undefined` (caller should fall back to tier 2) when no
|
|
4014
|
+
* tier-3 state is registered.
|
|
4015
|
+
*/
|
|
4016
|
+
unlockViaPin(vault: string, resume: (state: QuickUnlockState) => Promise<UnlockedKeyring>): Promise<UnlockedKeyring | undefined>;
|
|
4017
|
+
/** Drop the tier-3 state for a vault — explicit logout. */
|
|
4018
|
+
clearQuickUnlock(vault: string): void;
|
|
3116
4019
|
/** Get or load the keyring for a vault. */
|
|
3117
4020
|
private getKeyring;
|
|
3118
4021
|
}
|
|
@@ -3580,6 +4483,170 @@ declare function magicLinkGrantRecordId(token: string, index: number): string;
|
|
|
3580
4483
|
*/
|
|
3581
4484
|
declare function isMagicLinkGrantExpired(payload: MagicLinkGrantPayload, now?: Date): boolean;
|
|
3582
4485
|
|
|
4486
|
+
/**
|
|
4487
|
+
* Public `vault.user.*` API surface.
|
|
4488
|
+
*
|
|
4489
|
+
* Three families:
|
|
4490
|
+
* - Write-self: `me` / `updateMe` / `setMe` — always target the writer's
|
|
4491
|
+
* own keyringId. **Own-only write rule** is structural — no method
|
|
4492
|
+
* exists to write someone else's envelope.
|
|
4493
|
+
* - Read-anyone: `get` / `list` — read other principals' envelopes
|
|
4494
|
+
* (subject to `view-team-profiles` policy gate, wired in #22).
|
|
4495
|
+
* - Reactive: `subscribe` / `live` — in-process event emission on local
|
|
4496
|
+
* writes. Cross-instance updates land via the team/sync engine and
|
|
4497
|
+
* surface to subscribers when the sync diff replays through this API.
|
|
4498
|
+
*
|
|
4499
|
+
* @see docs/superpowers/specs/2026-05-05-user-envelope-design.md
|
|
4500
|
+
*
|
|
4501
|
+
* @module
|
|
4502
|
+
*/
|
|
4503
|
+
|
|
4504
|
+
/**
|
|
4505
|
+
* Recursive partial. Used for `updateMe(patch)` so callers can hand in
|
|
4506
|
+
* deeply-nested partial shapes and have them deep-merged onto the
|
|
4507
|
+
* current envelope.
|
|
4508
|
+
*/
|
|
4509
|
+
type DeepPartial<T> = T extends object ? {
|
|
4510
|
+
[P in keyof T]?: DeepPartial<T[P]>;
|
|
4511
|
+
} : T;
|
|
4512
|
+
/** Cancel a previously-registered subscription. */
|
|
4513
|
+
type Unsubscribe = () => void;
|
|
4514
|
+
/**
|
|
4515
|
+
* Optional factor-proof bundle threaded into gated user-envelope
|
|
4516
|
+
* operations. Same shape as `Noydb.checkGate(vault, gate, presented)`
|
|
4517
|
+
* accepts elsewhere — apps that have already presented a TOTP/email-OTP
|
|
4518
|
+
* for this session pass it here to satisfy tightened policies.
|
|
4519
|
+
*/
|
|
4520
|
+
interface UserEnvelopePresented {
|
|
4521
|
+
readonly factors?: readonly FactorProof[];
|
|
4522
|
+
readonly sharedDevice?: boolean;
|
|
4523
|
+
}
|
|
4524
|
+
/**
|
|
4525
|
+
* Callback used by `UserApi` to validate the active session against a
|
|
4526
|
+
* policy gate. Provided by the `Vault` constructor; in production this
|
|
4527
|
+
* delegates to `Noydb.checkGate(vault, gate, presented)`. In tests, a
|
|
4528
|
+
* no-op stub is fine.
|
|
4529
|
+
*/
|
|
4530
|
+
type UserEnvelopeCheckGate = (gate: 'edit-own-profile' | 'view-team-profiles', presented?: UserEnvelopePresented) => Promise<void>;
|
|
4531
|
+
/**
|
|
4532
|
+
* Reactive handle returned by `live()`. `current` is the most recently
|
|
4533
|
+
* observed value; `subscribe(cb)` fires on subsequent local writes.
|
|
4534
|
+
* `stop()` releases the underlying subscription.
|
|
4535
|
+
*/
|
|
4536
|
+
interface LiveUserEnvelope<T> {
|
|
4537
|
+
current(): UserEnvelope<T> | null;
|
|
4538
|
+
subscribe(cb: (env: UserEnvelope<T> | null) => void): Unsubscribe;
|
|
4539
|
+
stop(): void;
|
|
4540
|
+
}
|
|
4541
|
+
/**
|
|
4542
|
+
* Implementation behind `vault.user`. Constructed once per Vault, holds
|
|
4543
|
+
* the writer's keyringId in closure so `updateMe`/`setMe` cannot target
|
|
4544
|
+
* any other principal — the own-only rule is enforced at the type level
|
|
4545
|
+
* (no `set(otherKeyringId, …)` method) AND at runtime (the
|
|
4546
|
+
* keyringId argument simply doesn't exist on the write path).
|
|
4547
|
+
*/
|
|
4548
|
+
declare class UserApi {
|
|
4549
|
+
private readonly adapter;
|
|
4550
|
+
private readonly vaultName;
|
|
4551
|
+
/** The writer's own keyringId. Frozen at construction time. */
|
|
4552
|
+
private readonly writerKeyringId;
|
|
4553
|
+
private readonly getDek;
|
|
4554
|
+
/**
|
|
4555
|
+
* Policy-gate validator. When omitted, gates are skipped — useful
|
|
4556
|
+
* for low-level tests that exercise the storage layer directly.
|
|
4557
|
+
* Production paths always wire the Noydb-backed implementation.
|
|
4558
|
+
*/
|
|
4559
|
+
private readonly checkGate?;
|
|
4560
|
+
/** keyringId → set of listeners. Wildcard '*' fires on every change. */
|
|
4561
|
+
private readonly listeners;
|
|
4562
|
+
constructor(adapter: NoydbStore, vaultName: string,
|
|
4563
|
+
/** The writer's own keyringId. Frozen at construction time. */
|
|
4564
|
+
writerKeyringId: string, getDek: () => Promise<CryptoKey>,
|
|
4565
|
+
/**
|
|
4566
|
+
* Policy-gate validator. When omitted, gates are skipped — useful
|
|
4567
|
+
* for low-level tests that exercise the storage layer directly.
|
|
4568
|
+
* Production paths always wire the Noydb-backed implementation.
|
|
4569
|
+
*/
|
|
4570
|
+
checkGate?: UserEnvelopeCheckGate | undefined);
|
|
4571
|
+
/** Read the writer's own envelope. Returns null if never written. */
|
|
4572
|
+
me<T = unknown>(): Promise<UserEnvelope<T> | null>;
|
|
4573
|
+
/**
|
|
4574
|
+
* Deep-merge a partial patch into the writer's own envelope. Creates
|
|
4575
|
+
* the envelope on first call. Optimistic-concurrency safe — a stale
|
|
4576
|
+
* `_v` (parallel writer on another device) throws `ConflictError`.
|
|
4577
|
+
*
|
|
4578
|
+
* Gated by the `edit-own-profile` policy gate (default `minTier: 3`).
|
|
4579
|
+
* Pass `presented` to satisfy tightened policies that require a
|
|
4580
|
+
* factor proof (e.g. STRICT_POLICY's TOTP requirement).
|
|
4581
|
+
*/
|
|
4582
|
+
updateMe<T extends object = Record<string, unknown>>(patch: DeepPartial<T>, presented?: UserEnvelopePresented): Promise<UserEnvelope<T>>;
|
|
4583
|
+
/**
|
|
4584
|
+
* Replace the writer's own envelope with `payload`. Use sparingly —
|
|
4585
|
+
* `updateMe` is the canonical mutation. No `expectedVersion` check;
|
|
4586
|
+
* callers explicitly take last-write-wins semantics.
|
|
4587
|
+
*
|
|
4588
|
+
* Gated by `edit-own-profile`. See `updateMe` for `presented` usage.
|
|
4589
|
+
*/
|
|
4590
|
+
setMe<T = unknown>(payload: T, presented?: UserEnvelopePresented): Promise<UserEnvelope<T>>;
|
|
4591
|
+
/**
|
|
4592
|
+
* Read another principal's envelope by their keyringId. Returns null
|
|
4593
|
+
* if the principal exists but has no envelope yet, or if the
|
|
4594
|
+
* keyringId does not exist at all.
|
|
4595
|
+
*
|
|
4596
|
+
* Gated by `view-team-profiles` (default `minTier: 2`) — but ONLY for
|
|
4597
|
+
* cross-principal reads. Reading your own envelope (`keyringId ===
|
|
4598
|
+
* self`) is never gated; that's just `me()` written long-form.
|
|
4599
|
+
*/
|
|
4600
|
+
get<T = unknown>(keyringId: string, presented?: UserEnvelopePresented): Promise<UserEnvelope<T> | null>;
|
|
4601
|
+
/**
|
|
4602
|
+
* Read every persisted envelope in the vault. Order is store-defined.
|
|
4603
|
+
*
|
|
4604
|
+
* Gated by `view-team-profiles`. Default policy (`minTier: 2`) lets
|
|
4605
|
+
* any authenticated session read all envelopes. Two privacy-strict
|
|
4606
|
+
* opt-outs:
|
|
4607
|
+
*
|
|
4608
|
+
* - `view-team-profiles.enabled: false` → list() returns only the
|
|
4609
|
+
* caller's own envelope (silent self-fallback, no thrown error).
|
|
4610
|
+
* - `view-team-profiles.minTier: 1` + insufficient tier → throws
|
|
4611
|
+
* `PolicyDeniedError` with `reason: 'insufficient-tier'`. The
|
|
4612
|
+
* caller is expected to elevate, not silently degrade.
|
|
4613
|
+
*
|
|
4614
|
+
* The asymmetry is deliberate: `enabled: false` is a deliberate
|
|
4615
|
+
* design choice ("nobody sees teammate profiles in this app");
|
|
4616
|
+
* `insufficient-tier` is "you need to authenticate further". Different
|
|
4617
|
+
* UX prompts for different intents.
|
|
4618
|
+
*/
|
|
4619
|
+
list<T = unknown>(presented?: UserEnvelopePresented): Promise<UserEnvelope<T>[]>;
|
|
4620
|
+
/**
|
|
4621
|
+
* Listen for changes to a specific keyringId's envelope. The callback
|
|
4622
|
+
* fires synchronously after every successful local `updateMe` /
|
|
4623
|
+
* `setMe` for that principal.
|
|
4624
|
+
*
|
|
4625
|
+
* Cross-instance changes (a teammate edits their profile on their
|
|
4626
|
+
* device, the sync engine pulls the diff onto this device) will fire
|
|
4627
|
+
* subscribers when the sync layer replays the write through this API.
|
|
4628
|
+
* In v1, subscribers do NOT fire on raw store changes — wire your sync
|
|
4629
|
+
* layer to call back through `vault.user.setMe` / `updateMe` if you
|
|
4630
|
+
* need that.
|
|
4631
|
+
*
|
|
4632
|
+
* Pass keyringId `'*'` to fire on every change in the vault.
|
|
4633
|
+
*/
|
|
4634
|
+
subscribe<T = unknown>(keyringId: string, cb: (env: UserEnvelope<T> | null) => void): Unsubscribe;
|
|
4635
|
+
/**
|
|
4636
|
+
* Reactive handle that caches the current value and re-reads on every
|
|
4637
|
+
* change for the given keyringId. Convenient for framework bindings:
|
|
4638
|
+
*
|
|
4639
|
+
* const live = vault.user.live<UserShape>(vault.userId)
|
|
4640
|
+
* live.subscribe(env => render(env?.data))
|
|
4641
|
+
*
|
|
4642
|
+
* Initial value is `null` until the first `current()` call materializes
|
|
4643
|
+
* it via `vault.user.get()`. Call `stop()` when done to release the
|
|
4644
|
+
* subscription.
|
|
4645
|
+
*/
|
|
4646
|
+
live<T = unknown>(keyringId: string): LiveUserEnvelope<T>;
|
|
4647
|
+
private fireChange;
|
|
4648
|
+
}
|
|
4649
|
+
|
|
3583
4650
|
/** A vault (tenant namespace) containing collections. */
|
|
3584
4651
|
declare class Vault {
|
|
3585
4652
|
private readonly adapter;
|
|
@@ -3623,6 +4690,19 @@ declare class Vault {
|
|
|
3623
4690
|
private readonly i18nStrategy;
|
|
3624
4691
|
private readonly syncStrategy;
|
|
3625
4692
|
private getDEK;
|
|
4693
|
+
/**
|
|
4694
|
+
* Per-principal user envelope API.
|
|
4695
|
+
*
|
|
4696
|
+
* - Write-self: `me()`, `updateMe(patch)`, `setMe(payload)` — always
|
|
4697
|
+
* target this vault session's keyringId. There is no method to write
|
|
4698
|
+
* another principal's envelope (own-only write rule, structural).
|
|
4699
|
+
* - Read-anyone: `get(keyringId)`, `list()` — read other principals'
|
|
4700
|
+
* envelopes, subject to the `view-team-profiles` policy gate (#22).
|
|
4701
|
+
* - Reactive: `subscribe(id, cb)`, `live(id)` — fire on local writes.
|
|
4702
|
+
*
|
|
4703
|
+
* @see docs/superpowers/specs/2026-05-05-user-envelope-design.md
|
|
4704
|
+
*/
|
|
4705
|
+
readonly user: UserApi;
|
|
3626
4706
|
/**
|
|
3627
4707
|
* Optional callback that re-derives an UnlockedKeyring from the
|
|
3628
4708
|
* adapter using the active user's passphrase. Called by `load()`
|
|
@@ -4368,6 +5448,22 @@ declare class Vault {
|
|
|
4368
5448
|
* separate vault instances now.
|
|
4369
5449
|
*/
|
|
4370
5450
|
getBundleHandle(): Promise<string>;
|
|
5451
|
+
/**
|
|
5452
|
+
* Read the owner-curated public envelope for this vault (or
|
|
5453
|
+
* `undefined` if none is persisted). The envelope lives in
|
|
5454
|
+
* `_meta/public-envelope` as plaintext — readable without any KEK
|
|
5455
|
+
* — so `getBundleHandle`-style callers can label a vault before
|
|
5456
|
+
* unlock.
|
|
5457
|
+
*
|
|
5458
|
+
* Mirrors `Noydb.getPublicEnvelope(vault, opts)` but scoped to a
|
|
5459
|
+
* single, already-opened `Vault` instance so the
|
|
5460
|
+
* bundle writer can snapshot it without holding a `Noydb` reference.
|
|
5461
|
+
*
|
|
5462
|
+
* @see docs/subsystems/public-envelope.md
|
|
5463
|
+
*/
|
|
5464
|
+
getPublicEnvelope(opts?: {
|
|
5465
|
+
readonly locale?: string;
|
|
5466
|
+
}): Promise<PublicEnvelope | undefined>;
|
|
4371
5467
|
/**
|
|
4372
5468
|
* Dump vault as a verifiable encrypted JSON backup string.
|
|
4373
5469
|
*
|
|
@@ -6448,6 +7544,71 @@ interface ImportCapability {
|
|
|
6448
7544
|
readonly plaintext?: readonly ExportFormat[];
|
|
6449
7545
|
readonly bundle?: boolean;
|
|
6450
7546
|
}
|
|
7547
|
+
/**
|
|
7548
|
+
* Forward-declared on-disk shape for `VaultPolicy` — the actual policy
|
|
7549
|
+
* model lives in `policy/types.ts` (#9). Declared here as `unknown`-typed
|
|
7550
|
+
* map so types.ts has no dependency on the policy module while the
|
|
7551
|
+
* `KeyringFile.policy` field can still round-trip foreign documents.
|
|
7552
|
+
*
|
|
7553
|
+
* @internal
|
|
7554
|
+
*/
|
|
7555
|
+
type VaultPolicyOnDisk = Record<string, unknown>;
|
|
7556
|
+
/**
|
|
7557
|
+
* Recovery profile enrolled at vault creation (issue #10).
|
|
7558
|
+
*
|
|
7559
|
+
* - `paper` — `on-recovery` codes (the only end-to-end profile in v0.1.0-pre.5).
|
|
7560
|
+
* - `shamir` / `multi-channel` / `admin-mediated` — API surface ships;
|
|
7561
|
+
* per-profile dispatch lands in follow-up issues. Calling
|
|
7562
|
+
* `db.recoverPassphrase` against these throws
|
|
7563
|
+
* {@link RecoveryProfileNotImplementedError}.
|
|
7564
|
+
*/
|
|
7565
|
+
type RecoveryEnrollment = {
|
|
7566
|
+
readonly profile: 'paper';
|
|
7567
|
+
/** Number of single-use codes to print at enrollment. */
|
|
7568
|
+
readonly codes: number;
|
|
7569
|
+
} | {
|
|
7570
|
+
readonly profile: 'shamir';
|
|
7571
|
+
readonly k: number;
|
|
7572
|
+
readonly n: number;
|
|
7573
|
+
readonly trustees: ReadonlyArray<string>;
|
|
7574
|
+
} | {
|
|
7575
|
+
readonly profile: 'multi-channel';
|
|
7576
|
+
readonly email?: string;
|
|
7577
|
+
readonly pin?: boolean;
|
|
7578
|
+
readonly paperCodes?: number;
|
|
7579
|
+
} | {
|
|
7580
|
+
readonly profile: 'admin-mediated';
|
|
7581
|
+
readonly grantorUserId: string;
|
|
7582
|
+
};
|
|
7583
|
+
/**
|
|
7584
|
+
* One tier-2 authenticator slot inside a keyring file. Each slot
|
|
7585
|
+
* independently wraps the SAME KEK under a method-specific derived key
|
|
7586
|
+
* (LUKS pattern). Adding or removing a slot is a constant-time keyring
|
|
7587
|
+
* write — no DEK re-keying required.
|
|
7588
|
+
*
|
|
7589
|
+
* @see docs/subsystems/session-tiers.md → Tier 2 — Authenticate (multi-slot)
|
|
7590
|
+
*/
|
|
7591
|
+
interface KeyringAuthenticator {
|
|
7592
|
+
/** Caller-chosen identifier — e.g. `'webauthn-yubikey-blue'`, `'oidc-google'`, `'password-daily'`. */
|
|
7593
|
+
readonly id: string;
|
|
7594
|
+
/** Method family — selects which `@noy-db/on-*` package handles unlock. */
|
|
7595
|
+
readonly method: 'webauthn' | 'oidc' | 'password';
|
|
7596
|
+
/** ISO-8601 timestamp at which the slot was added. */
|
|
7597
|
+
readonly enrolled_at: string;
|
|
7598
|
+
/**
|
|
7599
|
+
* Which session tier ENROLLED this slot. Tier 1 enrolls a fresh slot;
|
|
7600
|
+
* tier 2 may add a sibling slot when the active policy permits.
|
|
7601
|
+
*/
|
|
7602
|
+
readonly enrolled_via_tier: 1 | 2;
|
|
7603
|
+
/** Base64 wrapped-KEK ciphertext under the method-derived key. */
|
|
7604
|
+
readonly wrapped_kek: string;
|
|
7605
|
+
/**
|
|
7606
|
+
* Method-specific metadata: WebAuthn cred id, OIDC issuer/sub, PBKDF2
|
|
7607
|
+
* salt for `on-password`, etc. The schema is open by design — the
|
|
7608
|
+
* `@noy-db/on-*` package owns the contents.
|
|
7609
|
+
*/
|
|
7610
|
+
readonly meta: Record<string, unknown>;
|
|
7611
|
+
}
|
|
6451
7612
|
interface KeyringFile {
|
|
6452
7613
|
readonly _noydb_keyring: typeof NOYDB_KEYRING_VERSION;
|
|
6453
7614
|
readonly user_id: string;
|
|
@@ -6458,6 +7619,23 @@ interface KeyringFile {
|
|
|
6458
7619
|
readonly salt: string;
|
|
6459
7620
|
readonly created_at: string;
|
|
6460
7621
|
readonly granted_by: string;
|
|
7622
|
+
/**
|
|
7623
|
+
* Tier-2 authenticator slots (multi-slot keyring extension).
|
|
7624
|
+
* Optional / append-only: keyring files written before the
|
|
7625
|
+
* extension load with an empty list. Each slot independently wraps
|
|
7626
|
+
* the same KEK; any one of them unlocks.
|
|
7627
|
+
*
|
|
7628
|
+
* @see KeyringAuthenticator
|
|
7629
|
+
*/
|
|
7630
|
+
readonly authenticators?: readonly KeyringAuthenticator[];
|
|
7631
|
+
/**
|
|
7632
|
+
* Per-keyring policy override (reserved). The on-disk format
|
|
7633
|
+
* accepts the field for forward compatibility with the Option C
|
|
7634
|
+
* merge engine deferred to a later release; v1.0 reads only the
|
|
7635
|
+
* vault-level `_meta/policy` document, so this field is parsed and
|
|
7636
|
+
* round-tripped but never enforced.
|
|
7637
|
+
*/
|
|
7638
|
+
readonly policy?: VaultPolicyOnDisk;
|
|
6461
7639
|
/**
|
|
6462
7640
|
* Optional — authorization spec capability bits. Absent on keyrings written
|
|
6463
7641
|
* before the RFC implementation. Loading falls back to role-based
|
|
@@ -6812,6 +7990,29 @@ interface GrantOptions {
|
|
|
6812
7990
|
* is grantable until positively listed; bundle import is denied.
|
|
6813
7991
|
*/
|
|
6814
7992
|
readonly importCapability?: ImportCapability;
|
|
7993
|
+
/**
|
|
7994
|
+
* Skip phrase-format strength validation (issue #7). Defaults to
|
|
7995
|
+
* false — `grant()` rejects phrases that don't meet the configured
|
|
7996
|
+
* `PassphrasePolicy`. Test fixtures and CLI scripts pass `true`.
|
|
7997
|
+
*/
|
|
7998
|
+
readonly allowWeakPassphrase?: boolean;
|
|
7999
|
+
/**
|
|
8000
|
+
* Initial user-envelope payload for the new principal. Sealed under
|
|
8001
|
+
* the same vault DEK (the reserved `_users` collection's DEK) and
|
|
8002
|
+
* persisted alongside the keyring during grant.
|
|
8003
|
+
*
|
|
8004
|
+
* **Bootstrap-only.** Once the new user activates and writes their
|
|
8005
|
+
* own envelope, the own-only write rule kicks in — admins cannot
|
|
8006
|
+
* edit a teammate's envelope after activation. Use this field for
|
|
8007
|
+
* pre-fill at invite time (e.g. "displayName: Bob, locale: en-US")
|
|
8008
|
+
* and let the user take over from there.
|
|
8009
|
+
*
|
|
8010
|
+
* Hub does not introspect the payload; it is JSON-serialized and
|
|
8011
|
+
* encrypted opaquely. Apps own the schema.
|
|
8012
|
+
*
|
|
8013
|
+
* @see docs/superpowers/specs/2026-05-05-user-envelope-design.md → Lifecycle
|
|
8014
|
+
*/
|
|
8015
|
+
readonly initialProfile?: unknown;
|
|
6815
8016
|
}
|
|
6816
8017
|
interface RevokeOptions {
|
|
6817
8018
|
readonly userId: string;
|
|
@@ -7460,8 +8661,75 @@ interface NoydbOptions {
|
|
|
7460
8661
|
* legacy `sessionTimeout` field.
|
|
7461
8662
|
*/
|
|
7462
8663
|
readonly sessionPolicy?: SessionPolicy;
|
|
7463
|
-
/**
|
|
8664
|
+
/**
|
|
8665
|
+
* Validate passphrase strength against the phrase format
|
|
8666
|
+
* (`@noy-db/hub` issue #7) on first-time keyring creation. When
|
|
8667
|
+
* `true`, weak phrases throw {@link WeakPassphraseError} from
|
|
8668
|
+
* `createNoydb()` / `db.rotatePassphrase()`. Default: `false` for
|
|
8669
|
+
* back-compat in v0.1.x; planned to flip to `true` at v1.0.
|
|
8670
|
+
*/
|
|
7464
8671
|
readonly validatePassphrase?: boolean;
|
|
8672
|
+
/**
|
|
8673
|
+
* Vault-level policy gate document (issue #9). When present, the hub
|
|
8674
|
+
* persists the merged policy at `_meta/policy` on first-time vault
|
|
8675
|
+
* creation and gates sensitive operations (`db.rotatePassphrase`,
|
|
8676
|
+
* `db.export*`, …) against it. Omitted ⇒ the engine uses
|
|
8677
|
+
* {@link PERSONAL_POLICY}. Use {@link STRICT_POLICY} for regulated
|
|
8678
|
+
* deployments.
|
|
8679
|
+
*
|
|
8680
|
+
* The on-disk document is the source of truth — the policy field
|
|
8681
|
+
* is only honored at vault creation; subsequent runs read from
|
|
8682
|
+
* `_meta/policy`. Use `db.updatePolicy()` to change it deliberately.
|
|
8683
|
+
*
|
|
8684
|
+
* Imported from `@noy-db/hub` as a type-only reference; the runtime
|
|
8685
|
+
* import lives in `policy/index.ts`.
|
|
8686
|
+
*/
|
|
8687
|
+
readonly policy?: VaultPolicy;
|
|
8688
|
+
/**
|
|
8689
|
+
* Mandatory recovery profile enrollment (issue #10). Vaults with
|
|
8690
|
+
* `recover-passphrase` enabled MUST register at least one profile
|
|
8691
|
+
* before being production-ready, otherwise `createNoydb()` throws
|
|
8692
|
+
* {@link RecoveryNotEnrolledError}. Set
|
|
8693
|
+
* `policy.gates['recover-passphrase'].enabled = false` to
|
|
8694
|
+
* deliberately opt out of recovery (passphrase loss = data loss).
|
|
8695
|
+
*
|
|
8696
|
+
* v0.1.0-pre.5 supports the `'paper'` profile end-to-end. Other
|
|
8697
|
+
* profiles ship the API shape and throw
|
|
8698
|
+
* {@link RecoveryProfileNotImplementedError} during use.
|
|
8699
|
+
*/
|
|
8700
|
+
readonly recovery?: ReadonlyArray<RecoveryEnrollment>;
|
|
8701
|
+
/**
|
|
8702
|
+
* When `true`, `createNoydb` rejects vaults with no recovery
|
|
8703
|
+
* entries persisted (per the spec's mandatory-enrollment
|
|
8704
|
+
* requirement). Default `false` for v0.1.x back-compat; planned to
|
|
8705
|
+
* flip to `true` at v1.0. Apps in regulated environments should
|
|
8706
|
+
* turn this on now.
|
|
8707
|
+
*/
|
|
8708
|
+
readonly requireRecovery?: boolean;
|
|
8709
|
+
/**
|
|
8710
|
+
* What to do when `openVault` finds an existing keyring in the store that
|
|
8711
|
+
* cannot be decrypted with the supplied credentials (`InvalidKeyError`).
|
|
8712
|
+
*
|
|
8713
|
+
* - `'error'` (default) — propagate the error. The app must prompt the user
|
|
8714
|
+
* to supply the correct credentials or clear both the data and auth stores.
|
|
8715
|
+
* - `'reset'` — delete the stale keyring and re-initialise the vault from
|
|
8716
|
+
* scratch using the current credentials. Use this when the data store can
|
|
8717
|
+
* become detached from the auth store (e.g. the user cleared the IndexedDB
|
|
8718
|
+
* data records but not the keyring row, or a WebAuthn credential was rotated).
|
|
8719
|
+
* **All previously encrypted data is unrecoverable after a reset.**
|
|
8720
|
+
*
|
|
8721
|
+
* Only applies to the passphrase (`secret`) path. When `getKeyring` is used,
|
|
8722
|
+
* the callback is responsible for handling stale-keyring detection itself.
|
|
8723
|
+
*/
|
|
8724
|
+
readonly onInvalidKey?: 'error' | 'reset';
|
|
8725
|
+
/**
|
|
8726
|
+
* Enable the public envelope subsystem (`docs/subsystems/public-envelope.md`).
|
|
8727
|
+
* Pass `true` for the default schema (every standard field, 256 KB
|
|
8728
|
+
* icon cap, 200-char text cap), or a `PublicEnvelopeSchema` to
|
|
8729
|
+
* narrow what the owner can set. Off by default — vaults written
|
|
8730
|
+
* by hubs without this option carry no envelope, full stop.
|
|
8731
|
+
*/
|
|
8732
|
+
readonly publicEnvelope?: true | PublicEnvelopeSchema;
|
|
7465
8733
|
/** Audit history configuration. */
|
|
7466
8734
|
readonly history?: HistoryConfig;
|
|
7467
8735
|
/**
|
|
@@ -7562,4 +8830,4 @@ interface DeleteManyResult {
|
|
|
7562
8830
|
}>;
|
|
7563
8831
|
}
|
|
7564
8832
|
|
|
7565
|
-
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
|
|
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, assertStrongPassphrase 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, SyncEngine as cA, type SyncMetadata as cB, type SyncPolicy as cC, SyncScheduler as cD, type SyncSchedulerStatus as cE, type SyncStatus as cF, type SyncTarget as cG, type SyncTargetRole as cH, SyncTransaction as cI, type SyncTransactionResult as cJ, type TierMode as cK, type TranslatorAuditEntry as cL, type TxOp as cM, USER_ENVELOPE_COLLECTION as cN, USER_ENVELOPE_MAX_BYTES as cO, type Unsubscribe as cP, UserApi as cQ, type UserEnvelopeCheckGate as cR, UserEnvelopeOversizedError as cS, type UserEnvelopePresented as cT, type UserInfo as cU, type VaultBackup as cV, type VaultPolicyOnDisk as cW, type VaultSnapshot as cX, type WarningRules as cY, WeakPassphraseError as cZ, type WeakPassphraseReason 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 RecoveryProof as co, type ResolvedPublicEnvelopeSchema as cp, type RevokeOptions as cq, type RotatePassphraseInput as cr, type SessionPolicy as cs, type SetPublicEnvelopeInput as ct, type StandardSchemaV1 as cu, type StandardSchemaV1Issue as cv, type StandardSchemaV1SyncResult as cw, type StoreAuth as cx, type StoreAuthKind as cy, type StoreCapabilities as cz, type DictionaryOptions as d, buildRecipientKeyringFile as d0, burnPaperRecoveryEntry as d1, createNoydb as d2, createStore as d3, deriveMagicLinkContentKey as d4, enrollAuthenticator as d5, estimateEntropy as d6, evaluateExportCapability as d7, evaluateImportCapability as d8, findAuthenticator as d9, writeMagicLinkGrant as dA, hasExportCapability as da, hasImportCapability as db, hasRecoveryEnrolled as dc, isMagicLinkGrantExpired as dd, isPublicEnvelope as de, issueDelegation as df, recoverPassphrase as dg, rotatePassphrase as dh, listMagicLinkGrants as di, listUsers as dj, listUsersWithEnvelopes as dk, loadActiveDelegations as dl, loadPaperRecoveryEntries as dm, magicLinkGrantRecordId as dn, readMagicLinkGrantRecord as dp, removeAuthenticator as dq, resolveSchema as dr, revokeDelegation as ds, revokeMagicLinkGrant as dt, savePaperRecoveryEntries as du, unwrapMagicLinkGrant as dv, validatePassphrase as dw, validatePublicEnvelopeInput as dx, validateSchemaInput as dy, validateSchemaOutput 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 };
|