mindoodb 0.0.44 → 0.0.46

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.
Files changed (91) hide show
  1. package/README.md +9 -0
  2. package/dist/appendonlystores/network/HttpTransport.d.ts.map +1 -1
  3. package/dist/appendonlystores/network/HttpTransport.js +4 -0
  4. package/dist/appendonlystores/network/HttpTransport.js.map +1 -1
  5. package/dist/browser/index.mjs +31 -0
  6. package/dist/core/BaseMindooDB.d.ts +74 -0
  7. package/dist/core/BaseMindooDB.d.ts.map +1 -1
  8. package/dist/core/BaseMindooDB.js +160 -0
  9. package/dist/core/BaseMindooDB.js.map +1 -1
  10. package/dist/core/BaseMindooTenant.d.ts +22 -0
  11. package/dist/core/BaseMindooTenant.d.ts.map +1 -1
  12. package/dist/core/BaseMindooTenant.js +57 -6
  13. package/dist/core/BaseMindooTenant.js.map +1 -1
  14. package/dist/core/BaseMindooTenantFactory.d.ts.map +1 -1
  15. package/dist/core/BaseMindooTenantFactory.js +31 -9
  16. package/dist/core/BaseMindooTenantFactory.js.map +1 -1
  17. package/dist/core/copy/attachments.d.ts +53 -0
  18. package/dist/core/copy/attachments.d.ts.map +1 -0
  19. package/dist/core/copy/attachments.js +186 -0
  20. package/dist/core/copy/attachments.js.map +1 -0
  21. package/dist/core/copy/copyDocument.d.ts +86 -0
  22. package/dist/core/copy/copyDocument.d.ts.map +1 -0
  23. package/dist/core/copy/copyDocument.js +369 -0
  24. package/dist/core/copy/copyDocument.js.map +1 -0
  25. package/dist/core/copy/copyDocuments.d.ts +57 -0
  26. package/dist/core/copy/copyDocuments.d.ts.map +1 -0
  27. package/dist/core/copy/copyDocuments.js +167 -0
  28. package/dist/core/copy/copyDocuments.js.map +1 -0
  29. package/dist/core/copy/entryRewrite.d.ts +49 -0
  30. package/dist/core/copy/entryRewrite.d.ts.map +1 -0
  31. package/dist/core/copy/entryRewrite.js +74 -0
  32. package/dist/core/copy/entryRewrite.js.map +1 -0
  33. package/dist/core/copy/feasibility.d.ts +73 -0
  34. package/dist/core/copy/feasibility.d.ts.map +1 -0
  35. package/dist/core/copy/feasibility.js +169 -0
  36. package/dist/core/copy/feasibility.js.map +1 -0
  37. package/dist/core/copy/flatten.d.ts +55 -0
  38. package/dist/core/copy/flatten.d.ts.map +1 -0
  39. package/dist/core/copy/flatten.js +155 -0
  40. package/dist/core/copy/flatten.js.map +1 -0
  41. package/dist/core/copy/host.d.ts +92 -0
  42. package/dist/core/copy/host.d.ts.map +1 -0
  43. package/dist/core/copy/host.js +16 -0
  44. package/dist/core/copy/host.js.map +1 -0
  45. package/dist/core/copy/preflight.d.ts +62 -0
  46. package/dist/core/copy/preflight.d.ts.map +1 -0
  47. package/dist/core/copy/preflight.js +98 -0
  48. package/dist/core/copy/preflight.js.map +1 -0
  49. package/dist/core/copy/provenance.d.ts +74 -0
  50. package/dist/core/copy/provenance.d.ts.map +1 -0
  51. package/dist/core/copy/provenance.js +91 -0
  52. package/dist/core/copy/provenance.js.map +1 -0
  53. package/dist/core/copy/types.d.ts +418 -0
  54. package/dist/core/copy/types.d.ts.map +1 -0
  55. package/dist/core/copy/types.js +21 -0
  56. package/dist/core/copy/types.js.map +1 -0
  57. package/dist/core/crypto/DomainStatement.d.ts +44 -0
  58. package/dist/core/crypto/DomainStatement.d.ts.map +1 -0
  59. package/dist/core/crypto/DomainStatement.js +71 -0
  60. package/dist/core/crypto/DomainStatement.js.map +1 -0
  61. package/dist/core/crypto/EntrySignature.d.ts +109 -15
  62. package/dist/core/crypto/EntrySignature.d.ts.map +1 -1
  63. package/dist/core/crypto/EntrySignature.js +83 -22
  64. package/dist/core/crypto/EntrySignature.js.map +1 -1
  65. package/dist/core/index.d.ts +21 -0
  66. package/dist/core/index.d.ts.map +1 -1
  67. package/dist/core/index.js +54 -3
  68. package/dist/core/index.js.map +1 -1
  69. package/dist/core/timestamps/timestampClient.d.ts +88 -0
  70. package/dist/core/timestamps/timestampClient.d.ts.map +1 -0
  71. package/dist/core/timestamps/timestampClient.js +157 -0
  72. package/dist/core/timestamps/timestampClient.js.map +1 -0
  73. package/dist/core/types.d.ts +134 -4
  74. package/dist/core/types.d.ts.map +1 -1
  75. package/dist/core/types.js.map +1 -1
  76. package/dist/node/server/MindooDBServer.d.ts +37 -0
  77. package/dist/node/server/MindooDBServer.d.ts.map +1 -1
  78. package/dist/node/server/MindooDBServer.js +157 -0
  79. package/dist/node/server/MindooDBServer.js.map +1 -1
  80. package/dist/node/server/config.d.ts.map +1 -1
  81. package/dist/node/server/config.js +16 -0
  82. package/dist/node/server/config.js.map +1 -1
  83. package/dist/node/server/timestampProxy.d.ts +129 -0
  84. package/dist/node/server/timestampProxy.d.ts.map +1 -0
  85. package/dist/node/server/timestampProxy.js +361 -0
  86. package/dist/node/server/timestampProxy.js.map +1 -0
  87. package/dist/node/server/types.d.ts +20 -0
  88. package/dist/node/server/types.d.ts.map +1 -1
  89. package/dist/node/server/types.js +12 -0
  90. package/dist/node/server/types.js.map +1 -1
  91. package/package.json +1 -1
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Client transport for RFC 3161 timestamps.
3
+ *
4
+ * Two paths, because no single one works everywhere:
5
+ *
6
+ * - **Direct.** `https://rfc3161.ai.moda` is the only public TSA measured to
7
+ * send CORS headers, so a browser can reach it unaided. This keeps sealing
8
+ * working on a Haven with no MindooDB server at all.
9
+ * - **Proxy.** Everything else is http-only or CORS-less — including every
10
+ * eIDAS-qualified authority — so those go through the tenant's server at
11
+ * `POST /:tenantId/timestamps/rfc3161`.
12
+ *
13
+ * Both carry opaque DER. **Neither is trusted.** The caller must verify that
14
+ * the returned token's `messageImprint` equals the digest it sent and that the
15
+ * token chains to a trusted root (see `verifyTimestampResponse` in
16
+ * `mindoodb-seal`). With those two checks a hostile transport can withhold a
17
+ * timestamp but cannot forge one, which is why calling a third-party proxy from
18
+ * the browser is acceptable at all.
19
+ */
20
+ export declare const TIMESTAMP_QUERY_CONTENT_TYPE = "application/timestamp-query";
21
+ export declare const TIMESTAMP_REPLY_CONTENT_TYPE = "application/timestamp-reply";
22
+ /** Bound on an accepted TimeStampResp; real tokens are a few KB. */
23
+ export declare const MAX_TIMESTAMP_RESPONSE_BYTES: number;
24
+ export declare const DEFAULT_TIMESTAMP_TIMEOUT_MS = 15000;
25
+ /**
26
+ * The one public TSA a browser can call directly (verified by CORS preflight:
27
+ * reflected origin, `POST, OPTIONS`, `Content-Type`). It is a failover proxy in
28
+ * front of DigiCert, Sectigo, Azure and others, so its tokens chain to roots
29
+ * already in public trust stores.
30
+ */
31
+ export declare const BROWSER_REACHABLE_TSA: Readonly<{
32
+ id: "aimoda";
33
+ name: "ai.moda RFC 3161 proxy";
34
+ url: "https://rfc3161.ai.moda";
35
+ }>;
36
+ export type TimestampSource = "direct" | "proxy";
37
+ export interface TimestampTokenResult {
38
+ /** Raw DER TimeStampResp, unverified. */
39
+ response: Uint8Array;
40
+ providerId: string;
41
+ source: TimestampSource;
42
+ }
43
+ export declare class TimestampRequestError extends Error {
44
+ readonly source: TimestampSource;
45
+ readonly httpStatus?: number | undefined;
46
+ constructor(message: string, source: TimestampSource, httpStatus?: number | undefined);
47
+ }
48
+ interface PostTimestampOptions {
49
+ request: Uint8Array;
50
+ timeoutMs?: number;
51
+ fetchImpl?: typeof fetch;
52
+ maxResponseBytes?: number;
53
+ }
54
+ /** Call a CORS-enabled TSA straight from the client. */
55
+ export declare function requestTimestampDirect(options: PostTimestampOptions & {
56
+ url?: string;
57
+ providerId?: string;
58
+ }): Promise<TimestampTokenResult>;
59
+ export interface ProxyTimestampOptions extends PostTimestampOptions {
60
+ /** Base URL of the MindooDB server, e.g. `https://sync.example.com`. */
61
+ serverUrl: string;
62
+ tenantId: string;
63
+ /** Bearer token from the tenant's existing authenticated session. */
64
+ authToken: string;
65
+ /** Provider id from the server's advertised list. Never a URL — the server allowlists. */
66
+ providerId?: string;
67
+ }
68
+ /** Route through the tenant's MindooDB server, for http-only or CORS-less authorities. */
69
+ export declare function requestTimestampViaProxy(options: ProxyTimestampOptions): Promise<TimestampTokenResult>;
70
+ export interface TimestampWithFallbackOptions extends PostTimestampOptions {
71
+ /** Proxy details, when the client has an authenticated server connection. */
72
+ proxy?: Omit<ProxyTimestampOptions, keyof PostTimestampOptions>;
73
+ /** Try the proxy first — the right order when the tenant has a qualified provider configured. */
74
+ preferProxy?: boolean;
75
+ /** Skip the direct call (air-gapped deployments, or policy against third-party endpoints). */
76
+ allowDirect?: boolean;
77
+ }
78
+ /**
79
+ * Try both transports, returning the first token obtained.
80
+ *
81
+ * A seal is worth little without a timestamp, so failing over is worth the
82
+ * extra round trip. The order is caller-chosen because it is a policy question,
83
+ * not a technical one: a tenant with a qualified provider configured wants the
84
+ * proxy first, while a laptop with no server reachable wants the direct path.
85
+ */
86
+ export declare function requestTimestampWithFallback(options: TimestampWithFallbackOptions): Promise<TimestampTokenResult>;
87
+ export {};
88
+ //# sourceMappingURL=timestampClient.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timestampClient.d.ts","sourceRoot":"","sources":["../../../src/core/timestamps/timestampClient.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,eAAO,MAAM,4BAA4B,gCAAgC,CAAC;AAC1E,eAAO,MAAM,4BAA4B,gCAAgC,CAAC;AAE1E,oEAAoE;AACpE,eAAO,MAAM,4BAA4B,QAAa,CAAC;AACvD,eAAO,MAAM,4BAA4B,QAAS,CAAC;AAEnD;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB;;;;EAIhC,CAAC;AAEH,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEjD,MAAM,WAAW,oBAAoB;IACnC,yCAAyC;IACzC,QAAQ,EAAE,UAAU,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,eAAe,CAAC;CACzB;AAED,qBAAa,qBAAsB,SAAQ,KAAK;IAG5C,QAAQ,CAAC,MAAM,EAAE,eAAe;IAChC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM;gBAF5B,OAAO,EAAE,MAAM,EACN,MAAM,EAAE,eAAe,EACvB,UAAU,CAAC,EAAE,MAAM,YAAA;CAK/B;AAED,UAAU,oBAAoB;IAC5B,OAAO,EAAE,UAAU,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAkED,wDAAwD;AACxD,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,oBAAoB,GAAG;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,GACpE,OAAO,CAAC,oBAAoB,CAAC,CAQ/B;AAED,MAAM,WAAW,qBAAsB,SAAQ,oBAAoB;IACjE,wEAAwE;IACxE,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,qEAAqE;IACrE,SAAS,EAAE,MAAM,CAAC;IAClB,0FAA0F;IAC1F,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,0FAA0F;AAC1F,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,oBAAoB,CAAC,CAe/B;AAED,MAAM,WAAW,4BAA6B,SAAQ,oBAAoB;IACxE,6EAA6E;IAC7E,KAAK,CAAC,EAAE,IAAI,CAAC,qBAAqB,EAAE,MAAM,oBAAoB,CAAC,CAAC;IAChE,iGAAiG;IACjG,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,8FAA8F;IAC9F,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED;;;;;;;GAOG;AACH,wBAAsB,4BAA4B,CAChD,OAAO,EAAE,4BAA4B,GACpC,OAAO,CAAC,oBAAoB,CAAC,CA+B/B"}
@@ -0,0 +1,157 @@
1
+ "use strict";
2
+ /**
3
+ * Client transport for RFC 3161 timestamps.
4
+ *
5
+ * Two paths, because no single one works everywhere:
6
+ *
7
+ * - **Direct.** `https://rfc3161.ai.moda` is the only public TSA measured to
8
+ * send CORS headers, so a browser can reach it unaided. This keeps sealing
9
+ * working on a Haven with no MindooDB server at all.
10
+ * - **Proxy.** Everything else is http-only or CORS-less — including every
11
+ * eIDAS-qualified authority — so those go through the tenant's server at
12
+ * `POST /:tenantId/timestamps/rfc3161`.
13
+ *
14
+ * Both carry opaque DER. **Neither is trusted.** The caller must verify that
15
+ * the returned token's `messageImprint` equals the digest it sent and that the
16
+ * token chains to a trusted root (see `verifyTimestampResponse` in
17
+ * `mindoodb-seal`). With those two checks a hostile transport can withhold a
18
+ * timestamp but cannot forge one, which is why calling a third-party proxy from
19
+ * the browser is acceptable at all.
20
+ */
21
+ Object.defineProperty(exports, "__esModule", { value: true });
22
+ exports.TimestampRequestError = exports.BROWSER_REACHABLE_TSA = exports.DEFAULT_TIMESTAMP_TIMEOUT_MS = exports.MAX_TIMESTAMP_RESPONSE_BYTES = exports.TIMESTAMP_REPLY_CONTENT_TYPE = exports.TIMESTAMP_QUERY_CONTENT_TYPE = void 0;
23
+ exports.requestTimestampDirect = requestTimestampDirect;
24
+ exports.requestTimestampViaProxy = requestTimestampViaProxy;
25
+ exports.requestTimestampWithFallback = requestTimestampWithFallback;
26
+ exports.TIMESTAMP_QUERY_CONTENT_TYPE = "application/timestamp-query";
27
+ exports.TIMESTAMP_REPLY_CONTENT_TYPE = "application/timestamp-reply";
28
+ /** Bound on an accepted TimeStampResp; real tokens are a few KB. */
29
+ exports.MAX_TIMESTAMP_RESPONSE_BYTES = 128 * 1024;
30
+ exports.DEFAULT_TIMESTAMP_TIMEOUT_MS = 15000;
31
+ /**
32
+ * The one public TSA a browser can call directly (verified by CORS preflight:
33
+ * reflected origin, `POST, OPTIONS`, `Content-Type`). It is a failover proxy in
34
+ * front of DigiCert, Sectigo, Azure and others, so its tokens chain to roots
35
+ * already in public trust stores.
36
+ */
37
+ exports.BROWSER_REACHABLE_TSA = Object.freeze({
38
+ id: "aimoda",
39
+ name: "ai.moda RFC 3161 proxy",
40
+ url: "https://rfc3161.ai.moda",
41
+ });
42
+ class TimestampRequestError extends Error {
43
+ constructor(message, source, httpStatus) {
44
+ super(message);
45
+ this.source = source;
46
+ this.httpStatus = httpStatus;
47
+ this.name = "TimestampRequestError";
48
+ }
49
+ }
50
+ exports.TimestampRequestError = TimestampRequestError;
51
+ async function postTimestampQuery(url, source, options) {
52
+ const { request, timeoutMs = exports.DEFAULT_TIMESTAMP_TIMEOUT_MS, fetchImpl = fetch, maxResponseBytes = exports.MAX_TIMESTAMP_RESPONSE_BYTES, headers = {}, } = options;
53
+ const controller = new AbortController();
54
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
55
+ try {
56
+ const response = await fetchImpl(url, {
57
+ method: "POST",
58
+ headers: {
59
+ "content-type": exports.TIMESTAMP_QUERY_CONTENT_TYPE,
60
+ accept: exports.TIMESTAMP_REPLY_CONTENT_TYPE,
61
+ ...headers,
62
+ },
63
+ body: request.slice().buffer,
64
+ signal: controller.signal,
65
+ });
66
+ if (!response.ok) {
67
+ throw new TimestampRequestError(`Timestamp request failed with HTTP ${response.status}`, source, response.status);
68
+ }
69
+ const buffer = new Uint8Array(await response.arrayBuffer());
70
+ if (buffer.byteLength === 0) {
71
+ throw new TimestampRequestError("Timestamp authority returned an empty response", source);
72
+ }
73
+ if (buffer.byteLength > maxResponseBytes) {
74
+ throw new TimestampRequestError(`Timestamp response too large (${buffer.byteLength} bytes)`, source);
75
+ }
76
+ // Cheap sanity check; the real validation is CMS verification by the caller.
77
+ if (buffer[0] !== 0x30) {
78
+ throw new TimestampRequestError("Timestamp response is not DER-encoded", source);
79
+ }
80
+ return buffer;
81
+ }
82
+ catch (error) {
83
+ if (error instanceof TimestampRequestError)
84
+ throw error;
85
+ if (error instanceof Error && error.name === "AbortError") {
86
+ throw new TimestampRequestError(`Timestamp request timed out after ${timeoutMs}ms`, source);
87
+ }
88
+ throw new TimestampRequestError(`Timestamp request failed: ${error instanceof Error ? error.message : String(error)}`, source);
89
+ }
90
+ finally {
91
+ clearTimeout(timer);
92
+ }
93
+ }
94
+ /** Call a CORS-enabled TSA straight from the client. */
95
+ async function requestTimestampDirect(options) {
96
+ const url = options.url ?? exports.BROWSER_REACHABLE_TSA.url;
97
+ const response = await postTimestampQuery(url, "direct", options);
98
+ return {
99
+ response,
100
+ providerId: options.providerId ?? exports.BROWSER_REACHABLE_TSA.id,
101
+ source: "direct",
102
+ };
103
+ }
104
+ /** Route through the tenant's MindooDB server, for http-only or CORS-less authorities. */
105
+ async function requestTimestampViaProxy(options) {
106
+ const base = options.serverUrl.replace(/\/+$/, "");
107
+ const query = options.providerId ? `?provider=${encodeURIComponent(options.providerId)}` : "";
108
+ const url = `${base}/${encodeURIComponent(options.tenantId)}/timestamps/rfc3161${query}`;
109
+ const response = await postTimestampQuery(url, "proxy", {
110
+ ...options,
111
+ headers: { authorization: `Bearer ${options.authToken}` },
112
+ });
113
+ return {
114
+ response,
115
+ providerId: options.providerId ?? "server",
116
+ source: "proxy",
117
+ };
118
+ }
119
+ /**
120
+ * Try both transports, returning the first token obtained.
121
+ *
122
+ * A seal is worth little without a timestamp, so failing over is worth the
123
+ * extra round trip. The order is caller-chosen because it is a policy question,
124
+ * not a technical one: a tenant with a qualified provider configured wants the
125
+ * proxy first, while a laptop with no server reachable wants the direct path.
126
+ */
127
+ async function requestTimestampWithFallback(options) {
128
+ const { proxy, preferProxy = false, allowDirect = true, ...post } = options;
129
+ const attempts = [];
130
+ const direct = () => requestTimestampDirect(post);
131
+ const viaProxy = proxy ? () => requestTimestampViaProxy({ ...proxy, ...post }) : undefined;
132
+ if (preferProxy && viaProxy) {
133
+ attempts.push(viaProxy);
134
+ if (allowDirect)
135
+ attempts.push(direct);
136
+ }
137
+ else {
138
+ if (allowDirect)
139
+ attempts.push(direct);
140
+ if (viaProxy)
141
+ attempts.push(viaProxy);
142
+ }
143
+ if (attempts.length === 0) {
144
+ throw new TimestampRequestError("No timestamp transport is available", "direct");
145
+ }
146
+ const errors = [];
147
+ for (const attempt of attempts) {
148
+ try {
149
+ return await attempt();
150
+ }
151
+ catch (error) {
152
+ errors.push(error instanceof Error ? error.message : String(error));
153
+ }
154
+ }
155
+ throw new TimestampRequestError(`No timestamp authority could be reached (${errors.join("; ")})`, "direct");
156
+ }
157
+ //# sourceMappingURL=timestampClient.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timestampClient.js","sourceRoot":"","sources":["../../../src/core/timestamps/timestampClient.ts"],"names":[],"mappings":"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"}
@@ -4,6 +4,8 @@ import type { PublicUserId, PrivateUserId } from "./userid";
4
4
  import { StoreKind } from "./appendonlystores/types";
5
5
  import type { ContentAddressedStore, OpenStoreOptions, RejectedPutEntry } from "./appendonlystores/types";
6
6
  import type { CryptoAdapter } from "./crypto/CryptoAdapter";
7
+ import type { EntryProvenance } from "./crypto/EntrySignature";
8
+ import type { CopyDocumentOptions, CopyDocumentResult, CopyDocumentSelector, CopyDocumentsOptions, CopyDocumentsResult, CopyFeasibility } from "./copy/types";
7
9
  import { MindooDocSigner } from "./crypto/MindooDocSigner";
8
10
  import type { DefaultAccessPolicyDoc, KeyDistributionPushRecipient, KeyDistributionRequest, KeyDistributionView, KeyVersionRef, AppDistributionRequest, AppDistributionView, AppDistributionReconcilePlan, SyncSetupPolicyRequest, SyncSetupPolicyView, SyncSetupPolicyReconcilePlan, DocHistoryPurgeRequest, DocHistoryPurgeView, RuleTargets, RuleType, WithFieldClause, AclRuleDoc, AccessDecision } from "./accesscontrol/types";
9
11
  import type { DirectoryStateNode } from "./accesscontrol/DirectoryStateNode";
@@ -242,10 +244,25 @@ export interface CreateTenantResult {
242
244
  * Can be serialized to a mdb://join-request/... URI for out-of-band exchange.
243
245
  */
244
246
  export interface JoinRequest {
245
- /** Protocol version (always 1 for now) */
246
- v: 1;
247
- /** The username the user wants to be registered as (e.g. "cn=user2/o=acme") */
248
- username: string;
247
+ /**
248
+ * Protocol version. A `v: 1` request always carries {@link username}; a
249
+ * `v: 2` request may omit it and leave the name to the approving admin.
250
+ * Emitting `2` only for nameless requests keeps older readers from silently
251
+ * registering an undefined username — they reject the unknown version
252
+ * instead.
253
+ */
254
+ v: 1 | 2;
255
+ /**
256
+ * The username the user wants to be registered as (e.g. "cn=user2/o=acme").
257
+ *
258
+ * Optional from v2 on. Omitting it is the recommended path when enrolling an
259
+ * additional device for someone who is already in the directory: the exact
260
+ * spelling only exists on the admin's device, and a mistyped name here would
261
+ * silently register a second person rather than a second device (§6.5). The
262
+ * approving admin then supplies the name via
263
+ * {@link ApproveJoinRequestOptions.username}.
264
+ */
265
+ username?: string;
249
266
  /** Ed25519 public signing key (PEM format) */
250
267
  signingPublicKey: string;
251
268
  /** RSA-OAEP public encryption key (PEM format) */
@@ -288,6 +305,16 @@ export interface ApproveJoinRequestOptions {
288
305
  * §6.5). Useful for recording a date or a note about the device type.
289
306
  */
290
307
  label?: string;
308
+ /**
309
+ * Username to register the joining device under, overriding whatever the
310
+ * request suggested. Required when the request omits a username.
311
+ *
312
+ * Passing the exact spelling of an existing directory entry is what turns a
313
+ * join request into an additional device for that user instead of a new
314
+ * person, so admin UIs should offer a pick-from-directory choice here rather
315
+ * than trusting the requester's spelling (§6.5).
316
+ */
317
+ username?: string;
291
318
  /** If "uri", approveJoinRequest returns a mdb://join-response/... URI string instead of an object */
292
319
  format?: "object" | "uri";
293
320
  }
@@ -323,6 +350,15 @@ export interface JoinResponse {
323
350
  serverUrl?: string;
324
351
  /** Optional admin username for display purposes */
325
352
  adminUsername?: string;
353
+ /**
354
+ * The username the joining device was actually registered as.
355
+ *
356
+ * The joining device adopts this so its local identity matches the directory
357
+ * even when the admin corrected the requested name or supplied one for a
358
+ * nameless request. Absent in responses from older admins, in which case the
359
+ * requester keeps the name it chose itself.
360
+ */
361
+ username?: string;
326
362
  /** Selected tenant document keys, encrypted with the sharePassword. */
327
363
  encryptedDocKeys: JoinResponseEncryptedDocKey[];
328
364
  }
@@ -381,6 +417,12 @@ export interface JoinTenantResult {
381
417
  tenant: MindooTenant;
382
418
  /** The KeyBag containing the imported tenant and $publicinfos keys */
383
419
  keyBag: KeyBag;
420
+ /**
421
+ * The identity the tenant was opened with. Identical to the supplied user
422
+ * unless the join response carried a different username, in which case this
423
+ * is the renamed copy (same keys) that the caller should persist locally.
424
+ */
425
+ user: PrivateUserId;
384
426
  }
385
427
  /**
386
428
  * Options for publishing a tenant to a server.
@@ -593,6 +635,28 @@ export interface MindooTenant {
593
635
  * @return The signature (Ed25519 signature as Uint8Array)
594
636
  */
595
637
  signPayloadWithKey(payload: Uint8Array, signingKeyPair: SigningKeyPair, password: string): Promise<Uint8Array>;
638
+ /**
639
+ * Sign application-supplied bytes under an allowlisted domain, signing
640
+ * `utf8(domain) || 0x00 || statement`.
641
+ *
642
+ * The only sanctioned way to have the identity key sign content an
643
+ * application chose. The domain prefix keeps such bytes disjoint from the
644
+ * store-entry and auth-challenge layouts, so a signature obtained here cannot
645
+ * be replayed as either (see `crypto/DomainStatement.ts`).
646
+ *
647
+ * @param domain One of `SIGNING_DOMAINS`; anything else is rejected.
648
+ * @param statement Canonical statement bytes.
649
+ * @return The Ed25519 signature over the domain-prefixed bytes.
650
+ */
651
+ signDomainStatement(domain: string, statement: Uint8Array): Promise<Uint8Array>;
652
+ /**
653
+ * Verify a domain-separated statement signature against a named public key.
654
+ *
655
+ * Does not consult the directory: whether that key is *currently trusted* is
656
+ * a separate question from whether it made this signature, and an evidence
657
+ * verifier legitimately asks only the second one.
658
+ */
659
+ verifyDomainStatement(domain: string, statement: Uint8Array, signature: Uint8Array, publicKeyPem: string): Promise<boolean>;
596
660
  /**
597
661
  * Verify a signature for a payload with a public key.
598
662
  *
@@ -922,6 +986,21 @@ export interface StoreEntryMetadata {
922
986
  * fall back to {@link signature} when it is missing (backward compatible).
923
987
  */
924
988
  metadataSignature?: Uint8Array;
989
+ /**
990
+ * Set on entries produced by copying a document into another database with
991
+ * `mode: "history"` and re-authored signatures (see `copyDocumentTo`). Records
992
+ * where the entry came from and who originally authored it, in a form a reader
993
+ * can verify rather than merely trust.
994
+ *
995
+ * Bound into {@link metadataSignature} as a tagged trailing block (see
996
+ * `crypto/EntrySignature.ts`), so a relay can neither attach, strip nor rewrite
997
+ * it. Absent on every entry that is not the product of such a copy, in which
998
+ * case the signing input is byte-identical to the original layout.
999
+ *
1000
+ * Plain JSON by design (the signature inside is base64) so it survives the
1001
+ * on-disk, IndexedDB and network serializers without per-field binary handling.
1002
+ */
1003
+ provenance?: EntryProvenance;
925
1004
  /**
926
1005
  * Original size of the plaintext data before encryption (in bytes).
927
1006
  * For doc_* entries: size of the Automerge binary change/snapshot
@@ -4338,6 +4417,57 @@ export interface MindooDB {
4338
4417
  * call.
4339
4418
  */
4340
4419
  getBackgroundWarmerProgress?(): BackgroundWarmerProgress | null;
4420
+ /**
4421
+ * Copy one document into another database, either flattened to a single
4422
+ * revision or with its full revision history.
4423
+ *
4424
+ * Unlike {@link pullChangesFrom} / {@link pushChangesTo}, which replicate one
4425
+ * database between peers, this moves content ACROSS databases — and, with
4426
+ * `signingKeyPair` unset and the right options, across tenants too.
4427
+ *
4428
+ * The default is a flattened copy under a freshly generated document id,
4429
+ * authored by the current user. `mode: "history"` carries the whole revision
4430
+ * graph instead; adding `targetDocId: "same"` and `authorship: "preserve"`
4431
+ * (only legal within one tenant, under the same encryption key, into a
4432
+ * different database) keeps the original signers' signatures valid, which is
4433
+ * what makes {@link copyDocumentsTo} usable as a sharding tool.
4434
+ *
4435
+ * See `docs/document-copy.md` for the full strategy matrix and caveats.
4436
+ *
4437
+ * @param docId The document to copy.
4438
+ * @param target The database to copy into.
4439
+ * @param options See {@link CopyDocumentOptions}.
4440
+ * @return What was copied, including the resulting target document id.
4441
+ */
4442
+ copyDocumentTo(docId: string, target: MindooDB, options?: CopyDocumentOptions): Promise<CopyDocumentResult>;
4443
+ /**
4444
+ * Report what {@link copyDocumentTo} would do with these options, without
4445
+ * writing anything: the strategy it would use, whether the original authors'
4446
+ * signatures would survive, whether it would merge into an existing document,
4447
+ * and — via `reasons` — why not, when the answer is no.
4448
+ *
4449
+ * @param docId The document that would be copied.
4450
+ * @param target The database it would be copied into.
4451
+ * @param options The same options that would be passed to {@link copyDocumentTo}.
4452
+ */
4453
+ canCopyDocumentTo(docId: string, target: MindooDB, options?: CopyDocumentOptions): Promise<CopyFeasibility>;
4454
+ /**
4455
+ * Copy many documents into another database in one pass.
4456
+ *
4457
+ * With `mode: "history"`, `targetDocId: "same"` and `authorship: "preserve"`
4458
+ * this splits a database into shards with document ids, full history and
4459
+ * original signers intact, without decrypting or re-signing anything.
4460
+ * Repeated runs transfer only the delta, so a shard can proceed while users
4461
+ * keep working; see the migration playbook in `docs/document-copy.md`.
4462
+ *
4463
+ * A failure on one document does not abort the run — failures are collected
4464
+ * in {@link CopyDocumentsResult.failed}.
4465
+ *
4466
+ * @param selector Which documents to copy: explicit ids, id prefixes, or both.
4467
+ * @param target The database to copy into.
4468
+ * @param options See {@link CopyDocumentsOptions}.
4469
+ */
4470
+ copyDocumentsTo(selector: CopyDocumentSelector, target: MindooDB, options?: CopyDocumentsOptions): Promise<CopyDocumentsResult>;
4341
4471
  /**
4342
4472
  * Pull changes from a remote content-addressed store or another MindooDB instance.
4343
4473
  *