@viewportai/daemon 0.5.2 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/commands.d.ts +1 -0
- package/dist/cli/commands.d.ts.map +1 -1
- package/dist/cli/commands.js +1 -0
- package/dist/cli/commands.js.map +1 -1
- package/dist/cli/context-access-command.d.ts +0 -6
- package/dist/cli/context-access-command.d.ts.map +1 -1
- package/dist/cli/context-access-command.js +1 -71
- package/dist/cli/context-access-command.js.map +1 -1
- package/dist/cli/context-command.d.ts.map +1 -1
- package/dist/cli/context-command.js +593 -27
- package/dist/cli/context-command.js.map +1 -1
- package/dist/cli/context-sync-target.d.ts +2 -1
- package/dist/cli/context-sync-target.d.ts.map +1 -1
- package/dist/cli/context-sync-target.js +28 -0
- package/dist/cli/context-sync-target.js.map +1 -1
- package/dist/cli/context-vault-metadata-command.d.ts.map +1 -1
- package/dist/cli/context-vault-metadata-command.js +6 -1
- package/dist/cli/context-vault-metadata-command.js.map +1 -1
- package/dist/cli/lifecycle-commands.d.ts.map +1 -1
- package/dist/cli/lifecycle-commands.js +6 -6
- package/dist/cli/lifecycle-commands.js.map +1 -1
- package/dist/cli/unlock-command.d.ts +2 -0
- package/dist/cli/unlock-command.d.ts.map +1 -0
- package/dist/cli/unlock-command.js +35 -0
- package/dist/cli/unlock-command.js.map +1 -0
- package/dist/context/local-edge-store.d.ts +23 -1
- package/dist/context/local-edge-store.d.ts.map +1 -1
- package/dist/context/local-edge-store.js +51 -0
- package/dist/context/local-edge-store.js.map +1 -1
- package/dist/context/local-edge-sync.d.ts +63 -0
- package/dist/context/local-edge-sync.d.ts.map +1 -1
- package/dist/context/local-edge-sync.js +464 -4
- package/dist/context/local-edge-sync.js.map +1 -1
- package/dist/context/local-edge-types.d.ts +21 -0
- package/dist/context/local-edge-types.d.ts.map +1 -1
- package/dist/hooks/platform-plan-sync.d.ts +4 -1
- package/dist/hooks/platform-plan-sync.d.ts.map +1 -1
- package/dist/hooks/platform-plan-sync.js +20 -5
- package/dist/hooks/platform-plan-sync.js.map +1 -1
- package/dist/hooks/trusted-edge-plan-artifacts.d.ts +117 -0
- package/dist/hooks/trusted-edge-plan-artifacts.d.ts.map +1 -0
- package/dist/hooks/trusted-edge-plan-artifacts.js +371 -0
- package/dist/hooks/trusted-edge-plan-artifacts.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/relay/bridge-token-issuer.d.ts +1 -0
- package/dist/relay/bridge-token-issuer.d.ts.map +1 -1
- package/dist/relay/bridge-token-issuer.js +1 -1
- package/dist/relay/bridge-token-issuer.js.map +1 -1
- package/dist/security/epoch-enrollment.d.ts +48 -0
- package/dist/security/epoch-enrollment.d.ts.map +1 -0
- package/dist/security/epoch-enrollment.js +290 -0
- package/dist/security/epoch-enrollment.js.map +1 -0
- package/dist/security/epoch-protocol.d.ts +181 -0
- package/dist/security/epoch-protocol.d.ts.map +1 -0
- package/dist/security/epoch-protocol.js +285 -0
- package/dist/security/epoch-protocol.js.map +1 -0
- package/dist/security/epoch-public-pins.d.ts +19 -0
- package/dist/security/epoch-public-pins.d.ts.map +1 -0
- package/dist/security/epoch-public-pins.js +129 -0
- package/dist/security/epoch-public-pins.js.map +1 -0
- package/dist/security/epoch-recovery.d.ts +56 -0
- package/dist/security/epoch-recovery.d.ts.map +1 -0
- package/dist/security/epoch-recovery.js +314 -0
- package/dist/security/epoch-recovery.js.map +1 -0
- package/dist/security/epoch-store.d.ts +111 -0
- package/dist/security/epoch-store.d.ts.map +1 -0
- package/dist/security/epoch-store.js +224 -0
- package/dist/security/epoch-store.js.map +1 -0
- package/dist/security/epoch-sync.d.ts +47 -0
- package/dist/security/epoch-sync.d.ts.map +1 -0
- package/dist/security/epoch-sync.js +371 -0
- package/dist/security/epoch-sync.js.map +1 -0
- package/dist/security/team-epoch-grants.d.ts +28 -0
- package/dist/security/team-epoch-grants.d.ts.map +1 -0
- package/dist/security/team-epoch-grants.js +256 -0
- package/dist/security/team-epoch-grants.js.map +1 -0
- package/dist/server/context-preview-service.d.ts +26 -0
- package/dist/server/context-preview-service.d.ts.map +1 -0
- package/dist/server/context-preview-service.js +71 -0
- package/dist/server/context-preview-service.js.map +1 -0
- package/dist/server/http-context-routes.d.ts +2 -1
- package/dist/server/http-context-routes.d.ts.map +1 -1
- package/dist/server/http-context-routes.js +65 -30
- package/dist/server/http-context-routes.js.map +1 -1
- package/dist/server/http-server.js +1 -1
- package/dist/server/http-server.js.map +1 -1
- package/dist/server/rate-limiter.d.ts.map +1 -1
- package/dist/server/rate-limiter.js +6 -1
- package/dist/server/rate-limiter.js.map +1 -1
- package/dist/server/trusted-edge-command-capability.d.ts +14 -0
- package/dist/server/trusted-edge-command-capability.d.ts.map +1 -0
- package/dist/server/trusted-edge-command-capability.js +114 -0
- package/dist/server/trusted-edge-command-capability.js.map +1 -0
- package/dist/server/ws-command-handlers.d.ts.map +1 -1
- package/dist/server/ws-command-handlers.js +231 -27
- package/dist/server/ws-command-handlers.js.map +1 -1
- package/dist/server/ws-protocol.d.ts +419 -5
- package/dist/server/ws-protocol.d.ts.map +1 -1
- package/dist/server/ws-protocol.js +141 -4
- package/dist/server/ws-protocol.js.map +1 -1
- package/docs/protocol-matrix.json +93 -5
- package/node_modules/@viewportai/context-engine/src/repo/materializer.js +20 -5
- package/node_modules/@viewportai/context-engine/src/repo/membership.js +15 -0
- package/node_modules/@viewportai/context-engine/src/repo/sync.js +4 -4
- package/node_modules/@viewportai/context-engine/src/repo/vault.js +8 -3
- package/package.json +1 -1
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import crypto from 'node:crypto';
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { configDir } from '../core/config.js';
|
|
5
|
+
import { TEAM_EPOCH_SCHEMA, USER_EPOCH_SCHEMA, } from './epoch-protocol.js';
|
|
6
|
+
const STORE_SCHEMA = 'viewport.local_crypto_epochs/v1';
|
|
7
|
+
export async function getActiveLocalUserEpoch(workspaceId, home = configDir()) {
|
|
8
|
+
const store = await readLocalEpochStore(home);
|
|
9
|
+
return (store.userEpochs
|
|
10
|
+
.filter((epoch) => epoch.workspaceId === workspaceId && epoch.status === 'active')
|
|
11
|
+
.sort((a, b) => b.epoch - a.epoch)[0] ?? null);
|
|
12
|
+
}
|
|
13
|
+
export async function getActiveLocalTeamEpoch(workspaceId, teamId, home = configDir()) {
|
|
14
|
+
const store = await readLocalEpochStore(home);
|
|
15
|
+
return (store.teamEpochs
|
|
16
|
+
.filter((epoch) => epoch.workspaceId === workspaceId &&
|
|
17
|
+
(epoch.teamId === teamId || epoch.platformTeamId === teamId) &&
|
|
18
|
+
epoch.status === 'active')
|
|
19
|
+
.sort((a, b) => b.epoch - a.epoch)[0] ?? null);
|
|
20
|
+
}
|
|
21
|
+
export async function listActiveLocalTeamEpochs(workspaceId, home = configDir()) {
|
|
22
|
+
const store = await readLocalEpochStore(home);
|
|
23
|
+
return store.teamEpochs
|
|
24
|
+
.filter((epoch) => epoch.workspaceId === workspaceId && epoch.status === 'active')
|
|
25
|
+
.sort((a, b) => b.epoch - a.epoch);
|
|
26
|
+
}
|
|
27
|
+
export async function getLocalTeamEpochByPlatformId(workspaceId, platformEpochId, home = configDir()) {
|
|
28
|
+
const store = await readLocalEpochStore(home);
|
|
29
|
+
return (store.teamEpochs.find((epoch) => epoch.workspaceId === workspaceId &&
|
|
30
|
+
epoch.platformEpochId === platformEpochId &&
|
|
31
|
+
epoch.status === 'active') ?? null);
|
|
32
|
+
}
|
|
33
|
+
export async function getLocalUserEpochByPlatformId(workspaceId, platformEpochId, home = configDir()) {
|
|
34
|
+
const store = await readLocalEpochStore(home);
|
|
35
|
+
return (store.userEpochs.find((epoch) => epoch.workspaceId === workspaceId &&
|
|
36
|
+
epoch.platformEpochId === platformEpochId &&
|
|
37
|
+
epoch.status === 'active') ?? null);
|
|
38
|
+
}
|
|
39
|
+
export async function getLocalDeviceEnrollment(workspaceId, enrollmentIdOrFingerprint, home = configDir()) {
|
|
40
|
+
const store = await readLocalEpochStore(home);
|
|
41
|
+
return (store.deviceEnrollments.find((enrollment) => enrollment.workspaceId === workspaceId &&
|
|
42
|
+
(enrollment.enrollmentId === enrollmentIdOrFingerprint ||
|
|
43
|
+
enrollment.fingerprint === enrollmentIdOrFingerprint)) ?? null);
|
|
44
|
+
}
|
|
45
|
+
export async function upsertLocalUserEpoch(input, home = configDir()) {
|
|
46
|
+
const store = await readLocalEpochStore(home);
|
|
47
|
+
const now = new Date().toISOString();
|
|
48
|
+
for (const epoch of store.userEpochs) {
|
|
49
|
+
if (epoch.workspaceId === input.workspaceId && epoch.status === 'active') {
|
|
50
|
+
epoch.status = 'superseded';
|
|
51
|
+
epoch.updatedAt = now;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
const existing = store.userEpochs.find((epoch) => epoch.workspaceId === input.workspaceId && epoch.fingerprint === input.fingerprint);
|
|
55
|
+
if (existing) {
|
|
56
|
+
Object.assign(existing, input, { updatedAt: now });
|
|
57
|
+
await writeLocalEpochStore(store, home);
|
|
58
|
+
return existing;
|
|
59
|
+
}
|
|
60
|
+
const record = { ...input, createdAt: now, updatedAt: now };
|
|
61
|
+
store.userEpochs.push(record);
|
|
62
|
+
await writeLocalEpochStore(store, home);
|
|
63
|
+
return record;
|
|
64
|
+
}
|
|
65
|
+
export async function upsertLocalTeamEpoch(input, home = configDir()) {
|
|
66
|
+
const store = await readLocalEpochStore(home);
|
|
67
|
+
const now = new Date().toISOString();
|
|
68
|
+
for (const epoch of store.teamEpochs) {
|
|
69
|
+
if (epoch.workspaceId === input.workspaceId &&
|
|
70
|
+
(epoch.teamId === input.teamId || epoch.platformTeamId === input.platformTeamId) &&
|
|
71
|
+
epoch.status === 'active') {
|
|
72
|
+
epoch.status = 'superseded';
|
|
73
|
+
epoch.updatedAt = now;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
const existing = store.teamEpochs.find((epoch) => epoch.workspaceId === input.workspaceId && epoch.fingerprint === input.fingerprint);
|
|
77
|
+
if (existing) {
|
|
78
|
+
Object.assign(existing, input, { updatedAt: now });
|
|
79
|
+
await writeLocalEpochStore(store, home);
|
|
80
|
+
return existing;
|
|
81
|
+
}
|
|
82
|
+
const record = { ...input, createdAt: now, updatedAt: now };
|
|
83
|
+
store.teamEpochs.push(record);
|
|
84
|
+
await writeLocalEpochStore(store, home);
|
|
85
|
+
return record;
|
|
86
|
+
}
|
|
87
|
+
export async function upsertLocalDeviceEnrollment(input, home = configDir()) {
|
|
88
|
+
const store = await readLocalEpochStore(home);
|
|
89
|
+
const now = new Date().toISOString();
|
|
90
|
+
const existing = store.deviceEnrollments.find((enrollment) => enrollment.workspaceId === input.workspaceId &&
|
|
91
|
+
(enrollment.fingerprint === input.fingerprint ||
|
|
92
|
+
(input.enrollmentId && enrollment.enrollmentId === input.enrollmentId)));
|
|
93
|
+
if (existing) {
|
|
94
|
+
Object.assign(existing, input, { updatedAt: now });
|
|
95
|
+
await writeLocalEpochStore(store, home);
|
|
96
|
+
return existing;
|
|
97
|
+
}
|
|
98
|
+
const record = { ...input, createdAt: now, updatedAt: now };
|
|
99
|
+
store.deviceEnrollments.push(record);
|
|
100
|
+
await writeLocalEpochStore(store, home);
|
|
101
|
+
return record;
|
|
102
|
+
}
|
|
103
|
+
export async function getLocalPublicEpochPin(input, home = configDir()) {
|
|
104
|
+
const store = await readLocalEpochStore(home);
|
|
105
|
+
return (store.publicEpochPins
|
|
106
|
+
.filter((pin) => pin.workspaceId === input.workspaceId &&
|
|
107
|
+
pin.subjectType === input.subjectType &&
|
|
108
|
+
pin.subjectId === input.subjectId)
|
|
109
|
+
.sort((a, b) => b.epoch - a.epoch)[0] ?? null);
|
|
110
|
+
}
|
|
111
|
+
export async function upsertLocalPublicEpochPin(input, home = configDir()) {
|
|
112
|
+
const store = await readLocalEpochStore(home);
|
|
113
|
+
const now = new Date().toISOString();
|
|
114
|
+
const existing = store.publicEpochPins.find((pin) => pin.workspaceId === input.workspaceId &&
|
|
115
|
+
pin.subjectType === input.subjectType &&
|
|
116
|
+
pin.subjectId === input.subjectId &&
|
|
117
|
+
pin.epoch === input.epoch);
|
|
118
|
+
if (existing) {
|
|
119
|
+
Object.assign(existing, input, { updatedAt: now });
|
|
120
|
+
await writeLocalEpochStore(store, home);
|
|
121
|
+
return existing;
|
|
122
|
+
}
|
|
123
|
+
const record = { ...input, createdAt: now, updatedAt: now };
|
|
124
|
+
store.publicEpochPins.push(record);
|
|
125
|
+
await writeLocalEpochStore(store, home);
|
|
126
|
+
return record;
|
|
127
|
+
}
|
|
128
|
+
export function createLocalUserEpochKeyMaterial(input) {
|
|
129
|
+
const encryption = crypto.generateKeyPairSync('x25519');
|
|
130
|
+
const signing = crypto.generateKeyPairSync('ed25519');
|
|
131
|
+
const descriptor = {
|
|
132
|
+
schema: USER_EPOCH_SCHEMA,
|
|
133
|
+
workspaceId: input.workspaceId,
|
|
134
|
+
subjectType: 'user',
|
|
135
|
+
subjectId: input.userId ?? 'pending-platform-user',
|
|
136
|
+
epoch: input.epoch ?? 1,
|
|
137
|
+
encryptionPublicKeyJwk: encryption.publicKey.export({ format: 'jwk' }),
|
|
138
|
+
signingPublicKeyJwk: signing.publicKey.export({ format: 'jwk' }),
|
|
139
|
+
previousEpochFingerprint: input.previousEpochFingerprint ?? null,
|
|
140
|
+
createdAt: new Date().toISOString(),
|
|
141
|
+
};
|
|
142
|
+
return {
|
|
143
|
+
descriptor,
|
|
144
|
+
encryptionPrivateKeyJwk: encryption.privateKey.export({ format: 'jwk' }),
|
|
145
|
+
signingPrivateKeyJwk: signing.privateKey.export({ format: 'jwk' }),
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
export function createLocalTeamEpochKeyMaterial(input) {
|
|
149
|
+
const encryption = crypto.generateKeyPairSync('x25519');
|
|
150
|
+
const signing = crypto.generateKeyPairSync('ed25519');
|
|
151
|
+
const descriptor = {
|
|
152
|
+
schema: TEAM_EPOCH_SCHEMA,
|
|
153
|
+
workspaceId: input.workspaceId,
|
|
154
|
+
subjectType: 'team',
|
|
155
|
+
subjectId: input.teamId,
|
|
156
|
+
epoch: input.epoch ?? 1,
|
|
157
|
+
encryptionPublicKeyJwk: encryption.publicKey.export({ format: 'jwk' }),
|
|
158
|
+
signingPublicKeyJwk: signing.publicKey.export({ format: 'jwk' }),
|
|
159
|
+
previousEpochFingerprint: input.previousEpochFingerprint ?? null,
|
|
160
|
+
createdAt: new Date().toISOString(),
|
|
161
|
+
};
|
|
162
|
+
return {
|
|
163
|
+
descriptor,
|
|
164
|
+
encryptionPrivateKeyJwk: encryption.privateKey.export({ format: 'jwk' }),
|
|
165
|
+
signingPrivateKeyJwk: signing.privateKey.export({ format: 'jwk' }),
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
export function createLocalDeviceEnrollmentKeyMaterial(input) {
|
|
169
|
+
const encryption = crypto.generateKeyPairSync('x25519');
|
|
170
|
+
const signing = crypto.generateKeyPairSync('ed25519');
|
|
171
|
+
return {
|
|
172
|
+
enrollment: {
|
|
173
|
+
workspaceId: input.workspaceId,
|
|
174
|
+
deviceId: input.deviceId,
|
|
175
|
+
deviceLabel: input.deviceLabel,
|
|
176
|
+
encryptionPublicKeyJwk: encryption.publicKey.export({ format: 'jwk' }),
|
|
177
|
+
encryptionPrivateKeyJwk: encryption.privateKey.export({ format: 'jwk' }),
|
|
178
|
+
signingPublicKeyJwk: signing.publicKey.export({ format: 'jwk' }),
|
|
179
|
+
signingPrivateKeyJwk: signing.privateKey.export({ format: 'jwk' }),
|
|
180
|
+
nonce: input.nonce ?? crypto.randomBytes(24).toString('base64url'),
|
|
181
|
+
},
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
async function readLocalEpochStore(home = configDir()) {
|
|
185
|
+
try {
|
|
186
|
+
const raw = await fs.readFile(localEpochStorePath(home), 'utf8');
|
|
187
|
+
const parsed = JSON.parse(raw);
|
|
188
|
+
if (parsed.schema !== STORE_SCHEMA || !Array.isArray(parsed.userEpochs)) {
|
|
189
|
+
throw new Error('Invalid local crypto epoch store.');
|
|
190
|
+
}
|
|
191
|
+
if (!Array.isArray(parsed.teamEpochs))
|
|
192
|
+
parsed.teamEpochs = [];
|
|
193
|
+
if (!Array.isArray(parsed.deviceEnrollments))
|
|
194
|
+
parsed.deviceEnrollments = [];
|
|
195
|
+
if (!Array.isArray(parsed.publicEpochPins))
|
|
196
|
+
parsed.publicEpochPins = [];
|
|
197
|
+
return parsed;
|
|
198
|
+
}
|
|
199
|
+
catch (error) {
|
|
200
|
+
if (error.code === 'ENOENT') {
|
|
201
|
+
return {
|
|
202
|
+
schema: STORE_SCHEMA,
|
|
203
|
+
userEpochs: [],
|
|
204
|
+
teamEpochs: [],
|
|
205
|
+
deviceEnrollments: [],
|
|
206
|
+
publicEpochPins: [],
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
throw error;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
async function writeLocalEpochStore(store, home = configDir()) {
|
|
213
|
+
const filePath = localEpochStorePath(home);
|
|
214
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
215
|
+
await fs.writeFile(filePath, `${JSON.stringify(store, null, 2)}\n`, {
|
|
216
|
+
encoding: 'utf8',
|
|
217
|
+
mode: 0o600,
|
|
218
|
+
});
|
|
219
|
+
await fs.chmod(filePath, 0o600);
|
|
220
|
+
}
|
|
221
|
+
function localEpochStorePath(home = configDir()) {
|
|
222
|
+
return path.join(home, 'crypto', 'epochs.json');
|
|
223
|
+
}
|
|
224
|
+
//# sourceMappingURL=epoch-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"epoch-store.js","sourceRoot":"","sources":["../../src/security/epoch-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { transportFetch, type TlsVerifyMode } from '../cli/network.js';
|
|
2
|
+
import { type LocalTeamCryptoEpoch, type LocalUserCryptoEpoch } from './epoch-store.js';
|
|
3
|
+
import { type EpochTransitionPayload } from './epoch-protocol.js';
|
|
4
|
+
export interface CryptoEpochSyncTarget {
|
|
5
|
+
workspaceId: string;
|
|
6
|
+
serverUrl: string;
|
|
7
|
+
credential: string;
|
|
8
|
+
tlsVerify?: TlsVerifyMode;
|
|
9
|
+
caCertPath?: string;
|
|
10
|
+
tlsPins?: string[];
|
|
11
|
+
}
|
|
12
|
+
export declare function ensureUserCryptoEpoch(options: {
|
|
13
|
+
target: CryptoEpochSyncTarget;
|
|
14
|
+
home?: string;
|
|
15
|
+
fetchImpl?: typeof transportFetch;
|
|
16
|
+
}): Promise<LocalUserCryptoEpoch>;
|
|
17
|
+
export declare function rotateUserCryptoEpoch(options: {
|
|
18
|
+
target: CryptoEpochSyncTarget;
|
|
19
|
+
reason: EpochTransitionPayload['reason'];
|
|
20
|
+
home?: string;
|
|
21
|
+
fetchImpl?: typeof transportFetch;
|
|
22
|
+
}): Promise<LocalUserCryptoEpoch>;
|
|
23
|
+
export declare function ensureTeamCryptoEpoch(options: {
|
|
24
|
+
target: CryptoEpochSyncTarget;
|
|
25
|
+
teamId: string;
|
|
26
|
+
home?: string;
|
|
27
|
+
fetchImpl?: typeof transportFetch;
|
|
28
|
+
}): Promise<LocalTeamCryptoEpoch>;
|
|
29
|
+
export declare function rotateTeamCryptoEpoch(options: {
|
|
30
|
+
target: CryptoEpochSyncTarget;
|
|
31
|
+
teamId: string;
|
|
32
|
+
reason: EpochTransitionPayload['reason'];
|
|
33
|
+
home?: string;
|
|
34
|
+
fetchImpl?: typeof transportFetch;
|
|
35
|
+
}): Promise<LocalTeamCryptoEpoch>;
|
|
36
|
+
export declare function processPendingCryptoRotationRequests(options: {
|
|
37
|
+
target: CryptoEpochSyncTarget;
|
|
38
|
+
home?: string;
|
|
39
|
+
fetchImpl?: typeof transportFetch;
|
|
40
|
+
}): Promise<{
|
|
41
|
+
processed: number;
|
|
42
|
+
userRotations: number;
|
|
43
|
+
teamRotations: number;
|
|
44
|
+
teamMemberGrants: number;
|
|
45
|
+
skipped: number;
|
|
46
|
+
}>;
|
|
47
|
+
//# sourceMappingURL=epoch-sync.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"epoch-sync.d.ts","sourceRoot":"","sources":["../../src/security/epoch-sync.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,KAAK,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEvE,OAAO,EAOL,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,EAC1B,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAKL,KAAK,sBAAsB,EAE5B,MAAM,qBAAqB,CAAC;AAG7B,MAAM,WAAW,qBAAqB;IACpC,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,wBAAsB,qBAAqB,CAAC,OAAO,EAAE;IACnD,MAAM,EAAE,qBAAqB,CAAC;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,OAAO,cAAc,CAAC;CACnC,GAAG,OAAO,CAAC,oBAAoB,CAAC,CA2ChC;AAED,wBAAsB,qBAAqB,CAAC,OAAO,EAAE;IACnD,MAAM,EAAE,qBAAqB,CAAC;IAC9B,MAAM,EAAE,sBAAsB,CAAC,QAAQ,CAAC,CAAC;IACzC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,OAAO,cAAc,CAAC;CACnC,GAAG,OAAO,CAAC,oBAAoB,CAAC,CA2EhC;AAED,wBAAsB,qBAAqB,CAAC,OAAO,EAAE;IACnD,MAAM,EAAE,qBAAqB,CAAC;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,OAAO,cAAc,CAAC;CACnC,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAiDhC;AAED,wBAAsB,qBAAqB,CAAC,OAAO,EAAE;IACnD,MAAM,EAAE,qBAAqB,CAAC;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,sBAAsB,CAAC,QAAQ,CAAC,CAAC;IACzC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,OAAO,cAAc,CAAC;CACnC,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAgFhC;AAED,wBAAsB,oCAAoC,CAAC,OAAO,EAAE;IAClE,MAAM,EAAE,qBAAqB,CAAC;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,OAAO,cAAc,CAAC;CACnC,GAAG,OAAO,CAAC;IACV,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC,CA6DD"}
|
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
import { transportFetch } from '../cli/network.js';
|
|
2
|
+
import { configDir } from '../core/config.js';
|
|
3
|
+
import { createLocalTeamEpochKeyMaterial, createLocalUserEpochKeyMaterial, getActiveLocalTeamEpoch, getActiveLocalUserEpoch, upsertLocalTeamEpoch, upsertLocalUserEpoch, } from './epoch-store.js';
|
|
4
|
+
import { epochTransitionPayload, signEpochTransition, TRUSTED_EDGE_CRYPTO_PROTOCOL_HEADER, TRUSTED_EDGE_CRYPTO_PROTOCOL_VERSION, } from './epoch-protocol.js';
|
|
5
|
+
import { grantTeamEpochToUserEpoch } from './team-epoch-grants.js';
|
|
6
|
+
export async function ensureUserCryptoEpoch(options) {
|
|
7
|
+
const existing = await getActiveLocalUserEpoch(options.target.workspaceId, options.home);
|
|
8
|
+
if (existing)
|
|
9
|
+
return existing;
|
|
10
|
+
const material = createLocalUserEpochKeyMaterial({
|
|
11
|
+
workspaceId: options.target.workspaceId,
|
|
12
|
+
epoch: 1,
|
|
13
|
+
});
|
|
14
|
+
const payload = await postJson(options.fetchImpl ?? transportFetch, `${options.target.serverUrl.replace(/\/+$/, '')}/api/runtime/workspaces/${encodeURIComponent(options.target.workspaceId)}/crypto/user-epochs`, {
|
|
15
|
+
credential: options.target.credential,
|
|
16
|
+
epoch: 1,
|
|
17
|
+
encryption_public_key_jwk: material.descriptor.encryptionPublicKeyJwk,
|
|
18
|
+
signing_public_key_jwk: material.descriptor.signingPublicKeyJwk,
|
|
19
|
+
}, options.target);
|
|
20
|
+
const data = objectField(payload, 'data');
|
|
21
|
+
return upsertLocalUserEpoch({
|
|
22
|
+
workspaceId: stringField(data, 'workspace_id'),
|
|
23
|
+
userId: String(numberOrStringField(data, 'user_id')),
|
|
24
|
+
platformEpochId: stringField(data, 'id'),
|
|
25
|
+
epoch: numberField(data, 'epoch'),
|
|
26
|
+
schema: 'viewport.user_crypto_epoch/v1',
|
|
27
|
+
status: 'active',
|
|
28
|
+
encryptionPublicKeyJwk: objectField(data, 'encryption_public_key_jwk'),
|
|
29
|
+
encryptionPrivateKeyJwk: material.encryptionPrivateKeyJwk,
|
|
30
|
+
signingPublicKeyJwk: objectField(data, 'signing_public_key_jwk'),
|
|
31
|
+
signingPrivateKeyJwk: material.signingPrivateKeyJwk,
|
|
32
|
+
fingerprint: stringField(data, 'fingerprint'),
|
|
33
|
+
previousEpochFingerprint: typeof data.previous_epoch_fingerprint === 'string'
|
|
34
|
+
? data.previous_epoch_fingerprint
|
|
35
|
+
: null,
|
|
36
|
+
}, options.home ?? configDir());
|
|
37
|
+
}
|
|
38
|
+
export async function rotateUserCryptoEpoch(options) {
|
|
39
|
+
const previous = await getActiveLocalUserEpoch(options.target.workspaceId, options.home);
|
|
40
|
+
if (!previous) {
|
|
41
|
+
throw new Error('Cannot rotate user crypto epoch before an active local epoch exists.');
|
|
42
|
+
}
|
|
43
|
+
const material = createLocalUserEpochKeyMaterial({
|
|
44
|
+
workspaceId: options.target.workspaceId,
|
|
45
|
+
userId: previous.userId,
|
|
46
|
+
epoch: previous.epoch + 1,
|
|
47
|
+
previousEpochFingerprint: previous.fingerprint,
|
|
48
|
+
});
|
|
49
|
+
const createdAt = new Date().toISOString();
|
|
50
|
+
const continuity = signEpochTransition({
|
|
51
|
+
payload: epochTransitionPayload({
|
|
52
|
+
from: {
|
|
53
|
+
schema: previous.schema,
|
|
54
|
+
workspaceId: previous.workspaceId,
|
|
55
|
+
subjectType: 'user',
|
|
56
|
+
subjectId: previous.userId,
|
|
57
|
+
epoch: previous.epoch,
|
|
58
|
+
encryptionPublicKeyJwk: previous.encryptionPublicKeyJwk,
|
|
59
|
+
signingPublicKeyJwk: previous.signingPublicKeyJwk,
|
|
60
|
+
previousEpochFingerprint: previous.previousEpochFingerprint ?? null,
|
|
61
|
+
createdAt: previous.createdAt,
|
|
62
|
+
},
|
|
63
|
+
to: material.descriptor,
|
|
64
|
+
reason: options.reason,
|
|
65
|
+
createdAt,
|
|
66
|
+
}),
|
|
67
|
+
signingPrivateKeyJwk: previous.signingPrivateKeyJwk,
|
|
68
|
+
signedByEpochFingerprint: previous.fingerprint,
|
|
69
|
+
});
|
|
70
|
+
const payload = await postJson(options.fetchImpl ?? transportFetch, `${options.target.serverUrl.replace(/\/+$/, '')}/api/runtime/workspaces/${encodeURIComponent(options.target.workspaceId)}/crypto/user-epochs`, {
|
|
71
|
+
credential: options.target.credential,
|
|
72
|
+
epoch: material.descriptor.epoch,
|
|
73
|
+
encryption_public_key_jwk: material.descriptor.encryptionPublicKeyJwk,
|
|
74
|
+
signing_public_key_jwk: material.descriptor.signingPublicKeyJwk,
|
|
75
|
+
previous_epoch_fingerprint: previous.fingerprint,
|
|
76
|
+
continuity: {
|
|
77
|
+
payload: continuity.payload,
|
|
78
|
+
signature: continuity.signature,
|
|
79
|
+
signed_by_epoch_fingerprint: continuity.signedByEpochFingerprint,
|
|
80
|
+
},
|
|
81
|
+
}, options.target);
|
|
82
|
+
const data = objectField(payload, 'data');
|
|
83
|
+
return upsertLocalUserEpoch({
|
|
84
|
+
workspaceId: stringField(data, 'workspace_id'),
|
|
85
|
+
userId: String(numberOrStringField(data, 'user_id')),
|
|
86
|
+
platformEpochId: stringField(data, 'id'),
|
|
87
|
+
epoch: numberField(data, 'epoch'),
|
|
88
|
+
schema: 'viewport.user_crypto_epoch/v1',
|
|
89
|
+
status: 'active',
|
|
90
|
+
encryptionPublicKeyJwk: objectField(data, 'encryption_public_key_jwk'),
|
|
91
|
+
encryptionPrivateKeyJwk: material.encryptionPrivateKeyJwk,
|
|
92
|
+
signingPublicKeyJwk: objectField(data, 'signing_public_key_jwk'),
|
|
93
|
+
signingPrivateKeyJwk: material.signingPrivateKeyJwk,
|
|
94
|
+
fingerprint: stringField(data, 'fingerprint'),
|
|
95
|
+
previousEpochFingerprint: typeof data.previous_epoch_fingerprint === 'string'
|
|
96
|
+
? data.previous_epoch_fingerprint
|
|
97
|
+
: null,
|
|
98
|
+
}, options.home ?? configDir());
|
|
99
|
+
}
|
|
100
|
+
export async function ensureTeamCryptoEpoch(options) {
|
|
101
|
+
const existing = await getActiveLocalTeamEpoch(options.target.workspaceId, options.teamId, options.home);
|
|
102
|
+
if (existing)
|
|
103
|
+
return existing;
|
|
104
|
+
const material = createLocalTeamEpochKeyMaterial({
|
|
105
|
+
workspaceId: options.target.workspaceId,
|
|
106
|
+
teamId: options.teamId,
|
|
107
|
+
epoch: 1,
|
|
108
|
+
});
|
|
109
|
+
const payload = await postJson(options.fetchImpl ?? transportFetch, `${options.target.serverUrl.replace(/\/+$/, '')}/api/runtime/workspaces/${encodeURIComponent(options.target.workspaceId)}/crypto/teams/${encodeURIComponent(options.teamId)}/epochs`, {
|
|
110
|
+
credential: options.target.credential,
|
|
111
|
+
epoch: 1,
|
|
112
|
+
encryption_public_key_jwk: material.descriptor.encryptionPublicKeyJwk,
|
|
113
|
+
signing_public_key_jwk: material.descriptor.signingPublicKeyJwk,
|
|
114
|
+
}, options.target);
|
|
115
|
+
const data = objectField(payload, 'data');
|
|
116
|
+
return upsertLocalTeamEpoch({
|
|
117
|
+
workspaceId: stringField(data, 'workspace_id'),
|
|
118
|
+
teamId: options.teamId,
|
|
119
|
+
platformTeamId: String(numberOrStringField(data, 'team_id')),
|
|
120
|
+
platformEpochId: stringField(data, 'id'),
|
|
121
|
+
epoch: numberField(data, 'epoch'),
|
|
122
|
+
schema: 'viewport.team_crypto_epoch/v1',
|
|
123
|
+
status: 'active',
|
|
124
|
+
encryptionPublicKeyJwk: objectField(data, 'encryption_public_key_jwk'),
|
|
125
|
+
encryptionPrivateKeyJwk: material.encryptionPrivateKeyJwk,
|
|
126
|
+
signingPublicKeyJwk: objectField(data, 'signing_public_key_jwk'),
|
|
127
|
+
signingPrivateKeyJwk: material.signingPrivateKeyJwk,
|
|
128
|
+
fingerprint: stringField(data, 'fingerprint'),
|
|
129
|
+
previousEpochFingerprint: typeof data.previous_epoch_fingerprint === 'string'
|
|
130
|
+
? data.previous_epoch_fingerprint
|
|
131
|
+
: null,
|
|
132
|
+
}, options.home ?? configDir());
|
|
133
|
+
}
|
|
134
|
+
export async function rotateTeamCryptoEpoch(options) {
|
|
135
|
+
const previous = await getActiveLocalTeamEpoch(options.target.workspaceId, options.teamId, options.home);
|
|
136
|
+
if (!previous) {
|
|
137
|
+
throw new Error('Cannot rotate team crypto epoch before an active local team epoch exists.');
|
|
138
|
+
}
|
|
139
|
+
const material = createLocalTeamEpochKeyMaterial({
|
|
140
|
+
workspaceId: options.target.workspaceId,
|
|
141
|
+
teamId: previous.platformTeamId ?? previous.teamId,
|
|
142
|
+
epoch: previous.epoch + 1,
|
|
143
|
+
previousEpochFingerprint: previous.fingerprint,
|
|
144
|
+
});
|
|
145
|
+
const createdAt = new Date().toISOString();
|
|
146
|
+
const continuity = signEpochTransition({
|
|
147
|
+
payload: epochTransitionPayload({
|
|
148
|
+
from: {
|
|
149
|
+
schema: previous.schema,
|
|
150
|
+
workspaceId: previous.workspaceId,
|
|
151
|
+
subjectType: 'team',
|
|
152
|
+
subjectId: previous.platformTeamId ?? previous.teamId,
|
|
153
|
+
epoch: previous.epoch,
|
|
154
|
+
encryptionPublicKeyJwk: previous.encryptionPublicKeyJwk,
|
|
155
|
+
signingPublicKeyJwk: previous.signingPublicKeyJwk,
|
|
156
|
+
previousEpochFingerprint: previous.previousEpochFingerprint ?? null,
|
|
157
|
+
createdAt: previous.createdAt,
|
|
158
|
+
},
|
|
159
|
+
to: material.descriptor,
|
|
160
|
+
reason: options.reason,
|
|
161
|
+
createdAt,
|
|
162
|
+
}),
|
|
163
|
+
signingPrivateKeyJwk: previous.signingPrivateKeyJwk,
|
|
164
|
+
signedByEpochFingerprint: previous.fingerprint,
|
|
165
|
+
});
|
|
166
|
+
const payload = await postJson(options.fetchImpl ?? transportFetch, `${options.target.serverUrl.replace(/\/+$/, '')}/api/runtime/workspaces/${encodeURIComponent(options.target.workspaceId)}/crypto/teams/${encodeURIComponent(options.teamId)}/epochs`, {
|
|
167
|
+
credential: options.target.credential,
|
|
168
|
+
epoch: material.descriptor.epoch,
|
|
169
|
+
encryption_public_key_jwk: material.descriptor.encryptionPublicKeyJwk,
|
|
170
|
+
signing_public_key_jwk: material.descriptor.signingPublicKeyJwk,
|
|
171
|
+
previous_epoch_fingerprint: previous.fingerprint,
|
|
172
|
+
continuity: {
|
|
173
|
+
payload: continuity.payload,
|
|
174
|
+
signature: continuity.signature,
|
|
175
|
+
signed_by_epoch_fingerprint: continuity.signedByEpochFingerprint,
|
|
176
|
+
},
|
|
177
|
+
}, options.target);
|
|
178
|
+
const data = objectField(payload, 'data');
|
|
179
|
+
return upsertLocalTeamEpoch({
|
|
180
|
+
workspaceId: stringField(data, 'workspace_id'),
|
|
181
|
+
teamId: options.teamId,
|
|
182
|
+
platformTeamId: String(numberOrStringField(data, 'team_id')),
|
|
183
|
+
platformEpochId: stringField(data, 'id'),
|
|
184
|
+
epoch: numberField(data, 'epoch'),
|
|
185
|
+
schema: 'viewport.team_crypto_epoch/v1',
|
|
186
|
+
status: 'active',
|
|
187
|
+
encryptionPublicKeyJwk: objectField(data, 'encryption_public_key_jwk'),
|
|
188
|
+
encryptionPrivateKeyJwk: material.encryptionPrivateKeyJwk,
|
|
189
|
+
signingPublicKeyJwk: objectField(data, 'signing_public_key_jwk'),
|
|
190
|
+
signingPrivateKeyJwk: material.signingPrivateKeyJwk,
|
|
191
|
+
fingerprint: stringField(data, 'fingerprint'),
|
|
192
|
+
previousEpochFingerprint: typeof data.previous_epoch_fingerprint === 'string'
|
|
193
|
+
? data.previous_epoch_fingerprint
|
|
194
|
+
: null,
|
|
195
|
+
}, options.home ?? configDir());
|
|
196
|
+
}
|
|
197
|
+
export async function processPendingCryptoRotationRequests(options) {
|
|
198
|
+
const fetchImpl = options.fetchImpl ?? transportFetch;
|
|
199
|
+
const response = await getJson(fetchImpl, `${runtimeBaseUrl(options.target)}/crypto/rotation-requests`, options.target);
|
|
200
|
+
const requests = arrayField(response, 'data').map((item) => rotationRequestPayload(item));
|
|
201
|
+
let userRotations = 0;
|
|
202
|
+
let teamRotations = 0;
|
|
203
|
+
let teamMemberGrants = 0;
|
|
204
|
+
let skipped = 0;
|
|
205
|
+
for (const request of requests) {
|
|
206
|
+
if (request.subject_type === 'user') {
|
|
207
|
+
await rotateUserCryptoEpoch({
|
|
208
|
+
target: options.target,
|
|
209
|
+
reason: rotationReason(request.reason),
|
|
210
|
+
home: options.home,
|
|
211
|
+
fetchImpl,
|
|
212
|
+
});
|
|
213
|
+
userRotations++;
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
if (request.subject_type === 'team' && request.team_public_id) {
|
|
217
|
+
const rotated = await rotateTeamCryptoEpoch({
|
|
218
|
+
target: options.target,
|
|
219
|
+
teamId: request.team_public_id,
|
|
220
|
+
reason: rotationReason(request.reason),
|
|
221
|
+
home: options.home,
|
|
222
|
+
fetchImpl,
|
|
223
|
+
});
|
|
224
|
+
teamRotations++;
|
|
225
|
+
if (!rotated.platformEpochId) {
|
|
226
|
+
skipped++;
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
for (const recipientEpochId of request.recipient_user_crypto_epoch_ids) {
|
|
230
|
+
await grantTeamEpochToUserEpoch({
|
|
231
|
+
target: options.target,
|
|
232
|
+
teamCryptoEpochId: rotated.platformEpochId,
|
|
233
|
+
recipientUserCryptoEpochId: recipientEpochId,
|
|
234
|
+
home: options.home,
|
|
235
|
+
fetchImpl,
|
|
236
|
+
});
|
|
237
|
+
teamMemberGrants++;
|
|
238
|
+
}
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
skipped++;
|
|
242
|
+
}
|
|
243
|
+
return {
|
|
244
|
+
processed: userRotations + teamRotations,
|
|
245
|
+
userRotations,
|
|
246
|
+
teamRotations,
|
|
247
|
+
teamMemberGrants,
|
|
248
|
+
skipped,
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
function runtimeBaseUrl(target) {
|
|
252
|
+
return `${target.serverUrl.replace(/\/+$/, '')}/api/runtime/workspaces/${encodeURIComponent(target.workspaceId)}`;
|
|
253
|
+
}
|
|
254
|
+
async function getJson(fetchImpl, url, transportOptions) {
|
|
255
|
+
const requestUrl = new URL(url);
|
|
256
|
+
requestUrl.searchParams.set('credential', transportOptions.credential);
|
|
257
|
+
const response = await fetchImpl(requestUrl.toString(), {
|
|
258
|
+
method: 'GET',
|
|
259
|
+
headers: trustedEdgeCryptoHeaders(),
|
|
260
|
+
timeoutMs: 5_000,
|
|
261
|
+
tlsVerify: transportOptions.tlsVerify,
|
|
262
|
+
caCertPath: transportOptions.caCertPath,
|
|
263
|
+
tlsPins: transportOptions.tlsPins,
|
|
264
|
+
});
|
|
265
|
+
const payload = await response.json().catch(() => null);
|
|
266
|
+
if (!response.ok) {
|
|
267
|
+
const message = payload && typeof payload === 'object' && 'message' in payload
|
|
268
|
+
? String(payload.message)
|
|
269
|
+
: `${response.status} ${response.statusText}`;
|
|
270
|
+
throw new Error(`Crypto rotation sync failed: ${message}`);
|
|
271
|
+
}
|
|
272
|
+
return payload;
|
|
273
|
+
}
|
|
274
|
+
function rotationRequestPayload(value) {
|
|
275
|
+
const data = objectValue(value);
|
|
276
|
+
const subjectType = stringField(data, 'subject_type');
|
|
277
|
+
if (subjectType !== 'user' && subjectType !== 'team') {
|
|
278
|
+
throw new Error(`Unsupported crypto rotation subject: ${subjectType}`);
|
|
279
|
+
}
|
|
280
|
+
const recipients = data.recipient_user_crypto_epoch_ids;
|
|
281
|
+
return {
|
|
282
|
+
id: stringField(data, 'id'),
|
|
283
|
+
subject_type: subjectType,
|
|
284
|
+
subject_id: stringField(data, 'subject_id'),
|
|
285
|
+
team_public_id: typeof data.team_public_id === 'string' ? data.team_public_id : null,
|
|
286
|
+
reason: stringField(data, 'reason'),
|
|
287
|
+
recipient_user_crypto_epoch_ids: Array.isArray(recipients)
|
|
288
|
+
? recipients.map((item) => String(item))
|
|
289
|
+
: [],
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
function rotationReason(value) {
|
|
293
|
+
if (value === 'device_revoked' ||
|
|
294
|
+
value === 'member_added' ||
|
|
295
|
+
value === 'member_revoked' ||
|
|
296
|
+
value === 'manual_rotation' ||
|
|
297
|
+
value === 'recovery') {
|
|
298
|
+
return value;
|
|
299
|
+
}
|
|
300
|
+
return 'manual_rotation';
|
|
301
|
+
}
|
|
302
|
+
async function postJson(fetchImpl, url, body, transportOptions = {}) {
|
|
303
|
+
const response = await fetchImpl(url, {
|
|
304
|
+
method: 'POST',
|
|
305
|
+
headers: trustedEdgeCryptoHeaders({ 'content-type': 'application/json' }),
|
|
306
|
+
body: JSON.stringify(body),
|
|
307
|
+
timeoutMs: 5_000,
|
|
308
|
+
tlsVerify: transportOptions.tlsVerify,
|
|
309
|
+
caCertPath: transportOptions.caCertPath,
|
|
310
|
+
tlsPins: transportOptions.tlsPins,
|
|
311
|
+
});
|
|
312
|
+
const payload = await response.json().catch(() => null);
|
|
313
|
+
if (!response.ok) {
|
|
314
|
+
const message = payload && typeof payload === 'object' && 'message' in payload
|
|
315
|
+
? String(payload.message)
|
|
316
|
+
: `${response.status} ${response.statusText}`;
|
|
317
|
+
throw new Error(`Crypto epoch sync failed: ${message}`);
|
|
318
|
+
}
|
|
319
|
+
return payload;
|
|
320
|
+
}
|
|
321
|
+
function trustedEdgeCryptoHeaders(extra = {}) {
|
|
322
|
+
return {
|
|
323
|
+
accept: 'application/json',
|
|
324
|
+
[TRUSTED_EDGE_CRYPTO_PROTOCOL_HEADER]: TRUSTED_EDGE_CRYPTO_PROTOCOL_VERSION,
|
|
325
|
+
...extra,
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
function objectField(value, field) {
|
|
329
|
+
const object = objectValue(value);
|
|
330
|
+
const child = object[field];
|
|
331
|
+
if (!child || typeof child !== 'object' || Array.isArray(child)) {
|
|
332
|
+
throw new Error(`Crypto epoch response did not include ${field}`);
|
|
333
|
+
}
|
|
334
|
+
return child;
|
|
335
|
+
}
|
|
336
|
+
function objectValue(value) {
|
|
337
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
338
|
+
throw new Error('Expected response object.');
|
|
339
|
+
}
|
|
340
|
+
return value;
|
|
341
|
+
}
|
|
342
|
+
function arrayField(value, field) {
|
|
343
|
+
const object = objectValue(value);
|
|
344
|
+
const child = object[field];
|
|
345
|
+
if (!Array.isArray(child)) {
|
|
346
|
+
throw new Error(`Crypto epoch response did not include ${field} array.`);
|
|
347
|
+
}
|
|
348
|
+
return child;
|
|
349
|
+
}
|
|
350
|
+
function stringField(value, field) {
|
|
351
|
+
const child = value[field];
|
|
352
|
+
if (typeof child !== 'string' || child.trim().length === 0) {
|
|
353
|
+
throw new Error(`Crypto epoch response did not include ${field}`);
|
|
354
|
+
}
|
|
355
|
+
return child;
|
|
356
|
+
}
|
|
357
|
+
function numberField(value, field) {
|
|
358
|
+
const child = value[field];
|
|
359
|
+
if (typeof child !== 'number') {
|
|
360
|
+
throw new Error(`Crypto epoch response did not include numeric ${field}`);
|
|
361
|
+
}
|
|
362
|
+
return child;
|
|
363
|
+
}
|
|
364
|
+
function numberOrStringField(value, field) {
|
|
365
|
+
const child = value[field];
|
|
366
|
+
if (typeof child !== 'number' && typeof child !== 'string') {
|
|
367
|
+
throw new Error(`Crypto epoch response did not include ${field}`);
|
|
368
|
+
}
|
|
369
|
+
return child;
|
|
370
|
+
}
|
|
371
|
+
//# sourceMappingURL=epoch-sync.js.map
|