mindoodb 0.0.84 → 0.0.86
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/README-server.md +1 -0
- package/README.md +6 -4
- package/dist/appendonlystores/network/ClientNetworkContentAddressedStore.d.ts +15 -0
- package/dist/appendonlystores/network/ClientNetworkContentAddressedStore.d.ts.map +1 -1
- package/dist/appendonlystores/network/ClientNetworkContentAddressedStore.js +75 -1
- package/dist/appendonlystores/network/ClientNetworkContentAddressedStore.js.map +1 -1
- package/dist/appendonlystores/network/HttpTransport.d.ts +9 -1
- package/dist/appendonlystores/network/HttpTransport.d.ts.map +1 -1
- package/dist/appendonlystores/network/HttpTransport.js +38 -10
- package/dist/appendonlystores/network/HttpTransport.js.map +1 -1
- package/dist/cloudflare/DurableObjectSql.d.ts +56 -0
- package/dist/cloudflare/DurableObjectSql.d.ts.map +1 -0
- package/dist/cloudflare/DurableObjectSql.js +180 -0
- package/dist/cloudflare/DurableObjectSql.js.map +1 -0
- package/dist/cloudflare/MindooDatabaseObject.d.ts +86 -0
- package/dist/cloudflare/MindooDatabaseObject.d.ts.map +1 -0
- package/dist/cloudflare/MindooDatabaseObject.js +614 -0
- package/dist/cloudflare/MindooDatabaseObject.js.map +1 -0
- package/dist/cloudflare/MindooServerObject.d.ts +63 -0
- package/dist/cloudflare/MindooServerObject.d.ts.map +1 -0
- package/dist/cloudflare/MindooServerObject.js +521 -0
- package/dist/cloudflare/MindooServerObject.js.map +1 -0
- package/dist/cloudflare/WorkersCryptoAdapter.d.ts +7 -0
- package/dist/cloudflare/WorkersCryptoAdapter.d.ts.map +1 -0
- package/dist/cloudflare/WorkersCryptoAdapter.js +14 -0
- package/dist/cloudflare/WorkersCryptoAdapter.js.map +1 -0
- package/dist/cloudflare/host.d.ts +49 -0
- package/dist/cloudflare/host.d.ts.map +1 -0
- package/dist/cloudflare/host.js +104 -0
- package/dist/cloudflare/host.js.map +1 -0
- package/dist/cloudflare/http.d.ts +91 -0
- package/dist/cloudflare/http.d.ts.map +1 -0
- package/dist/cloudflare/http.js +266 -0
- package/dist/cloudflare/http.js.map +1 -0
- package/dist/cloudflare/index.d.ts +12 -0
- package/dist/cloudflare/index.d.ts.map +1 -0
- package/dist/cloudflare/index.js +20 -0
- package/dist/cloudflare/index.js.map +1 -0
- package/dist/cloudflare/storeRpc.d.ts +22 -0
- package/dist/cloudflare/storeRpc.d.ts.map +1 -0
- package/dist/cloudflare/storeRpc.js +85 -0
- package/dist/cloudflare/storeRpc.js.map +1 -0
- package/dist/cloudflare/types.d.ts +101 -0
- package/dist/cloudflare/types.d.ts.map +1 -0
- package/dist/cloudflare/types.js +10 -0
- package/dist/cloudflare/types.js.map +1 -0
- package/dist/cloudflare/worker.d.ts +25 -0
- package/dist/cloudflare/worker.d.ts.map +1 -0
- package/dist/cloudflare/worker.js +170 -0
- package/dist/cloudflare/worker.js.map +1 -0
- package/dist/core/BaseMindooDB.d.ts +43 -1
- package/dist/core/BaseMindooDB.d.ts.map +1 -1
- package/dist/core/BaseMindooDB.js +209 -87
- package/dist/core/BaseMindooDB.js.map +1 -1
- package/dist/core/BaseMindooTenant.d.ts +14 -0
- package/dist/core/BaseMindooTenant.d.ts.map +1 -1
- package/dist/core/BaseMindooTenant.js +46 -0
- package/dist/core/BaseMindooTenant.js.map +1 -1
- package/dist/core/BaseMindooTenantDirectory.d.ts +10 -0
- package/dist/core/BaseMindooTenantDirectory.d.ts.map +1 -1
- package/dist/core/BaseMindooTenantDirectory.js +79 -8
- package/dist/core/BaseMindooTenantDirectory.js.map +1 -1
- package/dist/core/ChangeDocDraft.d.ts +133 -0
- package/dist/core/ChangeDocDraft.d.ts.map +1 -0
- package/dist/core/ChangeDocDraft.js +492 -0
- package/dist/core/ChangeDocDraft.js.map +1 -0
- package/dist/core/appendonlystores/InMemoryContentAddressedStore.d.ts.map +1 -1
- package/dist/core/appendonlystores/InMemoryContentAddressedStore.js +1 -0
- package/dist/core/appendonlystores/InMemoryContentAddressedStore.js.map +1 -1
- package/dist/core/appendonlystores/network/AuthenticationService.d.ts +8 -0
- package/dist/core/appendonlystores/network/AuthenticationService.d.ts.map +1 -1
- package/dist/core/appendonlystores/network/AuthenticationService.js +19 -0
- package/dist/core/appendonlystores/network/AuthenticationService.js.map +1 -1
- package/dist/core/appendonlystores/network/IrohStreamIO.d.ts.map +1 -1
- package/dist/core/appendonlystores/network/IrohStreamIO.js +3 -23
- package/dist/core/appendonlystores/network/IrohStreamIO.js.map +1 -1
- package/dist/core/appendonlystores/network/NetworkTransport.d.ts +7 -1
- package/dist/core/appendonlystores/network/NetworkTransport.d.ts.map +1 -1
- package/dist/core/appendonlystores/network/types.d.ts +23 -0
- package/dist/core/appendonlystores/network/types.d.ts.map +1 -1
- package/dist/core/appendonlystores/network/types.js.map +1 -1
- package/dist/core/appendonlystores/rpc/storeRpcCodec.d.ts.map +1 -1
- package/dist/core/appendonlystores/rpc/storeRpcCodec.js +3 -23
- package/dist/core/appendonlystores/rpc/storeRpcCodec.js.map +1 -1
- package/dist/core/appendonlystores/sqlite/SqliteContentAddressedStore.d.ts.map +1 -1
- package/dist/core/appendonlystores/sqlite/SqliteContentAddressedStore.js +21 -30
- package/dist/core/appendonlystores/sqlite/SqliteContentAddressedStore.js.map +1 -1
- package/dist/core/appendonlystores/sqlite/SqliteDriver.d.ts +37 -1
- package/dist/core/appendonlystores/sqlite/SqliteDriver.d.ts.map +1 -1
- package/dist/core/appendonlystores/sqlite/SqliteDriver.js +99 -40
- package/dist/core/appendonlystores/sqlite/SqliteDriver.js.map +1 -1
- package/dist/core/appendonlystores/syncStores.d.ts.map +1 -1
- package/dist/core/appendonlystores/syncStores.js +253 -168
- package/dist/core/appendonlystores/syncStores.js.map +1 -1
- package/dist/core/appendonlystores/types.d.ts +8 -0
- package/dist/core/appendonlystores/types.d.ts.map +1 -1
- package/dist/core/appendonlystores/types.js.map +1 -1
- package/dist/core/expressions/evaluateExpression.d.ts +8 -0
- package/dist/core/expressions/evaluateExpression.d.ts.map +1 -1
- package/dist/core/expressions/evaluateExpression.js +29 -4
- package/dist/core/expressions/evaluateExpression.js.map +1 -1
- package/dist/core/expressions/metadata.js +4 -4
- package/dist/core/expressions/metadata.js.map +1 -1
- package/dist/core/types.d.ts +61 -2
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/core/uri/MindooURI.d.ts.map +1 -1
- package/dist/core/uri/MindooURI.js +3 -24
- package/dist/core/uri/MindooURI.js.map +1 -1
- package/dist/core/utils/base64.d.ts +17 -0
- package/dist/core/utils/base64.d.ts.map +1 -0
- package/dist/core/utils/base64.js +54 -0
- package/dist/core/utils/base64.js.map +1 -0
- package/dist/core/utils/idGeneration.d.ts.map +1 -1
- package/dist/core/utils/idGeneration.js +14 -3
- package/dist/core/utils/idGeneration.js.map +1 -1
- package/dist/node/appendonlystores/BasicOnDiskContentAddressedStore.d.ts.map +1 -1
- package/dist/node/appendonlystores/BasicOnDiskContentAddressedStore.js +1 -0
- package/dist/node/appendonlystores/BasicOnDiskContentAddressedStore.js.map +1 -1
- package/dist/node/server/FsServerDataStore.d.ts +23 -0
- package/dist/node/server/FsServerDataStore.d.ts.map +1 -0
- package/dist/node/server/FsServerDataStore.js +59 -0
- package/dist/node/server/FsServerDataStore.js.map +1 -0
- package/dist/node/server/IrohServerEndpoint.d.ts +1 -1
- package/dist/node/server/IrohServerEndpoint.d.ts.map +1 -1
- package/dist/node/server/IrohServerRpc.d.ts +5 -5
- package/dist/node/server/IrohServerRpc.d.ts.map +1 -1
- package/dist/node/server/IrohServerRpc.js +1 -1
- package/dist/node/server/IrohServerRpc.js.map +1 -1
- package/dist/node/server/MindooDBServer.d.ts +29 -150
- package/dist/node/server/MindooDBServer.d.ts.map +1 -1
- package/dist/node/server/MindooDBServer.js +160 -1568
- package/dist/node/server/MindooDBServer.js.map +1 -1
- package/dist/node/server/PurgedDocRegistry.d.ts +4 -27
- package/dist/node/server/PurgedDocRegistry.d.ts.map +1 -1
- package/dist/node/server/PurgedDocRegistry.js +6 -83
- package/dist/node/server/PurgedDocRegistry.js.map +1 -1
- package/dist/node/server/StoreFactory.d.ts +1 -1
- package/dist/node/server/StoreFactory.d.ts.map +1 -1
- package/dist/node/server/TenantManager.d.ts +16 -284
- package/dist/node/server/TenantManager.d.ts.map +1 -1
- package/dist/node/server/TenantManager.js +35 -1168
- package/dist/node/server/TenantManager.js.map +1 -1
- package/dist/node/server/add-to-network.js +1 -1
- package/dist/node/server/add-to-network.js.map +1 -1
- package/dist/node/server/compressionMiddleware.d.ts +14 -0
- package/dist/node/server/compressionMiddleware.d.ts.map +1 -1
- package/dist/node/server/compressionMiddleware.js +46 -3
- package/dist/node/server/compressionMiddleware.js.map +1 -1
- package/dist/node/server/config.d.ts +11 -15
- package/dist/node/server/config.d.ts.map +1 -1
- package/dist/node/server/config.js +30 -245
- package/dist/node/server/config.js.map +1 -1
- package/dist/node/server/expressAdapter.d.ts +23 -0
- package/dist/node/server/expressAdapter.d.ts.map +1 -0
- package/dist/node/server/expressAdapter.js +108 -0
- package/dist/node/server/expressAdapter.js.map +1 -0
- package/dist/node/server/nativeIrohStreamIO.d.ts +1 -1
- package/dist/node/server/nativeIrohStreamIO.d.ts.map +1 -1
- package/dist/node/server/peer/FileClusterAuditStorage.d.ts +16 -0
- package/dist/node/server/peer/FileClusterAuditStorage.d.ts.map +1 -0
- package/dist/node/server/peer/FileClusterAuditStorage.js +39 -0
- package/dist/node/server/peer/FileClusterAuditStorage.js.map +1 -0
- package/dist/node/server/peer/IrohPeerLink.d.ts +1 -1
- package/dist/node/server/peer/IrohPeerLink.d.ts.map +1 -1
- package/dist/node/server/peer/routes.d.ts +10 -44
- package/dist/node/server/peer/routes.d.ts.map +1 -1
- package/dist/node/server/peer/routes.js +33 -275
- package/dist/node/server/peer/routes.js.map +1 -1
- package/dist/node/server/resolveServerPassword.js +1 -1
- package/dist/node/server/resolveServerPassword.js.map +1 -1
- package/dist/node/server/server.js +1 -1
- package/dist/node/server/server.js.map +1 -1
- package/dist/node/server/serverinit.js +1 -1
- package/dist/node/server/serverinit.js.map +1 -1
- package/dist/server/CapabilityMatcher.d.ts +60 -0
- package/dist/server/CapabilityMatcher.d.ts.map +1 -0
- package/dist/server/CapabilityMatcher.js +138 -0
- package/dist/server/CapabilityMatcher.js.map +1 -0
- package/dist/server/DeviceDiscoveryService.d.ts +36 -0
- package/dist/server/DeviceDiscoveryService.d.ts.map +1 -0
- package/dist/server/DeviceDiscoveryService.js +165 -0
- package/dist/server/DeviceDiscoveryService.js.map +1 -0
- package/dist/server/PurgedDocRegistry.d.ts +35 -0
- package/dist/server/PurgedDocRegistry.d.ts.map +1 -0
- package/dist/server/PurgedDocRegistry.js +90 -0
- package/dist/server/PurgedDocRegistry.js.map +1 -0
- package/dist/server/ServerDataStore.d.ts +67 -0
- package/dist/server/ServerDataStore.d.ts.map +1 -0
- package/dist/server/ServerDataStore.js +88 -0
- package/dist/server/ServerDataStore.js.map +1 -0
- package/dist/server/SyncEventBus.d.ts +39 -0
- package/dist/server/SyncEventBus.d.ts.map +1 -0
- package/dist/server/SyncEventBus.js +41 -0
- package/dist/server/SyncEventBus.js.map +1 -0
- package/dist/server/SystemAdminAuth.d.ts +82 -0
- package/dist/server/SystemAdminAuth.d.ts.map +1 -0
- package/dist/server/SystemAdminAuth.js +228 -0
- package/dist/server/SystemAdminAuth.js.map +1 -0
- package/dist/server/TenantManager.d.ts +330 -0
- package/dist/server/TenantManager.d.ts.map +1 -0
- package/dist/server/TenantManager.js +1186 -0
- package/dist/server/TenantManager.js.map +1 -0
- package/dist/server/http/MindooDBServerCore.d.ts +313 -0
- package/dist/server/http/MindooDBServerCore.d.ts.map +1 -0
- package/dist/server/http/MindooDBServerCore.js +1403 -0
- package/dist/server/http/MindooDBServerCore.js.map +1 -0
- package/dist/server/http/types.d.ts +89 -0
- package/dist/server/http/types.d.ts.map +1 -0
- package/dist/server/http/types.js +29 -0
- package/dist/server/http/types.js.map +1 -0
- package/dist/server/index.d.ts +32 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +76 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/peer/ClusterAuditLog.d.ts +71 -0
- package/dist/server/peer/ClusterAuditLog.d.ts.map +1 -0
- package/dist/server/peer/ClusterAuditLog.js +81 -0
- package/dist/server/peer/ClusterAuditLog.js.map +1 -0
- package/dist/server/peer/ClusterJobRunner.d.ts +43 -0
- package/dist/server/peer/ClusterJobRunner.d.ts.map +1 -0
- package/dist/server/peer/ClusterJobRunner.js +119 -0
- package/dist/server/peer/ClusterJobRunner.js.map +1 -0
- package/dist/server/peer/ClusterManager.d.ts +149 -0
- package/dist/server/peer/ClusterManager.d.ts.map +1 -0
- package/dist/server/peer/ClusterManager.js +292 -0
- package/dist/server/peer/ClusterManager.js.map +1 -0
- package/dist/server/peer/PeerAuthService.d.ts +69 -0
- package/dist/server/peer/PeerAuthService.d.ts.map +1 -0
- package/dist/server/peer/PeerAuthService.js +208 -0
- package/dist/server/peer/PeerAuthService.js.map +1 -0
- package/dist/server/peer/PeerReplicator.d.ts +303 -0
- package/dist/server/peer/PeerReplicator.d.ts.map +1 -0
- package/dist/server/peer/PeerReplicator.js +1071 -0
- package/dist/server/peer/PeerReplicator.js.map +1 -0
- package/dist/server/peer/clusterHttp.d.ts +113 -0
- package/dist/server/peer/clusterHttp.d.ts.map +1 -0
- package/dist/server/peer/clusterHttp.js +290 -0
- package/dist/server/peer/clusterHttp.js.map +1 -0
- package/dist/server/peer/types.d.ts +244 -0
- package/dist/server/peer/types.d.ts.map +1 -0
- package/dist/server/peer/types.js +189 -0
- package/dist/server/peer/types.js.map +1 -0
- package/dist/server/rateLimits.d.ts +119 -0
- package/dist/server/rateLimits.d.ts.map +1 -0
- package/dist/server/rateLimits.js +159 -0
- package/dist/server/rateLimits.js.map +1 -0
- package/dist/server/serverConfig.d.ts +37 -0
- package/dist/server/serverConfig.d.ts.map +1 -0
- package/dist/server/serverConfig.js +251 -0
- package/dist/server/serverConfig.js.map +1 -0
- package/dist/server/timestampProxy.d.ts +129 -0
- package/dist/server/timestampProxy.d.ts.map +1 -0
- package/dist/server/timestampProxy.js +361 -0
- package/dist/server/timestampProxy.js.map +1 -0
- package/dist/server/types.d.ts +267 -0
- package/dist/server/types.d.ts.map +1 -0
- package/dist/server/types.js +56 -0
- package/dist/server/types.js.map +1 -0
- package/dist/server/validation.d.ts +43 -0
- package/dist/server/validation.d.ts.map +1 -0
- package/dist/server/validation.js +104 -0
- package/dist/server/validation.js.map +1 -0
- package/package.json +9 -1
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types for server-to-server (peer) replication and the cluster admin surface.
|
|
3
|
+
*
|
|
4
|
+
* Two auth classes, two route prefixes — the split is load-bearing:
|
|
5
|
+
*
|
|
6
|
+
* - `/system/peer/*` is the **server-to-server protocol**. Callers authenticate
|
|
7
|
+
* with a server identity listed in `trusted-servers.json`. Only servers speak
|
|
8
|
+
* it.
|
|
9
|
+
* - `/system/cluster/*` is the **admin surface**. Callers authenticate with a
|
|
10
|
+
* system-admin JWT and pass the {@link CapabilityMatcher}. Only humans and
|
|
11
|
+
* UIs speak it.
|
|
12
|
+
*
|
|
13
|
+
* No endpoint accepts both, so a peer can never administer and an admin can
|
|
14
|
+
* never write as a peer.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Which way entries flow to a peer.
|
|
18
|
+
*
|
|
19
|
+
* Mirrors Haven's client-side `SyncDirectionPreference` so operators meet one
|
|
20
|
+
* vocabulary. `pull` is the backup/archive node (never writes to the peer),
|
|
21
|
+
* `push` the DMZ head (never reads back), `disabled` keeps the trust entry for
|
|
22
|
+
* inbound authentication but opens no outbound session.
|
|
23
|
+
*/
|
|
24
|
+
export type PeerSyncDirection = "bidirectional" | "push" | "pull" | "disabled";
|
|
25
|
+
/**
|
|
26
|
+
* Mesh shape.
|
|
27
|
+
*
|
|
28
|
+
* A `spoke` opens sessions only to `hub` peers, never to another spoke, which
|
|
29
|
+
* turns the full mesh into a spanning tree. Because the entry set is a CRDT
|
|
30
|
+
* both converge; the tree just avoids the O(N^2) sessions of a large mesh.
|
|
31
|
+
* `peer` (the default) is fully meshed and is the right answer for two or three
|
|
32
|
+
* nodes — duplicate pushes are acknowledged idempotently by the receiver's
|
|
33
|
+
* duplicate path, so the redundancy is cheap.
|
|
34
|
+
*/
|
|
35
|
+
export type PeerRole = "peer" | "hub" | "spoke";
|
|
36
|
+
/**
|
|
37
|
+
* How eagerly attachment blobs follow their documents.
|
|
38
|
+
*
|
|
39
|
+
* Attachments are usually the bulk of the bytes and live in a separate store,
|
|
40
|
+
* which makes this the biggest bandwidth lever. `lazy` still transfers them but
|
|
41
|
+
* only after every document store is done; `never` skips the attachment store
|
|
42
|
+
* entirely (an edge node that only needs text).
|
|
43
|
+
*/
|
|
44
|
+
export type PeerAttachmentMode = "eager" | "lazy" | "never";
|
|
45
|
+
/** Peer settings resolved against their defaults. */
|
|
46
|
+
export interface PeerSettings {
|
|
47
|
+
direction: PeerSyncDirection;
|
|
48
|
+
role: PeerRole;
|
|
49
|
+
attachments: PeerAttachmentMode;
|
|
50
|
+
}
|
|
51
|
+
export declare const DEFAULT_PEER_SETTINGS: PeerSettings;
|
|
52
|
+
export declare function isPeerSyncDirection(value: unknown): value is PeerSyncDirection;
|
|
53
|
+
export declare function isPeerRole(value: unknown): value is PeerRole;
|
|
54
|
+
export declare function isPeerAttachmentMode(value: unknown): value is PeerAttachmentMode;
|
|
55
|
+
/** Fill in defaults for any peer field the operator left unset. */
|
|
56
|
+
export declare function normalizePeerSettings(raw: {
|
|
57
|
+
direction?: unknown;
|
|
58
|
+
role?: unknown;
|
|
59
|
+
attachments?: unknown;
|
|
60
|
+
}): PeerSettings;
|
|
61
|
+
/**
|
|
62
|
+
* Should this node open an outbound session to that peer?
|
|
63
|
+
*
|
|
64
|
+
* Spoke-to-spoke is the only pairing we suppress: both sides would otherwise
|
|
65
|
+
* dial each other even though the hub already carries their traffic.
|
|
66
|
+
*/
|
|
67
|
+
export declare function shouldOpenSessionTo(local: PeerRole, remote: PeerRole): boolean;
|
|
68
|
+
/** May we send local entries to this peer? */
|
|
69
|
+
export declare function directionAllowsPush(direction: PeerSyncDirection): boolean;
|
|
70
|
+
/** May we take the peer's entries? */
|
|
71
|
+
export declare function directionAllowsPull(direction: PeerSyncDirection): boolean;
|
|
72
|
+
/**
|
|
73
|
+
* Order the databases of one tenant for replication.
|
|
74
|
+
*
|
|
75
|
+
* `directory` first, then `userdirectory`, then everything else alphabetically.
|
|
76
|
+
* This is not cosmetic. A peer validates every pushed entry against its own
|
|
77
|
+
* directory (`validatePublicSigningKey`), and an unknown author key is a
|
|
78
|
+
* **per-entry rejection**, not a batch failure. Push an application database
|
|
79
|
+
* before the peer has the author's grant and those entries are refused; with
|
|
80
|
+
* the client's cursor semantics they would then be skipped forever, leaving the
|
|
81
|
+
* two servers silently divergent.
|
|
82
|
+
*
|
|
83
|
+
* The same ordering also shrinks the transient window in which a purged
|
|
84
|
+
* document or a revoked `decryptionKeyId` makes the peer answer `ACCESS_DENIED`
|
|
85
|
+
* for a whole batch: both denylists are derived from the directory (purge
|
|
86
|
+
* requests live there and are executed by `executePendingPurges` after every
|
|
87
|
+
* directory push), so they converge as soon as the directory has arrived.
|
|
88
|
+
*/
|
|
89
|
+
export declare function orderDatabasesForReplication(dbIds: Iterable<string>): string[];
|
|
90
|
+
/** True for the two system databases that must replicate before app data. */
|
|
91
|
+
export declare function isTrustBearingDatabase(dbId: string): boolean;
|
|
92
|
+
/** Live state of the outbound session to one peer. */
|
|
93
|
+
export type ClusterSessionState = "connected" | "connecting" | "backoff" | "disabled" | "paused" | "unreachable";
|
|
94
|
+
/**
|
|
95
|
+
* Health rollup, computed on the server so every console agrees on the
|
|
96
|
+
* thresholds instead of inventing its own.
|
|
97
|
+
*/
|
|
98
|
+
export type ClusterHealth = "ok" | "degraded" | "stalled" | "down";
|
|
99
|
+
/**
|
|
100
|
+
* Why a peer is not `ok`. Typed so a UI can colour-code and explain without
|
|
101
|
+
* parsing prose.
|
|
102
|
+
*
|
|
103
|
+
* `entries-awaiting-directory-trust` is the one that matters most: it means the
|
|
104
|
+
* peer rejected entries because it does not yet trust their author, the cursor
|
|
105
|
+
* was held, and a retry is pending. Left unattended it is the visible precursor
|
|
106
|
+
* of two servers drifting apart.
|
|
107
|
+
*/
|
|
108
|
+
export type ClusterHealthReason = "entries-awaiting-directory-trust" | "access-denied-backoff" | "events-disconnected" | "intersection-stale" | "lag-above-threshold" | "peer-auth-failed" | "peer-unreachable" | "no-url-configured" | "session-disabled" | "session-paused";
|
|
109
|
+
/** Typed error class beside the free-text `lastError`. */
|
|
110
|
+
export type ClusterErrorClass = "auth" | "network" | "access-denied" | "protocol" | "internal";
|
|
111
|
+
/**
|
|
112
|
+
* Bucket a replication failure so the console can react without parsing the
|
|
113
|
+
* message.
|
|
114
|
+
*
|
|
115
|
+
* The distinction that matters operationally is `access-denied` versus the
|
|
116
|
+
* rest: it is the peer *deliberately* refusing a batch (a purged document, a
|
|
117
|
+
* revoked key) and it resolves itself once the directory has replicated, so it
|
|
118
|
+
* warrants a backoff and not an alert. Everything else is either a
|
|
119
|
+
* misconfiguration (`auth`) or a genuine outage (`network`).
|
|
120
|
+
*/
|
|
121
|
+
/**
|
|
122
|
+
* Strip document and entry identifiers out of a message before it reaches the
|
|
123
|
+
* cluster status read-model.
|
|
124
|
+
*
|
|
125
|
+
* Rejection messages name the offending entry, and an entry id starts with the
|
|
126
|
+
* document id. The cluster console is a system-admin surface, and a system
|
|
127
|
+
* admin is not supposed to learn which documents a tenant holds — that is the
|
|
128
|
+
* same admin-blindness the encryption enforces, and it would be odd to hand it
|
|
129
|
+
* back through an error string. The classified reason plus the counts are what
|
|
130
|
+
* an operator can act on anyway.
|
|
131
|
+
*/
|
|
132
|
+
export declare function redactEntryIds(message: string): string;
|
|
133
|
+
export declare function classifyPeerError(error: unknown): ClusterErrorClass;
|
|
134
|
+
/** Per-tenant slice of a peer's replication state. */
|
|
135
|
+
export interface ClusterTenantStatus {
|
|
136
|
+
tenantId: string;
|
|
137
|
+
/** Databases mirrored with this peer, in replication order. */
|
|
138
|
+
databases: string[];
|
|
139
|
+
lastSyncAt: string | null;
|
|
140
|
+
/** Entries the peer still owes us or we owe the peer, by head comparison. */
|
|
141
|
+
lag: number;
|
|
142
|
+
/** Entries rejected and queued for retry (see `entries-awaiting-directory-trust`). */
|
|
143
|
+
retryQueueDepth: number;
|
|
144
|
+
rejectedEntries: number;
|
|
145
|
+
health: ClusterHealth;
|
|
146
|
+
reasons: ClusterHealthReason[];
|
|
147
|
+
}
|
|
148
|
+
/** One peer as this node sees it. */
|
|
149
|
+
export interface ClusterPeerStatus {
|
|
150
|
+
name: string;
|
|
151
|
+
url: string | null;
|
|
152
|
+
direction: PeerSyncDirection;
|
|
153
|
+
role: PeerRole;
|
|
154
|
+
attachments: PeerAttachmentMode;
|
|
155
|
+
state: ClusterSessionState;
|
|
156
|
+
health: ClusterHealth;
|
|
157
|
+
reasons: ClusterHealthReason[];
|
|
158
|
+
/** Tenants both sides hold, from the last Bloom intersection. */
|
|
159
|
+
sharedTenants: number;
|
|
160
|
+
lastIntersectionAt: string | null;
|
|
161
|
+
lastSyncAt: string | null;
|
|
162
|
+
eventsConnected: boolean;
|
|
163
|
+
lag: number;
|
|
164
|
+
retryQueueDepth: number;
|
|
165
|
+
rejectedEntries: number;
|
|
166
|
+
transferredEntries: number;
|
|
167
|
+
transferredBytes: number;
|
|
168
|
+
lastError: string | null;
|
|
169
|
+
lastErrorClass: ClusterErrorClass | null;
|
|
170
|
+
lastErrorAt: string | null;
|
|
171
|
+
}
|
|
172
|
+
/** Summary answer of `GET /system/cluster/status`. */
|
|
173
|
+
export interface ClusterStatusResponse {
|
|
174
|
+
server: string | null;
|
|
175
|
+
health: ClusterHealth;
|
|
176
|
+
reasons: ClusterHealthReason[];
|
|
177
|
+
autoSyncEnabled: boolean;
|
|
178
|
+
generatedAt: string;
|
|
179
|
+
peers: ClusterPeerStatus[];
|
|
180
|
+
}
|
|
181
|
+
/** Drill-down answer of `GET /system/cluster/status/tenants/:tenantId`. */
|
|
182
|
+
export interface ClusterTenantStatusResponse {
|
|
183
|
+
tenantId: string;
|
|
184
|
+
generatedAt: string;
|
|
185
|
+
peers: Array<{
|
|
186
|
+
name: string;
|
|
187
|
+
tenant: ClusterTenantStatus | null;
|
|
188
|
+
}>;
|
|
189
|
+
}
|
|
190
|
+
/** One entry of `GET /system/cluster/topology`. */
|
|
191
|
+
export interface ClusterTopologyPeer {
|
|
192
|
+
name: string;
|
|
193
|
+
url: string | null;
|
|
194
|
+
direction: PeerSyncDirection;
|
|
195
|
+
role: PeerRole;
|
|
196
|
+
attachments: PeerAttachmentMode;
|
|
197
|
+
sessionActive: boolean;
|
|
198
|
+
}
|
|
199
|
+
export interface ClusterTopologyResponse {
|
|
200
|
+
/**
|
|
201
|
+
* This node's own identity and role. The console starts here and then polls
|
|
202
|
+
* every peer directly — no node answers on behalf of another (fan-out).
|
|
203
|
+
*/
|
|
204
|
+
server: {
|
|
205
|
+
name: string | null;
|
|
206
|
+
role: PeerRole;
|
|
207
|
+
};
|
|
208
|
+
peers: ClusterTopologyPeer[];
|
|
209
|
+
}
|
|
210
|
+
export type ClusterJobState = "queued" | "running" | "succeeded" | "failed";
|
|
211
|
+
export type ClusterJobKind = "sync" | "retry-rejected" | "refresh-intersection" | "pause" | "resume";
|
|
212
|
+
export interface ClusterJobScope {
|
|
213
|
+
peer?: string;
|
|
214
|
+
tenantId?: string;
|
|
215
|
+
dbId?: string;
|
|
216
|
+
}
|
|
217
|
+
export interface ClusterJob {
|
|
218
|
+
id: string;
|
|
219
|
+
kind: ClusterJobKind;
|
|
220
|
+
scope: ClusterJobScope;
|
|
221
|
+
state: ClusterJobState;
|
|
222
|
+
createdAt: string;
|
|
223
|
+
startedAt: string | null;
|
|
224
|
+
finishedAt: string | null;
|
|
225
|
+
/** Coarse progress counters; never carries document identity. */
|
|
226
|
+
progress: {
|
|
227
|
+
total: number;
|
|
228
|
+
completed: number;
|
|
229
|
+
};
|
|
230
|
+
result: ClusterJobResult | null;
|
|
231
|
+
error: string | null;
|
|
232
|
+
errorClass: ClusterErrorClass | null;
|
|
233
|
+
/** System-admin principal that requested the job. */
|
|
234
|
+
requestedBy: string;
|
|
235
|
+
}
|
|
236
|
+
export interface ClusterJobResult {
|
|
237
|
+
transferredEntries: number;
|
|
238
|
+
transferredBytes: number;
|
|
239
|
+
/** Count only — the entry ids would leak document identity to the console. */
|
|
240
|
+
rejectedEntries: number;
|
|
241
|
+
tenantsProcessed: number;
|
|
242
|
+
databasesProcessed: number;
|
|
243
|
+
}
|
|
244
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/server/peer/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH;;;;;;;GAOG;AACH,MAAM,MAAM,iBAAiB,GAAG,eAAe,GAAG,MAAM,GAAG,MAAM,GAAG,UAAU,CAAC;AAE/E;;;;;;;;;GASG;AACH,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,CAAC;AAEhD;;;;;;;GAOG;AACH,MAAM,MAAM,kBAAkB,GAAG,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC;AAE5D,qDAAqD;AACrD,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,iBAAiB,CAAC;IAC7B,IAAI,EAAE,QAAQ,CAAC;IACf,WAAW,EAAE,kBAAkB,CAAC;CACjC;AAED,eAAO,MAAM,qBAAqB,EAAE,YAInC,CAAC;AAEF,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,iBAAiB,CAI9E;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,QAAQ,CAE5D;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,kBAAkB,CAEhF;AAED,mEAAmE;AACnE,wBAAgB,qBAAqB,CAAC,GAAG,EAAE;IACzC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,GAAG,YAAY,CAUf;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,GAAG,OAAO,CAE9E;AAED,8CAA8C;AAC9C,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,iBAAiB,GAAG,OAAO,CAEzE;AAED,sCAAsC;AACtC,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,iBAAiB,GAAG,OAAO,CAEzE;AAMD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,MAAM,EAAE,CAoB9E;AAED,6EAA6E;AAC7E,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE5D;AAMD,sDAAsD;AACtD,MAAM,MAAM,mBAAmB,GAC3B,WAAW,GACX,YAAY,GACZ,SAAS,GACT,UAAU,GACV,QAAQ,GACR,aAAa,CAAC;AAElB;;;GAGG;AACH,MAAM,MAAM,aAAa,GAAG,IAAI,GAAG,UAAU,GAAG,SAAS,GAAG,MAAM,CAAC;AAEnE;;;;;;;;GAQG;AACH,MAAM,MAAM,mBAAmB,GAC3B,kCAAkC,GAClC,uBAAuB,GACvB,qBAAqB,GACrB,oBAAoB,GACpB,qBAAqB,GACrB,kBAAkB,GAClB,kBAAkB,GAClB,mBAAmB,GACnB,kBAAkB,GAClB,gBAAgB,CAAC;AAErB,0DAA0D;AAC1D,MAAM,MAAM,iBAAiB,GACzB,MAAM,GACN,SAAS,GACT,eAAe,GACf,UAAU,GACV,UAAU,CAAC;AAEf;;;;;;;;;GASG;AACH;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,iBAAiB,CAgDnE;AAED,sDAAsD;AACtD,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,+DAA+D;IAC/D,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,6EAA6E;IAC7E,GAAG,EAAE,MAAM,CAAC;IACZ,sFAAsF;IACtF,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,aAAa,CAAC;IACtB,OAAO,EAAE,mBAAmB,EAAE,CAAC;CAChC;AAED,qCAAqC;AACrC,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,SAAS,EAAE,iBAAiB,CAAC;IAC7B,IAAI,EAAE,QAAQ,CAAC;IACf,WAAW,EAAE,kBAAkB,CAAC;IAChC,KAAK,EAAE,mBAAmB,CAAC;IAC3B,MAAM,EAAE,aAAa,CAAC;IACtB,OAAO,EAAE,mBAAmB,EAAE,CAAC;IAC/B,iEAAiE;IACjE,aAAa,EAAE,MAAM,CAAC;IACtB,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,eAAe,EAAE,OAAO,CAAC;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,gBAAgB,EAAE,MAAM,CAAC;IACzB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,cAAc,EAAE,iBAAiB,GAAG,IAAI,CAAC;IACzC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,sDAAsD;AACtD,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,aAAa,CAAC;IACtB,OAAO,EAAE,mBAAmB,EAAE,CAAC;IAC/B,eAAe,EAAE,OAAO,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,iBAAiB,EAAE,CAAC;CAC5B;AAED,2EAA2E;AAC3E,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,mBAAmB,GAAG,IAAI,CAAA;KAAE,CAAC,CAAC;CACpE;AAED,mDAAmD;AACnD,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,SAAS,EAAE,iBAAiB,CAAC;IAC7B,IAAI,EAAE,QAAQ,CAAC;IACf,WAAW,EAAE,kBAAkB,CAAC;IAChC,aAAa,EAAE,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,uBAAuB;IACtC;;;OAGG;IACH,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,IAAI,EAAE,QAAQ,CAAA;KAAE,CAAC;IAChD,KAAK,EAAE,mBAAmB,EAAE,CAAC;CAC9B;AAMD,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,SAAS,GAAG,WAAW,GAAG,QAAQ,CAAC;AAE5E,MAAM,MAAM,cAAc,GACtB,MAAM,GACN,gBAAgB,GAChB,sBAAsB,GACtB,OAAO,GACP,QAAQ,CAAC;AAEb,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,cAAc,CAAC;IACrB,KAAK,EAAE,eAAe,CAAC;IACvB,KAAK,EAAE,eAAe,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,iEAAiE;IACjE,QAAQ,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/C,MAAM,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAChC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,UAAU,EAAE,iBAAiB,GAAG,IAAI,CAAC;IACrC,qDAAqD;IACrD,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,gBAAgB,EAAE,MAAM,CAAC;IACzB,8EAA8E;IAC9E,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,kBAAkB,EAAE,MAAM,CAAC;CAC5B"}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Types for server-to-server (peer) replication and the cluster admin surface.
|
|
4
|
+
*
|
|
5
|
+
* Two auth classes, two route prefixes — the split is load-bearing:
|
|
6
|
+
*
|
|
7
|
+
* - `/system/peer/*` is the **server-to-server protocol**. Callers authenticate
|
|
8
|
+
* with a server identity listed in `trusted-servers.json`. Only servers speak
|
|
9
|
+
* it.
|
|
10
|
+
* - `/system/cluster/*` is the **admin surface**. Callers authenticate with a
|
|
11
|
+
* system-admin JWT and pass the {@link CapabilityMatcher}. Only humans and
|
|
12
|
+
* UIs speak it.
|
|
13
|
+
*
|
|
14
|
+
* No endpoint accepts both, so a peer can never administer and an admin can
|
|
15
|
+
* never write as a peer.
|
|
16
|
+
*/
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.DEFAULT_PEER_SETTINGS = void 0;
|
|
19
|
+
exports.isPeerSyncDirection = isPeerSyncDirection;
|
|
20
|
+
exports.isPeerRole = isPeerRole;
|
|
21
|
+
exports.isPeerAttachmentMode = isPeerAttachmentMode;
|
|
22
|
+
exports.normalizePeerSettings = normalizePeerSettings;
|
|
23
|
+
exports.shouldOpenSessionTo = shouldOpenSessionTo;
|
|
24
|
+
exports.directionAllowsPush = directionAllowsPush;
|
|
25
|
+
exports.directionAllowsPull = directionAllowsPull;
|
|
26
|
+
exports.orderDatabasesForReplication = orderDatabasesForReplication;
|
|
27
|
+
exports.isTrustBearingDatabase = isTrustBearingDatabase;
|
|
28
|
+
exports.redactEntryIds = redactEntryIds;
|
|
29
|
+
exports.classifyPeerError = classifyPeerError;
|
|
30
|
+
const types_1 = require("../../core/types");
|
|
31
|
+
exports.DEFAULT_PEER_SETTINGS = {
|
|
32
|
+
direction: "bidirectional",
|
|
33
|
+
role: "peer",
|
|
34
|
+
attachments: "eager",
|
|
35
|
+
};
|
|
36
|
+
function isPeerSyncDirection(value) {
|
|
37
|
+
return (value === "bidirectional" || value === "push" || value === "pull" || value === "disabled");
|
|
38
|
+
}
|
|
39
|
+
function isPeerRole(value) {
|
|
40
|
+
return value === "peer" || value === "hub" || value === "spoke";
|
|
41
|
+
}
|
|
42
|
+
function isPeerAttachmentMode(value) {
|
|
43
|
+
return value === "eager" || value === "lazy" || value === "never";
|
|
44
|
+
}
|
|
45
|
+
/** Fill in defaults for any peer field the operator left unset. */
|
|
46
|
+
function normalizePeerSettings(raw) {
|
|
47
|
+
return {
|
|
48
|
+
direction: isPeerSyncDirection(raw.direction)
|
|
49
|
+
? raw.direction
|
|
50
|
+
: exports.DEFAULT_PEER_SETTINGS.direction,
|
|
51
|
+
role: isPeerRole(raw.role) ? raw.role : exports.DEFAULT_PEER_SETTINGS.role,
|
|
52
|
+
attachments: isPeerAttachmentMode(raw.attachments)
|
|
53
|
+
? raw.attachments
|
|
54
|
+
: exports.DEFAULT_PEER_SETTINGS.attachments,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Should this node open an outbound session to that peer?
|
|
59
|
+
*
|
|
60
|
+
* Spoke-to-spoke is the only pairing we suppress: both sides would otherwise
|
|
61
|
+
* dial each other even though the hub already carries their traffic.
|
|
62
|
+
*/
|
|
63
|
+
function shouldOpenSessionTo(local, remote) {
|
|
64
|
+
return !(local === "spoke" && remote === "spoke");
|
|
65
|
+
}
|
|
66
|
+
/** May we send local entries to this peer? */
|
|
67
|
+
function directionAllowsPush(direction) {
|
|
68
|
+
return direction === "bidirectional" || direction === "push";
|
|
69
|
+
}
|
|
70
|
+
/** May we take the peer's entries? */
|
|
71
|
+
function directionAllowsPull(direction) {
|
|
72
|
+
return direction === "bidirectional" || direction === "pull";
|
|
73
|
+
}
|
|
74
|
+
// =========================================================================
|
|
75
|
+
// Replication ordering (convergence invariant 1)
|
|
76
|
+
// =========================================================================
|
|
77
|
+
/**
|
|
78
|
+
* Order the databases of one tenant for replication.
|
|
79
|
+
*
|
|
80
|
+
* `directory` first, then `userdirectory`, then everything else alphabetically.
|
|
81
|
+
* This is not cosmetic. A peer validates every pushed entry against its own
|
|
82
|
+
* directory (`validatePublicSigningKey`), and an unknown author key is a
|
|
83
|
+
* **per-entry rejection**, not a batch failure. Push an application database
|
|
84
|
+
* before the peer has the author's grant and those entries are refused; with
|
|
85
|
+
* the client's cursor semantics they would then be skipped forever, leaving the
|
|
86
|
+
* two servers silently divergent.
|
|
87
|
+
*
|
|
88
|
+
* The same ordering also shrinks the transient window in which a purged
|
|
89
|
+
* document or a revoked `decryptionKeyId` makes the peer answer `ACCESS_DENIED`
|
|
90
|
+
* for a whole batch: both denylists are derived from the directory (purge
|
|
91
|
+
* requests live there and are executed by `executePendingPurges` after every
|
|
92
|
+
* directory push), so they converge as soon as the directory has arrived.
|
|
93
|
+
*/
|
|
94
|
+
function orderDatabasesForReplication(dbIds) {
|
|
95
|
+
const rest = [];
|
|
96
|
+
let hasDirectory = false;
|
|
97
|
+
let hasUserDirectory = false;
|
|
98
|
+
for (const dbId of dbIds) {
|
|
99
|
+
if (dbId === types_1.DIRECTORY_DB_ID) {
|
|
100
|
+
hasDirectory = true;
|
|
101
|
+
}
|
|
102
|
+
else if (dbId === types_1.USER_DIRECTORY_DB_ID) {
|
|
103
|
+
hasUserDirectory = true;
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
rest.push(dbId);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
rest.sort((left, right) => left.localeCompare(right));
|
|
110
|
+
const ordered = [];
|
|
111
|
+
if (hasDirectory)
|
|
112
|
+
ordered.push(types_1.DIRECTORY_DB_ID);
|
|
113
|
+
if (hasUserDirectory)
|
|
114
|
+
ordered.push(types_1.USER_DIRECTORY_DB_ID);
|
|
115
|
+
return ordered.concat(rest);
|
|
116
|
+
}
|
|
117
|
+
/** True for the two system databases that must replicate before app data. */
|
|
118
|
+
function isTrustBearingDatabase(dbId) {
|
|
119
|
+
return dbId === types_1.DIRECTORY_DB_ID || dbId === types_1.USER_DIRECTORY_DB_ID;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Bucket a replication failure so the console can react without parsing the
|
|
123
|
+
* message.
|
|
124
|
+
*
|
|
125
|
+
* The distinction that matters operationally is `access-denied` versus the
|
|
126
|
+
* rest: it is the peer *deliberately* refusing a batch (a purged document, a
|
|
127
|
+
* revoked key) and it resolves itself once the directory has replicated, so it
|
|
128
|
+
* warrants a backoff and not an alert. Everything else is either a
|
|
129
|
+
* misconfiguration (`auth`) or a genuine outage (`network`).
|
|
130
|
+
*/
|
|
131
|
+
/**
|
|
132
|
+
* Strip document and entry identifiers out of a message before it reaches the
|
|
133
|
+
* cluster status read-model.
|
|
134
|
+
*
|
|
135
|
+
* Rejection messages name the offending entry, and an entry id starts with the
|
|
136
|
+
* document id. The cluster console is a system-admin surface, and a system
|
|
137
|
+
* admin is not supposed to learn which documents a tenant holds — that is the
|
|
138
|
+
* same admin-blindness the encryption enforces, and it would be odd to hand it
|
|
139
|
+
* back through an error string. The classified reason plus the counts are what
|
|
140
|
+
* an operator can act on anyway.
|
|
141
|
+
*/
|
|
142
|
+
function redactEntryIds(message) {
|
|
143
|
+
return message.replace(/[0-9a-f]{16,}/gi, "<redacted>");
|
|
144
|
+
}
|
|
145
|
+
function classifyPeerError(error) {
|
|
146
|
+
const type = error?.type;
|
|
147
|
+
switch (type) {
|
|
148
|
+
case "ACCESS_DENIED":
|
|
149
|
+
// Every 403 reaches the client as USER_REVOKED, whatever the server meant
|
|
150
|
+
// by it. For a peer that is unambiguous: it holds no user identity, so a
|
|
151
|
+
// 403 can only mean the target refuses these particular entries — a purged
|
|
152
|
+
// document, a revoked decryption key. That is a backoff, not a
|
|
153
|
+
// misconfigured trust relationship.
|
|
154
|
+
case "USER_REVOKED":
|
|
155
|
+
return "access-denied";
|
|
156
|
+
case "INVALID_TOKEN":
|
|
157
|
+
case "INVALID_SIGNATURE":
|
|
158
|
+
case "CHALLENGE_EXPIRED":
|
|
159
|
+
return "auth";
|
|
160
|
+
case "NETWORK_ERROR":
|
|
161
|
+
case "RATE_LIMITED":
|
|
162
|
+
return "network";
|
|
163
|
+
case "PAYLOAD_TOO_LARGE":
|
|
164
|
+
case "SERVER_ERROR":
|
|
165
|
+
return "protocol";
|
|
166
|
+
default:
|
|
167
|
+
break;
|
|
168
|
+
}
|
|
169
|
+
const message = (error instanceof Error ? error.message : String(error)).toLowerCase();
|
|
170
|
+
if (message.includes("access_denied") || message.includes("access denied")) {
|
|
171
|
+
return "access-denied";
|
|
172
|
+
}
|
|
173
|
+
if (message.includes("401") ||
|
|
174
|
+
message.includes("403") ||
|
|
175
|
+
message.includes("authentication") ||
|
|
176
|
+
message.includes("token")) {
|
|
177
|
+
return "auth";
|
|
178
|
+
}
|
|
179
|
+
if (message.includes("fetch failed") ||
|
|
180
|
+
message.includes("econnrefused") ||
|
|
181
|
+
message.includes("enotfound") ||
|
|
182
|
+
message.includes("etimedout") ||
|
|
183
|
+
message.includes("socket") ||
|
|
184
|
+
message.includes("network")) {
|
|
185
|
+
return "network";
|
|
186
|
+
}
|
|
187
|
+
return "internal";
|
|
188
|
+
}
|
|
189
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/server/peer/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rate-limit resolution shared by the server's limiter tiers.
|
|
3
|
+
*
|
|
4
|
+
* Kept out of {@link MindooDBServer} so the interaction between the global net
|
|
5
|
+
* and the per-route tiers is testable on its own: it is the part that is easy
|
|
6
|
+
* to get wrong, and getting it wrong is silent — a global cap below a tier cap
|
|
7
|
+
* simply replaces that tier, and the tier's configuration stops meaning
|
|
8
|
+
* anything.
|
|
9
|
+
*/
|
|
10
|
+
import type { ServerRateLimitsConfig } from "./types";
|
|
11
|
+
export interface ResolvedRateLimit {
|
|
12
|
+
windowMs: number;
|
|
13
|
+
max: number;
|
|
14
|
+
}
|
|
15
|
+
export interface ResolvedGlobalRateLimit extends ResolvedRateLimit {
|
|
16
|
+
/** Requests the tiers may spend inside the global window, plus headroom. */
|
|
17
|
+
tierFloor: number;
|
|
18
|
+
/** True when the resolved max rejects traffic a tier would still allow. */
|
|
19
|
+
shadowsTiers: boolean;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Resolve the global limiter against the tiers it is supposed to back up.
|
|
23
|
+
*
|
|
24
|
+
* Without an explicit `rateLimits.global`, the ceiling is derived from the
|
|
25
|
+
* tiers so the net catches only traffic no tier claims. An explicit value is
|
|
26
|
+
* honoured — an operator may want a hard cap — but reported as shadowing so
|
|
27
|
+
* the caller can say so at startup instead of leaving it to be discovered as
|
|
28
|
+
* mysterious 429s.
|
|
29
|
+
*/
|
|
30
|
+
export declare function resolveGlobalRateLimit(configured: ServerRateLimitsConfig["global"], tiers: {
|
|
31
|
+
sync: ResolvedRateLimit;
|
|
32
|
+
auth: ResolvedRateLimit;
|
|
33
|
+
defaultWindowMs: number;
|
|
34
|
+
headroom: number;
|
|
35
|
+
}): ResolvedGlobalRateLimit;
|
|
36
|
+
/**
|
|
37
|
+
* Bucket key for the sync tier.
|
|
38
|
+
*
|
|
39
|
+
* The principal is what makes this usable behind a NAT or a CDN: keyed on the
|
|
40
|
+
* address alone, one client opening several databases spends everyone else's
|
|
41
|
+
* budget. The address stays in the key so that callers we could not
|
|
42
|
+
* authenticate still get separated per source rather than sharing one
|
|
43
|
+
* "anonymous" bucket.
|
|
44
|
+
*/
|
|
45
|
+
export declare function buildSyncRateLimitKey(tenantId: string | undefined, principal: string | undefined, ip: string | undefined): string;
|
|
46
|
+
/**
|
|
47
|
+
* Rate-limit defaults. Auth and sync have separate tiers because a single
|
|
48
|
+
* challenge/authenticate handshake is two requests; with multiple tenants,
|
|
49
|
+
* store kinds, and token renewals the auth budget exhausts quickly if set
|
|
50
|
+
* too low. These can be overridden in config.json under `rateLimits.auth`
|
|
51
|
+
* and `rateLimits.sync`.
|
|
52
|
+
*/
|
|
53
|
+
export declare const DEFAULT_AUTH_RATE_LIMIT_WINDOW_MS = 60000;
|
|
54
|
+
export declare const DEFAULT_AUTH_RATE_LIMIT_MAX = 120;
|
|
55
|
+
export declare const DEFAULT_SYNC_RATE_LIMIT_WINDOW_MS = 60000;
|
|
56
|
+
/**
|
|
57
|
+
* Sync is keyed per authenticated principal, and a client legitimately opens
|
|
58
|
+
* several databases at once: a host like Teacher's Desk reads six, and a cold
|
|
59
|
+
* pass over one of them costs a few hundred batched requests. A budget sized
|
|
60
|
+
* for a single database turns that normal open into a throttled one.
|
|
61
|
+
*/
|
|
62
|
+
export declare const DEFAULT_SYNC_RATE_LIMIT_MAX = 3000;
|
|
63
|
+
/**
|
|
64
|
+
* The global limiter is a coarse net for unauthenticated floods and unknown
|
|
65
|
+
* routes; the per-tier limiters are the real budgets. It runs first, so a
|
|
66
|
+
* global max below a tier max silently replaces that tier — the sync budget
|
|
67
|
+
* was unreachable while this was a hardcoded 500. The default is therefore
|
|
68
|
+
* derived from the tiers, and an override that would shadow one is honoured
|
|
69
|
+
* but flagged at startup rather than quietly applied.
|
|
70
|
+
*/
|
|
71
|
+
export declare const DEFAULT_GLOBAL_RATE_LIMIT_WINDOW_MS = 60000;
|
|
72
|
+
export declare const GLOBAL_RATE_LIMIT_TIER_HEADROOM = 500;
|
|
73
|
+
/**
|
|
74
|
+
* Timestamping gets its own budget rather than sharing the sync one. Sync is
|
|
75
|
+
* chatty and high-volume; sealing a document is a deliberate, rare user action.
|
|
76
|
+
* Sharing a bucket would let ordinary sync traffic starve the seal endpoint, and
|
|
77
|
+
* a runaway seal loop degrade sync.
|
|
78
|
+
*
|
|
79
|
+
* The daily cap matters more than the burst cap: proxying collapses every user
|
|
80
|
+
* onto the server's IP, so one tenant can exhaust an upstream's per-IP allowance
|
|
81
|
+
* (open-tsa.eu publishes 60/minute) and lock out everyone else on this server.
|
|
82
|
+
*/
|
|
83
|
+
export declare const DEFAULT_TIMESTAMP_RATE_LIMIT_WINDOW_MS = 60000;
|
|
84
|
+
export declare const DEFAULT_TIMESTAMP_RATE_LIMIT_MAX = 10;
|
|
85
|
+
export declare const DEFAULT_TIMESTAMP_DAILY_LIMIT_MAX = 500;
|
|
86
|
+
export declare const TIMESTAMP_DAILY_WINDOW_MS: number;
|
|
87
|
+
/** `/system/*` admin routes: aggressive per-IP throttle. */
|
|
88
|
+
export declare const DEFAULT_SYSTEM_RATE_LIMIT_WINDOW_MS = 60000;
|
|
89
|
+
export declare const DEFAULT_SYSTEM_RATE_LIMIT_MAX = 30;
|
|
90
|
+
/** Device discovery scans every local tenant per request. */
|
|
91
|
+
export declare const DEVICE_DISCOVERY_RATE_LIMIT: ResolvedRateLimit;
|
|
92
|
+
/** Server-to-server peer routes; replication must not crawl. */
|
|
93
|
+
export declare const PEER_RATE_LIMIT: ResolvedRateLimit;
|
|
94
|
+
/** Every limiter tier of a server, resolved against `config.json`. */
|
|
95
|
+
export interface ServerRateLimitPolicy {
|
|
96
|
+
global: ResolvedGlobalRateLimit;
|
|
97
|
+
auth: ResolvedRateLimit;
|
|
98
|
+
sync: ResolvedRateLimit;
|
|
99
|
+
timestamp: ResolvedRateLimit & {
|
|
100
|
+
dailyMax: number;
|
|
101
|
+
};
|
|
102
|
+
system: ResolvedRateLimit;
|
|
103
|
+
device: ResolvedRateLimit;
|
|
104
|
+
peer: ResolvedRateLimit;
|
|
105
|
+
}
|
|
106
|
+
export declare function resolveServerRateLimits(rateLimits: ServerRateLimitsConfig | undefined): ServerRateLimitPolicy;
|
|
107
|
+
/**
|
|
108
|
+
* Composite key for the auth tier. Challenge requests include the username so
|
|
109
|
+
* that brute-forcing one account doesn't block legitimate users on the same
|
|
110
|
+
* IP. Authenticate requests omit username because the challenge token is
|
|
111
|
+
* already scoped to a single principal.
|
|
112
|
+
*/
|
|
113
|
+
export declare function buildAuthRateLimitKey(scope: "system" | "tenant", phase: "challenge" | "authenticate", tenantId: string | undefined, clientIp: string, body: {
|
|
114
|
+
username?: unknown;
|
|
115
|
+
signingPublicKey?: unknown;
|
|
116
|
+
} | undefined): string;
|
|
117
|
+
/** Bucket key of the timestamp tiers: tenant + principal + IP. */
|
|
118
|
+
export declare function buildTimestampRateLimitKey(tier: "burst" | "daily", tenantId: string | undefined, principal: string | undefined, clientIp: string): string;
|
|
119
|
+
//# sourceMappingURL=rateLimits.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rateLimits.d.ts","sourceRoot":"","sources":["../../src/server/rateLimits.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAC;AAEtD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,uBAAwB,SAAQ,iBAAiB;IAChE,4EAA4E;IAC5E,SAAS,EAAE,MAAM,CAAC;IAClB,2EAA2E;IAC3E,YAAY,EAAE,OAAO,CAAC;CACvB;AAED;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,sBAAsB,CAAC,QAAQ,CAAC,EAC5C,KAAK,EAAE;IACL,IAAI,EAAE,iBAAiB,CAAC;IACxB,IAAI,EAAE,iBAAiB,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;CAClB,GACA,uBAAuB,CAiBzB;AAED;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,EAAE,EAAE,MAAM,GAAG,SAAS,GACrB,MAAM,CAER;AAED;;;;;;GAMG;AACH,eAAO,MAAM,iCAAiC,QAAS,CAAC;AACxD,eAAO,MAAM,2BAA2B,MAAM,CAAC;AAC/C,eAAO,MAAM,iCAAiC,QAAS,CAAC;AACxD;;;;;GAKG;AACH,eAAO,MAAM,2BAA2B,OAAQ,CAAC;AAEjD;;;;;;;GAOG;AACH,eAAO,MAAM,mCAAmC,QAAS,CAAC;AAC1D,eAAO,MAAM,+BAA+B,MAAM,CAAC;AAEnD;;;;;;;;;GASG;AACH,eAAO,MAAM,sCAAsC,QAAS,CAAC;AAC7D,eAAO,MAAM,gCAAgC,KAAK,CAAC;AACnD,eAAO,MAAM,iCAAiC,MAAM,CAAC;AACrD,eAAO,MAAM,yBAAyB,QAAsB,CAAC;AAE7D,4DAA4D;AAC5D,eAAO,MAAM,mCAAmC,QAAS,CAAC;AAC1D,eAAO,MAAM,6BAA6B,KAAK,CAAC;AAEhD,6DAA6D;AAC7D,eAAO,MAAM,2BAA2B,EAAE,iBAAiD,CAAC;AAE5F,gEAAgE;AAChE,eAAO,MAAM,eAAe,EAAE,iBAAkD,CAAC;AAEjF,sEAAsE;AACtE,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,uBAAuB,CAAC;IAChC,IAAI,EAAE,iBAAiB,CAAC;IACxB,IAAI,EAAE,iBAAiB,CAAC;IACxB,SAAS,EAAE,iBAAiB,GAAG;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IACpD,MAAM,EAAE,iBAAiB,CAAC;IAC1B,MAAM,EAAE,iBAAiB,CAAC;IAC1B,IAAI,EAAE,iBAAiB,CAAC;CACzB;AAED,wBAAgB,uBAAuB,CACrC,UAAU,EAAE,sBAAsB,GAAG,SAAS,GAC7C,qBAAqB,CA+BvB;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,QAAQ,GAAG,QAAQ,EAC1B,KAAK,EAAE,WAAW,GAAG,cAAc,EACnC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,gBAAgB,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,SAAS,GACnE,MAAM,CAgBR;AAED,kEAAkE;AAClE,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,OAAO,GAAG,OAAO,EACvB,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,QAAQ,EAAE,MAAM,GACf,MAAM,CAER"}
|