mindoodb 0.0.53 → 0.0.55
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 +125 -69
- package/dist/appendonlystores/network/ClientNetworkContentAddressedStore.d.ts +17 -0
- package/dist/appendonlystores/network/ClientNetworkContentAddressedStore.d.ts.map +1 -1
- package/dist/appendonlystores/network/ClientNetworkContentAddressedStore.js +22 -0
- package/dist/appendonlystores/network/ClientNetworkContentAddressedStore.js.map +1 -1
- package/dist/appendonlystores/network/ServerNetworkContentAddressedStore.d.ts +58 -0
- package/dist/appendonlystores/network/ServerNetworkContentAddressedStore.d.ts.map +1 -1
- package/dist/appendonlystores/network/ServerNetworkContentAddressedStore.js +156 -3
- package/dist/appendonlystores/network/ServerNetworkContentAddressedStore.js.map +1 -1
- package/dist/browser/index.mjs +2 -0
- package/dist/core/BaseMindooDB.d.ts +73 -74
- package/dist/core/BaseMindooDB.d.ts.map +1 -1
- package/dist/core/BaseMindooDB.js +263 -509
- package/dist/core/BaseMindooDB.js.map +1 -1
- package/dist/core/MindooDBServerAdmin.d.ts +44 -22
- package/dist/core/MindooDBServerAdmin.d.ts.map +1 -1
- package/dist/core/MindooDBServerAdmin.js +45 -21
- package/dist/core/MindooDBServerAdmin.js.map +1 -1
- package/dist/core/appendonlystores/syncStores.d.ts +147 -0
- package/dist/core/appendonlystores/syncStores.d.ts.map +1 -0
- package/dist/core/appendonlystores/syncStores.js +612 -0
- package/dist/core/appendonlystores/syncStores.js.map +1 -0
- package/dist/core/builtinDbInvariants.d.ts +30 -0
- package/dist/core/builtinDbInvariants.d.ts.map +1 -1
- package/dist/core/builtinDbInvariants.js +97 -0
- package/dist/core/builtinDbInvariants.js.map +1 -1
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +7 -4
- package/dist/core/index.js.map +1 -1
- package/dist/core/types.d.ts +42 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/node/server/CapabilityMatcher.d.ts +2 -2
- package/dist/node/server/CapabilityMatcher.js +2 -2
- package/dist/node/server/MindooDBServer.d.ts +14 -1
- package/dist/node/server/MindooDBServer.d.ts.map +1 -1
- package/dist/node/server/MindooDBServer.js +166 -159
- package/dist/node/server/MindooDBServer.js.map +1 -1
- package/dist/node/server/SyncEventBus.d.ts +10 -0
- package/dist/node/server/SyncEventBus.d.ts.map +1 -1
- package/dist/node/server/SyncEventBus.js.map +1 -1
- package/dist/node/server/TenantManager.d.ts +58 -6
- package/dist/node/server/TenantManager.d.ts.map +1 -1
- package/dist/node/server/TenantManager.js +90 -65
- package/dist/node/server/TenantManager.js.map +1 -1
- package/dist/node/server/add-to-network.d.ts.map +1 -1
- package/dist/node/server/add-to-network.js +33 -15
- package/dist/node/server/add-to-network.js.map +1 -1
- package/dist/node/server/canonicalSetupName.d.ts +22 -0
- package/dist/node/server/canonicalSetupName.d.ts.map +1 -0
- package/dist/node/server/canonicalSetupName.js +38 -0
- package/dist/node/server/canonicalSetupName.js.map +1 -0
- package/dist/node/server/config.d.ts.map +1 -1
- package/dist/node/server/config.js +33 -1
- package/dist/node/server/config.js.map +1 -1
- package/dist/node/server/peer/ClusterAuditLog.d.ts +64 -0
- package/dist/node/server/peer/ClusterAuditLog.d.ts.map +1 -0
- package/dist/node/server/peer/ClusterAuditLog.js +99 -0
- package/dist/node/server/peer/ClusterAuditLog.js.map +1 -0
- package/dist/node/server/peer/ClusterJobRunner.d.ts +43 -0
- package/dist/node/server/peer/ClusterJobRunner.d.ts.map +1 -0
- package/dist/node/server/peer/ClusterJobRunner.js +119 -0
- package/dist/node/server/peer/ClusterJobRunner.js.map +1 -0
- package/dist/node/server/peer/ClusterManager.d.ts +116 -0
- package/dist/node/server/peer/ClusterManager.d.ts.map +1 -0
- package/dist/node/server/peer/ClusterManager.js +274 -0
- package/dist/node/server/peer/ClusterManager.js.map +1 -0
- package/dist/node/server/peer/PeerAuthService.d.ts +69 -0
- package/dist/node/server/peer/PeerAuthService.d.ts.map +1 -0
- package/dist/node/server/peer/PeerAuthService.js +208 -0
- package/dist/node/server/peer/PeerAuthService.js.map +1 -0
- package/dist/node/server/peer/PeerReplicator.d.ts +232 -0
- package/dist/node/server/peer/PeerReplicator.d.ts.map +1 -0
- package/dist/node/server/peer/PeerReplicator.js +879 -0
- package/dist/node/server/peer/PeerReplicator.js.map +1 -0
- package/dist/node/server/peer/routes.d.ts +66 -0
- package/dist/node/server/peer/routes.d.ts.map +1 -0
- package/dist/node/server/peer/routes.js +322 -0
- package/dist/node/server/peer/routes.js.map +1 -0
- package/dist/node/server/peer/types.d.ts +244 -0
- package/dist/node/server/peer/types.d.ts.map +1 -0
- package/dist/node/server/peer/types.js +189 -0
- package/dist/node/server/peer/types.js.map +1 -0
- package/dist/node/server/server.d.ts +1 -1
- package/dist/node/server/server.js +18 -37
- package/dist/node/server/server.js.map +1 -1
- package/dist/node/server/serverinit.d.ts +2 -1
- package/dist/node/server/serverinit.d.ts.map +1 -1
- package/dist/node/server/serverinit.js +34 -12
- package/dist/node/server/serverinit.js.map +1 -1
- package/dist/node/server/types.d.ts +42 -30
- package/dist/node/server/types.d.ts.map +1 -1
- package/dist/node/server/types.js +1 -7
- package/dist/node/server/types.js.map +1 -1
- package/dist/node/server/validation.d.ts +0 -6
- package/dist/node/server/validation.d.ts.map +1 -1
- package/dist/node/server/validation.js +1 -7
- package/dist/node/server/validation.js.map +1 -1
- package/package.json +1 -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/node/server/peer/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -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/node/server/peer/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* Options:
|
|
10
10
|
* -d, --data-dir <path> Data directory path (default: ./data)
|
|
11
11
|
* -p, --port <port> Server port (default: 1661)
|
|
12
|
-
* -s, --auto-sync
|
|
12
|
+
* -s, --auto-sync Replicate with the peers in trusted-servers.json
|
|
13
13
|
* -w, --static-dir <path> Serve static files from this directory at /statics/
|
|
14
14
|
* --tls-cert <path> Path to TLS certificate file (PEM)
|
|
15
15
|
* --tls-key <path> Path to TLS private key file (PEM)
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* Options:
|
|
11
11
|
* -d, --data-dir <path> Data directory path (default: ./data)
|
|
12
12
|
* -p, --port <port> Server port (default: 1661)
|
|
13
|
-
* -s, --auto-sync
|
|
13
|
+
* -s, --auto-sync Replicate with the peers in trusted-servers.json
|
|
14
14
|
* -w, --static-dir <path> Serve static files from this directory at /statics/
|
|
15
15
|
* --tls-cert <path> Path to TLS certificate file (PEM)
|
|
16
16
|
* --tls-key <path> Path to TLS private key file (PEM)
|
|
@@ -21,11 +21,9 @@
|
|
|
21
21
|
*/
|
|
22
22
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
23
|
const MindooDBServer_1 = require("./MindooDBServer");
|
|
24
|
-
const types_1 = require("../../core/types");
|
|
25
|
-
const ServerSync_1 = require("./ServerSync");
|
|
26
24
|
const config_1 = require("./config");
|
|
27
25
|
const resolveServerPassword_1 = require("./resolveServerPassword");
|
|
28
|
-
const
|
|
26
|
+
const types_1 = require("./types");
|
|
29
27
|
function parseArgs(args) {
|
|
30
28
|
const options = {
|
|
31
29
|
dataDir: "./data",
|
|
@@ -111,7 +109,8 @@ Usage:
|
|
|
111
109
|
Options:
|
|
112
110
|
-d, --data-dir <path> Data directory path (default: ./data)
|
|
113
111
|
-p, --port <port> Server port (default: 1661)
|
|
114
|
-
-s, --auto-sync
|
|
112
|
+
-s, --auto-sync Replicate with every peer in trusted-servers.json
|
|
113
|
+
that has a "url" (see README-server.md, Clustering)
|
|
115
114
|
-w, --static-dir <path> Serve static files at /statics/ (e.g. bootstrap UI)
|
|
116
115
|
--config <path> Path to config.json (default: <dataDir>/config.json)
|
|
117
116
|
--tls-cert <path> Path to TLS certificate file (PEM format)
|
|
@@ -129,7 +128,7 @@ Examples:
|
|
|
129
128
|
# Start server with custom data directory and port
|
|
130
129
|
npm run server:dev -- -d /var/lib/mindoodb -p 8080
|
|
131
130
|
|
|
132
|
-
# Start server with
|
|
131
|
+
# Start server as a cluster node, replicating with its trusted peers
|
|
133
132
|
MINDOODB_SERVER_PASSWORD_FILE=./.server-password npm run server:dev -- -s
|
|
134
133
|
|
|
135
134
|
# Start server with explicit config file
|
|
@@ -170,45 +169,27 @@ async function main() {
|
|
|
170
169
|
const serverConfig = (0, config_1.loadServerConfig)(options.dataDir, options.configPath);
|
|
171
170
|
// Create and start the server
|
|
172
171
|
const server = new MindooDBServer_1.MindooDBServer(options.dataDir, serverPassword, options.staticDir, serverConfig, configPath);
|
|
173
|
-
//
|
|
174
|
-
|
|
172
|
+
// Peer replication is cluster-wide, not per tenant: one session per trusted
|
|
173
|
+
// server with a `url`, and every tenant both sides hold is mirrored over it.
|
|
174
|
+
if (options.autoSync || serverConfig.cluster?.autoSync) {
|
|
175
175
|
const tenantManager = server.getTenantManager();
|
|
176
|
-
|
|
177
|
-
if (!serverIdentity) {
|
|
176
|
+
if (!tenantManager.getServerIdentity()) {
|
|
178
177
|
console.warn("[Main] Auto-sync enabled but no server.identity.json found. " +
|
|
179
178
|
"Run 'npm run init' to create a server identity.");
|
|
180
179
|
}
|
|
181
180
|
else if (!serverPassword) {
|
|
182
181
|
console.warn("[Main] Auto-sync enabled but no server password (set " +
|
|
183
|
-
`${
|
|
184
|
-
"
|
|
182
|
+
`${types_1.ENV_VARS.SERVER_PASSWORD} or ${types_1.ENV_VARS.SERVER_PASSWORD_FILE}). ` +
|
|
183
|
+
"Peer replication needs it to sign challenges and unwrap peer entries.");
|
|
185
184
|
}
|
|
186
185
|
else {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
const stopSync = (0, ServerSync_1.startPeriodicSync)(serverSync, config.remoteServers);
|
|
195
|
-
process.on("SIGINT", () => {
|
|
196
|
-
console.log("\n[Main] Shutting down...");
|
|
197
|
-
stopSync();
|
|
198
|
-
process.exit(0);
|
|
199
|
-
});
|
|
200
|
-
process.on("SIGTERM", () => {
|
|
201
|
-
console.log("\n[Main] Shutting down...");
|
|
202
|
-
stopSync();
|
|
203
|
-
process.exit(0);
|
|
204
|
-
});
|
|
205
|
-
console.log(`[Main] Started auto-sync for tenant ${tenantId}`);
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
catch (error) {
|
|
209
|
-
console.error(`[Main] Error setting up auto-sync for tenant ${tenantId}:`, error);
|
|
210
|
-
}
|
|
211
|
-
}
|
|
186
|
+
server.startCluster();
|
|
187
|
+
const shutdown = () => {
|
|
188
|
+
console.log("\n[Main] Shutting down...");
|
|
189
|
+
void server.stopCluster().finally(() => process.exit(0));
|
|
190
|
+
};
|
|
191
|
+
process.on("SIGINT", shutdown);
|
|
192
|
+
process.on("SIGTERM", shutdown);
|
|
212
193
|
}
|
|
213
194
|
}
|
|
214
195
|
// Start listening
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../../src/node/server/server.ts"],"names":[],"mappings":";;AACA;;;;;;;;;;;;;;;;;;GAkBG;;AAEH,qDAAkD;AAClD,
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../../src/node/server/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
* npx ts-node src/serverinit.ts --name <serverName> [options]
|
|
8
8
|
*
|
|
9
9
|
* Options:
|
|
10
|
-
* -n, --name <name> Server name
|
|
10
|
+
* -n, --name <name> Server name, canonical or abbreviated
|
|
11
|
+
* (e.g. "cn=server1/o=acme" or "server1/acme") -- required
|
|
11
12
|
* -d, --data-dir <path> Data directory path (default: ./data)
|
|
12
13
|
* -f, --force Overwrite existing server.identity.json
|
|
13
14
|
* --skip-admin Skip system admin keypair generation
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serverinit.d.ts","sourceRoot":"","sources":["../../../src/node/server/serverinit.ts"],"names":[],"mappings":";AACA
|
|
1
|
+
{"version":3,"file":"serverinit.d.ts","sourceRoot":"","sources":["../../../src/node/server/serverinit.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;GAkBG"}
|