@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.
Files changed (109) hide show
  1. package/dist/cli/commands.d.ts +1 -0
  2. package/dist/cli/commands.d.ts.map +1 -1
  3. package/dist/cli/commands.js +1 -0
  4. package/dist/cli/commands.js.map +1 -1
  5. package/dist/cli/context-access-command.d.ts +0 -6
  6. package/dist/cli/context-access-command.d.ts.map +1 -1
  7. package/dist/cli/context-access-command.js +1 -71
  8. package/dist/cli/context-access-command.js.map +1 -1
  9. package/dist/cli/context-command.d.ts.map +1 -1
  10. package/dist/cli/context-command.js +593 -27
  11. package/dist/cli/context-command.js.map +1 -1
  12. package/dist/cli/context-sync-target.d.ts +2 -1
  13. package/dist/cli/context-sync-target.d.ts.map +1 -1
  14. package/dist/cli/context-sync-target.js +28 -0
  15. package/dist/cli/context-sync-target.js.map +1 -1
  16. package/dist/cli/context-vault-metadata-command.d.ts.map +1 -1
  17. package/dist/cli/context-vault-metadata-command.js +6 -1
  18. package/dist/cli/context-vault-metadata-command.js.map +1 -1
  19. package/dist/cli/lifecycle-commands.d.ts.map +1 -1
  20. package/dist/cli/lifecycle-commands.js +6 -6
  21. package/dist/cli/lifecycle-commands.js.map +1 -1
  22. package/dist/cli/unlock-command.d.ts +2 -0
  23. package/dist/cli/unlock-command.d.ts.map +1 -0
  24. package/dist/cli/unlock-command.js +35 -0
  25. package/dist/cli/unlock-command.js.map +1 -0
  26. package/dist/context/local-edge-store.d.ts +23 -1
  27. package/dist/context/local-edge-store.d.ts.map +1 -1
  28. package/dist/context/local-edge-store.js +51 -0
  29. package/dist/context/local-edge-store.js.map +1 -1
  30. package/dist/context/local-edge-sync.d.ts +63 -0
  31. package/dist/context/local-edge-sync.d.ts.map +1 -1
  32. package/dist/context/local-edge-sync.js +464 -4
  33. package/dist/context/local-edge-sync.js.map +1 -1
  34. package/dist/context/local-edge-types.d.ts +21 -0
  35. package/dist/context/local-edge-types.d.ts.map +1 -1
  36. package/dist/hooks/platform-plan-sync.d.ts +4 -1
  37. package/dist/hooks/platform-plan-sync.d.ts.map +1 -1
  38. package/dist/hooks/platform-plan-sync.js +20 -5
  39. package/dist/hooks/platform-plan-sync.js.map +1 -1
  40. package/dist/hooks/trusted-edge-plan-artifacts.d.ts +117 -0
  41. package/dist/hooks/trusted-edge-plan-artifacts.d.ts.map +1 -0
  42. package/dist/hooks/trusted-edge-plan-artifacts.js +371 -0
  43. package/dist/hooks/trusted-edge-plan-artifacts.js.map +1 -0
  44. package/dist/index.d.ts +1 -0
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/index.js +3 -1
  47. package/dist/index.js.map +1 -1
  48. package/dist/relay/bridge-token-issuer.d.ts +1 -0
  49. package/dist/relay/bridge-token-issuer.d.ts.map +1 -1
  50. package/dist/relay/bridge-token-issuer.js +1 -1
  51. package/dist/relay/bridge-token-issuer.js.map +1 -1
  52. package/dist/security/epoch-enrollment.d.ts +48 -0
  53. package/dist/security/epoch-enrollment.d.ts.map +1 -0
  54. package/dist/security/epoch-enrollment.js +290 -0
  55. package/dist/security/epoch-enrollment.js.map +1 -0
  56. package/dist/security/epoch-protocol.d.ts +181 -0
  57. package/dist/security/epoch-protocol.d.ts.map +1 -0
  58. package/dist/security/epoch-protocol.js +285 -0
  59. package/dist/security/epoch-protocol.js.map +1 -0
  60. package/dist/security/epoch-public-pins.d.ts +19 -0
  61. package/dist/security/epoch-public-pins.d.ts.map +1 -0
  62. package/dist/security/epoch-public-pins.js +129 -0
  63. package/dist/security/epoch-public-pins.js.map +1 -0
  64. package/dist/security/epoch-recovery.d.ts +56 -0
  65. package/dist/security/epoch-recovery.d.ts.map +1 -0
  66. package/dist/security/epoch-recovery.js +314 -0
  67. package/dist/security/epoch-recovery.js.map +1 -0
  68. package/dist/security/epoch-store.d.ts +111 -0
  69. package/dist/security/epoch-store.d.ts.map +1 -0
  70. package/dist/security/epoch-store.js +224 -0
  71. package/dist/security/epoch-store.js.map +1 -0
  72. package/dist/security/epoch-sync.d.ts +47 -0
  73. package/dist/security/epoch-sync.d.ts.map +1 -0
  74. package/dist/security/epoch-sync.js +371 -0
  75. package/dist/security/epoch-sync.js.map +1 -0
  76. package/dist/security/team-epoch-grants.d.ts +28 -0
  77. package/dist/security/team-epoch-grants.d.ts.map +1 -0
  78. package/dist/security/team-epoch-grants.js +256 -0
  79. package/dist/security/team-epoch-grants.js.map +1 -0
  80. package/dist/server/context-preview-service.d.ts +26 -0
  81. package/dist/server/context-preview-service.d.ts.map +1 -0
  82. package/dist/server/context-preview-service.js +71 -0
  83. package/dist/server/context-preview-service.js.map +1 -0
  84. package/dist/server/http-context-routes.d.ts +2 -1
  85. package/dist/server/http-context-routes.d.ts.map +1 -1
  86. package/dist/server/http-context-routes.js +65 -30
  87. package/dist/server/http-context-routes.js.map +1 -1
  88. package/dist/server/http-server.js +1 -1
  89. package/dist/server/http-server.js.map +1 -1
  90. package/dist/server/rate-limiter.d.ts.map +1 -1
  91. package/dist/server/rate-limiter.js +6 -1
  92. package/dist/server/rate-limiter.js.map +1 -1
  93. package/dist/server/trusted-edge-command-capability.d.ts +14 -0
  94. package/dist/server/trusted-edge-command-capability.d.ts.map +1 -0
  95. package/dist/server/trusted-edge-command-capability.js +114 -0
  96. package/dist/server/trusted-edge-command-capability.js.map +1 -0
  97. package/dist/server/ws-command-handlers.d.ts.map +1 -1
  98. package/dist/server/ws-command-handlers.js +231 -27
  99. package/dist/server/ws-command-handlers.js.map +1 -1
  100. package/dist/server/ws-protocol.d.ts +419 -5
  101. package/dist/server/ws-protocol.d.ts.map +1 -1
  102. package/dist/server/ws-protocol.js +141 -4
  103. package/dist/server/ws-protocol.js.map +1 -1
  104. package/docs/protocol-matrix.json +93 -5
  105. package/node_modules/@viewportai/context-engine/src/repo/materializer.js +20 -5
  106. package/node_modules/@viewportai/context-engine/src/repo/membership.js +15 -0
  107. package/node_modules/@viewportai/context-engine/src/repo/sync.js +4 -4
  108. package/node_modules/@viewportai/context-engine/src/repo/vault.js +8 -3
  109. package/package.json +1 -1
@@ -1,4 +1,5 @@
1
1
  import { transportFetch, type TlsVerifyMode } from '../cli/network.js';
2
+ import { type SignedContextGrantMaterialization } from '../security/epoch-protocol.js';
2
3
  import type { ContextCredentials } from './local-edge-types.js';
3
4
  export declare function pushContextEvents(options: {
4
5
  contextResourceId: string;
@@ -32,8 +33,70 @@ export declare function pullContextEvents(options: {
32
33
  }): Promise<{
33
34
  appliedCandidateDecisions: number;
34
35
  imported: number;
36
+ materializedGrants: number;
35
37
  pendingCandidateDecisions: number;
36
38
  pulled: number;
37
39
  repoId: string;
38
40
  }>;
41
+ export declare function recordContextGrantMaterialization(options: {
42
+ workspaceId: string;
43
+ serverUrl: string;
44
+ credential: string;
45
+ contextResourceId: string;
46
+ receipts: SignedContextGrantMaterialization[];
47
+ tlsVerify?: TlsVerifyMode;
48
+ caCertPath?: string;
49
+ tlsPins?: string[];
50
+ fetchImpl?: typeof transportFetch;
51
+ }): Promise<number>;
52
+ export declare function recordContextCandidatePreviewProof(options: {
53
+ workspaceId: string;
54
+ serverUrl: string;
55
+ credential: string;
56
+ contextResourceId: string;
57
+ candidateEventId: string;
58
+ payloadDigest?: string | null;
59
+ previewDigest?: string | null;
60
+ tlsVerify?: TlsVerifyMode;
61
+ caCertPath?: string;
62
+ tlsPins?: string[];
63
+ fetchImpl?: typeof transportFetch;
64
+ }): Promise<{
65
+ previewProofId: string;
66
+ expiresAt: string | null;
67
+ }>;
68
+ export declare function processPendingContextGrants(options: {
69
+ contextResourceId: string;
70
+ workspaceId: string;
71
+ serverUrl: string;
72
+ credential: string;
73
+ actorName: string;
74
+ credentials: ContextCredentials;
75
+ tlsVerify?: TlsVerifyMode;
76
+ caCertPath?: string;
77
+ tlsPins?: string[];
78
+ home?: string;
79
+ fetchImpl?: typeof transportFetch;
80
+ }): Promise<{
81
+ emitted: number;
82
+ missingIdentity: number;
83
+ pushed: number;
84
+ }>;
85
+ export declare function processPendingContextRevocations(options: {
86
+ contextResourceId: string;
87
+ workspaceId: string;
88
+ serverUrl: string;
89
+ credential: string;
90
+ actorName: string;
91
+ credentials: ContextCredentials;
92
+ tlsVerify?: TlsVerifyMode;
93
+ caCertPath?: string;
94
+ tlsPins?: string[];
95
+ home?: string;
96
+ fetchImpl?: typeof transportFetch;
97
+ }): Promise<{
98
+ revoked: number;
99
+ missingIdentity: number;
100
+ pushed: number;
101
+ }>;
39
102
  //# sourceMappingURL=local-edge-sync.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"local-edge-sync.d.ts","sourceRoot":"","sources":["../../src/context/local-edge-sync.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,KAAK,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAUvE,OAAO,KAAK,EAEV,kBAAkB,EAGnB,MAAM,uBAAuB,CAAC;AAE/B,wBAAsB,iBAAiB,CAAC,OAAO,EAAE;IAC/C,iBAAiB,EAAE,MAAM,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,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;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,OAAO,cAAc,CAAC;CACnC,GAAG,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAoChE;AAED,wBAAsB,iBAAiB,CAAC,OAAO,EAAE;IAC/C,iBAAiB,EAAE,MAAM,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,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;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,kBAAkB,CAAC;IAChC,mBAAmB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,OAAO,cAAc,CAAC;CACnC,GAAG,OAAO,CAAC;IACV,yBAAyB,EAAE,MAAM,CAAC;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,yBAAyB,EAAE,MAAM,CAAC;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC,CAgFD"}
1
+ {"version":3,"file":"local-edge-sync.d.ts","sourceRoot":"","sources":["../../src/context/local-edge-sync.ts"],"names":[],"mappings":"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"}
@@ -5,6 +5,11 @@ import { applyContextCandidateDecision } from './local-edge-candidates.js';
5
5
  import { readCandidateDecisionApplications } from './local-edge-decision-applications.js';
6
6
  import { verifyContextCandidateDecision } from './local-edge-decision-signature.js';
7
7
  import { readContextMetadata, touchContextMetadata } from './local-edge-metadata.js';
8
+ import { grantContextHpkeRecipient, revokeContextUser } from './local-edge-store.js';
9
+ import { validateAndPinPublicEpoch } from '../security/epoch-public-pins.js';
10
+ import { getActiveLocalUserEpoch, listActiveLocalTeamEpochs } from '../security/epoch-store.js';
11
+ import { TRUSTED_EDGE_CRYPTO_PROTOCOL_HEADER, TRUSTED_EDGE_CRYPTO_PROTOCOL_VERSION, contextGrantMaterializationPayload, signContextGrantMaterialization, } from '../security/epoch-protocol.js';
12
+ const CONTEXT_GRANT_EVENT_TYPES = new Set(['member.granted', 'key.rotated']);
8
13
  export async function pushContextEvents(options) {
9
14
  const home = options.home ?? configDir();
10
15
  const metadata = await readContextMetadata(options.contextResourceId, home);
@@ -64,11 +69,35 @@ export async function pullContextEvents(options) {
64
69
  });
65
70
  const records = extractPulledRecords(response);
66
71
  const events = records.map((record) => record.signedEvent);
72
+ const grantIdentities = await contextGrantIdentitiesForWorkspace({
73
+ workspaceId: options.workspaceId ?? options.contextResourceId,
74
+ home,
75
+ });
67
76
  const imported = await vault.importSyncEvents({
68
77
  repoId: metadata.repoId,
69
78
  events,
70
79
  actorName: options.actorName,
80
+ grantIdentities,
81
+ });
82
+ const materializationReceipts = contextGrantMaterializationReceipts({
83
+ workspaceId: options.workspaceId ?? options.contextResourceId,
84
+ contextResourceId: options.contextResourceId,
85
+ events,
86
+ grantIdentities,
71
87
  });
88
+ const materializedGrants = materializationReceipts.length > 0
89
+ ? await recordContextGrantMaterialization({
90
+ workspaceId: options.workspaceId ?? options.contextResourceId,
91
+ serverUrl: options.serverUrl,
92
+ credential: options.credential,
93
+ contextResourceId: options.contextResourceId,
94
+ receipts: materializationReceipts,
95
+ tlsVerify: options.tlsVerify,
96
+ caCertPath: options.caCertPath,
97
+ tlsPins: options.tlsPins,
98
+ fetchImpl: options.fetchImpl,
99
+ })
100
+ : 0;
72
101
  const candidateDecisions = extractPulledCandidateDecisions(response, options.trustedDecisionKeys);
73
102
  const candidateDecisionResults = [];
74
103
  for (const decision of candidateDecisions) {
@@ -91,19 +120,398 @@ export async function pullContextEvents(options) {
91
120
  return {
92
121
  appliedCandidateDecisions,
93
122
  imported: imported.imported.length,
123
+ materializedGrants,
94
124
  pendingCandidateDecisions,
95
125
  pulled: events.length,
96
126
  repoId: metadata.repoId,
97
127
  };
98
128
  }
129
+ export async function recordContextGrantMaterialization(options) {
130
+ if (options.receipts.length === 0) {
131
+ return 0;
132
+ }
133
+ const response = await postJson(options.fetchImpl ?? transportFetch, contextGrantMaterializedUrl(options.serverUrl, options.workspaceId), {
134
+ credential: options.credential,
135
+ context_resource_id: options.contextResourceId,
136
+ receipts: options.receipts.map((receipt) => ({
137
+ payload: receipt.payload,
138
+ signature: receipt.signature,
139
+ signed_by_epoch_fingerprint: receipt.signedByEpochFingerprint,
140
+ })),
141
+ }, {
142
+ tlsVerify: options.tlsVerify,
143
+ caCertPath: options.caCertPath,
144
+ tlsPins: options.tlsPins,
145
+ });
146
+ return numberField(response, 'materialized');
147
+ }
148
+ export async function recordContextCandidatePreviewProof(options) {
149
+ const response = await postJson(options.fetchImpl ?? transportFetch, contextCandidatePreviewProofUrl(options.serverUrl, options.workspaceId), {
150
+ credential: options.credential,
151
+ context_resource_id: options.contextResourceId,
152
+ candidate_event_id: options.candidateEventId,
153
+ ...(options.payloadDigest ? { payload_digest: options.payloadDigest } : {}),
154
+ ...(options.previewDigest ? { preview_digest: options.previewDigest } : {}),
155
+ }, {
156
+ tlsVerify: options.tlsVerify,
157
+ caCertPath: options.caCertPath,
158
+ tlsPins: options.tlsPins,
159
+ });
160
+ if (!response || typeof response !== 'object') {
161
+ throw new Error('Context preview proof response was not an object');
162
+ }
163
+ const previewProofId = response.preview_proof_id;
164
+ if (typeof previewProofId !== 'string' || previewProofId === '') {
165
+ throw new Error('Context preview proof response did not include preview_proof_id');
166
+ }
167
+ const expiresAt = response.expires_at;
168
+ return {
169
+ previewProofId,
170
+ expiresAt: typeof expiresAt === 'string' ? expiresAt : null,
171
+ };
172
+ }
173
+ export async function processPendingContextGrants(options) {
174
+ const fetchImpl = options.fetchImpl ?? transportFetch;
175
+ const response = await postJson(fetchImpl, contextPendingGrantsUrl(options.serverUrl, options.workspaceId), {
176
+ credential: options.credential,
177
+ context_resource_id: options.contextResourceId,
178
+ }, {
179
+ tlsVerify: options.tlsVerify,
180
+ caCertPath: options.caCertPath,
181
+ tlsPins: options.tlsPins,
182
+ });
183
+ const grants = arrayField(response, 'grants');
184
+ let emitted = 0;
185
+ let missingIdentity = 0;
186
+ let pushed = 0;
187
+ for (const grant of grants) {
188
+ const record = objectValue(grant);
189
+ const userEpoch = objectField(record, 'user_epoch', false);
190
+ if (userEpoch) {
191
+ const userEpochId = String(numberOrStringField(userEpoch, 'id'));
192
+ const userId = String(numberOrStringField(userEpoch, 'user_id'));
193
+ const epoch = numberField(userEpoch, 'epoch');
194
+ const fingerprint = stringField(userEpoch, 'fingerprint');
195
+ const encryptionPublicKeyJwk = objectField(userEpoch, 'encryption_public_key_jwk');
196
+ const signingPublicKeyJwk = objectField(userEpoch, 'signing_public_key_jwk');
197
+ await validateAndPinPublicEpoch({
198
+ platformEpochId: userEpochId,
199
+ workspaceId: options.workspaceId,
200
+ subjectType: 'user',
201
+ subjectId: userId,
202
+ epoch,
203
+ schema: 'viewport.user_crypto_epoch/v1',
204
+ fingerprint,
205
+ encryptionPublicKeyJwk: encryptionPublicKeyJwk,
206
+ signingPublicKeyJwk: signingPublicKeyJwk,
207
+ previousEpochFingerprint: nullableStringField(userEpoch, 'previous_epoch_fingerprint'),
208
+ continuityPayload: objectField(userEpoch, 'continuity_payload', false),
209
+ continuitySignature: nullableStringField(userEpoch, 'continuity_signature'),
210
+ signedByEpochFingerprint: nullableStringField(userEpoch, 'signed_by_epoch_fingerprint'),
211
+ }, options.home);
212
+ const recipientName = contextUserEpochRecipientName({ userEpochId, fingerprint });
213
+ const result = await grantContextHpkeRecipient({
214
+ contextResourceId: options.contextResourceId,
215
+ actorName: options.actorName,
216
+ recipientName,
217
+ recipientHpkePublicKey: jwkPublicXToBase64(encryptionPublicKeyJwk),
218
+ credentials: options.credentials,
219
+ home: options.home,
220
+ });
221
+ const event = objectValue(result.event);
222
+ const grantEventId = stringField(event, 'id');
223
+ const grantPayload = objectField(event, 'grant', false);
224
+ const keyEpoch = grantPayload ? numberField(grantPayload, 'keyEpoch', false) : null;
225
+ const pushResult = await pushContextEvents({
226
+ contextResourceId: options.contextResourceId,
227
+ workspaceId: options.workspaceId,
228
+ serverUrl: options.serverUrl,
229
+ credential: options.credential,
230
+ tlsVerify: options.tlsVerify,
231
+ caCertPath: options.caCertPath,
232
+ tlsPins: options.tlsPins,
233
+ home: options.home,
234
+ fetchImpl,
235
+ });
236
+ pushed += pushResult.accepted;
237
+ await postJson(fetchImpl, contextMarkGrantEmittedUrl(options.serverUrl, options.workspaceId), {
238
+ credential: options.credential,
239
+ crypto_grant_id: stringField(record, 'id'),
240
+ grant_event_id: grantEventId,
241
+ recipient_identity_name: recipientName,
242
+ recipient_type: 'user_epoch',
243
+ recipient_epoch_id: userEpochId,
244
+ recipient_fingerprint: fingerprint,
245
+ ...(keyEpoch !== null ? { key_epoch: keyEpoch } : {}),
246
+ }, {
247
+ tlsVerify: options.tlsVerify,
248
+ caCertPath: options.caCertPath,
249
+ tlsPins: options.tlsPins,
250
+ });
251
+ emitted++;
252
+ continue;
253
+ }
254
+ const teamEpoch = objectField(record, 'team_epoch', false);
255
+ if (teamEpoch) {
256
+ const teamEpochId = String(numberOrStringField(teamEpoch, 'id'));
257
+ const teamId = String(numberOrStringField(teamEpoch, 'team_id'));
258
+ const epoch = numberField(teamEpoch, 'epoch');
259
+ const fingerprint = stringField(teamEpoch, 'fingerprint');
260
+ const encryptionPublicKeyJwk = objectField(teamEpoch, 'encryption_public_key_jwk');
261
+ const signingPublicKeyJwk = objectField(teamEpoch, 'signing_public_key_jwk');
262
+ await validateAndPinPublicEpoch({
263
+ platformEpochId: teamEpochId,
264
+ workspaceId: options.workspaceId,
265
+ subjectType: 'team',
266
+ subjectId: teamId,
267
+ epoch,
268
+ schema: 'viewport.team_crypto_epoch/v1',
269
+ fingerprint,
270
+ encryptionPublicKeyJwk: encryptionPublicKeyJwk,
271
+ signingPublicKeyJwk: signingPublicKeyJwk,
272
+ previousEpochFingerprint: nullableStringField(teamEpoch, 'previous_epoch_fingerprint'),
273
+ continuityPayload: objectField(teamEpoch, 'continuity_payload', false),
274
+ continuitySignature: nullableStringField(teamEpoch, 'continuity_signature'),
275
+ signedByEpochFingerprint: nullableStringField(teamEpoch, 'signed_by_epoch_fingerprint'),
276
+ }, options.home);
277
+ const recipientName = contextTeamEpochRecipientName({ teamEpochId, fingerprint });
278
+ const result = await grantContextHpkeRecipient({
279
+ contextResourceId: options.contextResourceId,
280
+ actorName: options.actorName,
281
+ recipientName,
282
+ recipientHpkePublicKey: jwkPublicXToBase64(encryptionPublicKeyJwk),
283
+ credentials: options.credentials,
284
+ home: options.home,
285
+ });
286
+ const event = objectValue(result.event);
287
+ const grantEventId = stringField(event, 'id');
288
+ const grantPayload = objectField(event, 'grant', false);
289
+ const keyEpoch = grantPayload ? numberField(grantPayload, 'keyEpoch', false) : null;
290
+ const pushResult = await pushContextEvents({
291
+ contextResourceId: options.contextResourceId,
292
+ workspaceId: options.workspaceId,
293
+ serverUrl: options.serverUrl,
294
+ credential: options.credential,
295
+ tlsVerify: options.tlsVerify,
296
+ caCertPath: options.caCertPath,
297
+ tlsPins: options.tlsPins,
298
+ home: options.home,
299
+ fetchImpl,
300
+ });
301
+ pushed += pushResult.accepted;
302
+ await postJson(fetchImpl, contextMarkGrantEmittedUrl(options.serverUrl, options.workspaceId), {
303
+ credential: options.credential,
304
+ crypto_grant_id: stringField(record, 'id'),
305
+ grant_event_id: grantEventId,
306
+ recipient_identity_name: recipientName,
307
+ recipient_type: 'team_epoch',
308
+ recipient_epoch_id: teamEpochId,
309
+ recipient_fingerprint: fingerprint,
310
+ ...(keyEpoch !== null ? { key_epoch: keyEpoch } : {}),
311
+ }, {
312
+ tlsVerify: options.tlsVerify,
313
+ caCertPath: options.caCertPath,
314
+ tlsPins: options.tlsPins,
315
+ });
316
+ emitted++;
317
+ continue;
318
+ }
319
+ missingIdentity++;
320
+ }
321
+ return { emitted, missingIdentity, pushed };
322
+ }
323
+ export async function processPendingContextRevocations(options) {
324
+ const fetchImpl = options.fetchImpl ?? transportFetch;
325
+ const response = await postJson(fetchImpl, contextPendingRevocationsUrl(options.serverUrl, options.workspaceId), {
326
+ credential: options.credential,
327
+ context_resource_id: options.contextResourceId,
328
+ }, {
329
+ tlsVerify: options.tlsVerify,
330
+ caCertPath: options.caCertPath,
331
+ tlsPins: options.tlsPins,
332
+ });
333
+ const revocations = arrayField(response, 'revocations');
334
+ let revoked = 0;
335
+ let missingIdentity = 0;
336
+ let pushed = 0;
337
+ for (const revocation of revocations) {
338
+ const record = objectValue(revocation);
339
+ const recipientName = nullableStringField(record, 'recipient_identity_name');
340
+ if (!recipientName) {
341
+ missingIdentity++;
342
+ continue;
343
+ }
344
+ const result = await revokeContextUser({
345
+ contextResourceId: options.contextResourceId,
346
+ actorName: options.actorName,
347
+ recipientName,
348
+ credentials: options.credentials,
349
+ home: options.home,
350
+ });
351
+ const revokeEventId = stringField(objectValue(result.revokeEvent), 'id');
352
+ const rotationEventIds = result.rotateEvents.map((event) => stringField(objectValue(event), 'id'));
353
+ const rotationEvents = result.rotateEvents.map((event) => contextGrantRotationReceipt(event));
354
+ const maxKeyEpoch = maxEventEpoch([result.revokeEvent, ...result.rotateEvents]);
355
+ const pushResult = await pushContextEvents({
356
+ contextResourceId: options.contextResourceId,
357
+ workspaceId: options.workspaceId,
358
+ serverUrl: options.serverUrl,
359
+ credential: options.credential,
360
+ tlsVerify: options.tlsVerify,
361
+ caCertPath: options.caCertPath,
362
+ tlsPins: options.tlsPins,
363
+ home: options.home,
364
+ fetchImpl,
365
+ });
366
+ pushed += pushResult.accepted;
367
+ await postJson(fetchImpl, contextMarkRevokedUrl(options.serverUrl, options.workspaceId), {
368
+ credential: options.credential,
369
+ crypto_grant_id: stringField(record, 'id'),
370
+ revoke_event_id: revokeEventId,
371
+ rotation_event_ids: rotationEventIds,
372
+ rotation_events: rotationEvents,
373
+ ...(maxKeyEpoch !== null ? { key_epoch: maxKeyEpoch } : {}),
374
+ }, {
375
+ tlsVerify: options.tlsVerify,
376
+ caCertPath: options.caCertPath,
377
+ tlsPins: options.tlsPins,
378
+ });
379
+ revoked++;
380
+ }
381
+ return { revoked, missingIdentity, pushed };
382
+ }
383
+ function maxEventEpoch(events) {
384
+ let max = null;
385
+ for (const event of events) {
386
+ const epoch = numberField(objectValue(event), 'keyEpoch', false);
387
+ if (epoch !== null)
388
+ max = max === null ? epoch : Math.max(max, epoch);
389
+ }
390
+ return max;
391
+ }
392
+ function contextGrantRotationReceipt(event) {
393
+ const object = objectValue(event);
394
+ const grant = objectField(object, 'grant', false);
395
+ const recipientName = grant ? nullableStringField(grant, 'recipientName') : null;
396
+ return {
397
+ event_id: stringField(object, 'id'),
398
+ ...(recipientName ? { recipient_identity_name: recipientName } : {}),
399
+ };
400
+ }
401
+ async function contextGrantIdentitiesForWorkspace(options) {
402
+ const userEpoch = await getActiveLocalUserEpoch(options.workspaceId, options.home);
403
+ const teamEpochs = await listActiveLocalTeamEpochs(options.workspaceId, options.home);
404
+ return [
405
+ ...(userEpoch?.platformEpochId
406
+ ? [
407
+ {
408
+ kind: 'user_epoch',
409
+ name: contextUserEpochRecipientName({
410
+ userEpochId: userEpoch.platformEpochId,
411
+ fingerprint: userEpoch.fingerprint,
412
+ }),
413
+ hpkePrivateKey: jwkPrivateDToBase64(objectValue(userEpoch.encryptionPrivateKeyJwk)),
414
+ signingPrivateKeyJwk: userEpoch.signingPrivateKeyJwk,
415
+ signerFingerprint: userEpoch.fingerprint,
416
+ },
417
+ ]
418
+ : []),
419
+ ...teamEpochs.map((epoch) => ({
420
+ kind: 'team_epoch',
421
+ name: contextTeamEpochRecipientName({
422
+ teamEpochId: epoch.platformEpochId ?? `${epoch.platformTeamId ?? epoch.teamId}:${epoch.epoch}`,
423
+ fingerprint: epoch.fingerprint,
424
+ }),
425
+ hpkePrivateKey: jwkPrivateDToBase64(objectValue(epoch.encryptionPrivateKeyJwk)),
426
+ signingPrivateKeyJwk: epoch.signingPrivateKeyJwk,
427
+ signerFingerprint: epoch.fingerprint,
428
+ })),
429
+ ];
430
+ }
431
+ function contextGrantMaterializationReceipts(options) {
432
+ const receipts = [];
433
+ for (const event of options.events) {
434
+ const match = grantEventRecipientMatch(event, options.grantIdentities);
435
+ if (!match)
436
+ continue;
437
+ const keyEpoch = numberField(event, 'keyEpoch', false);
438
+ const payload = contextGrantMaterializationPayload({
439
+ workspaceId: options.workspaceId,
440
+ contextResourceId: options.contextResourceId,
441
+ grantEventId: stringField(event, 'id'),
442
+ recipientName: match.identity.name,
443
+ keyEpoch,
444
+ });
445
+ receipts.push(signContextGrantMaterialization({
446
+ payload,
447
+ signingPrivateKeyJwk: match.identity.signingPrivateKeyJwk,
448
+ signedByEpochFingerprint: match.identity.signerFingerprint,
449
+ }));
450
+ }
451
+ return receipts;
452
+ }
453
+ function grantEventRecipientMatch(event, grantIdentities) {
454
+ if (!CONTEXT_GRANT_EVENT_TYPES.has(event.type))
455
+ return null;
456
+ const grant = event.grant;
457
+ if (!grant || typeof grant !== 'object' || Array.isArray(grant))
458
+ return null;
459
+ const recipientName = grant.recipientName;
460
+ if (typeof recipientName !== 'string' || recipientName === '')
461
+ return null;
462
+ const identity = grantIdentities.find((candidate) => candidate.name === recipientName);
463
+ return identity ? { grant: grant, identity } : null;
464
+ }
465
+ function contextUserEpochRecipientName(input) {
466
+ return `user-epoch:${input.userEpochId}:${input.fingerprint}`;
467
+ }
468
+ function contextTeamEpochRecipientName(input) {
469
+ return `team-epoch:${input.teamEpochId}:${input.fingerprint}`;
470
+ }
471
+ function jwkPublicXToBase64(jwk) {
472
+ const x = stringField(jwk, 'x');
473
+ return Buffer.from(x, 'base64url').toString('base64');
474
+ }
475
+ function jwkPrivateDToBase64(jwk) {
476
+ const d = stringField(jwk, 'd');
477
+ return Buffer.from(d, 'base64url').toString('base64');
478
+ }
99
479
  function contextRuntimeUrl(serverUrl, workspaceId, operation) {
100
480
  const base = serverUrl.replace(/\/+$/, '');
101
481
  return `${base}/api/runtime/workspaces/${encodeURIComponent(workspaceId)}/context-vault/events/${operation}`;
102
482
  }
483
+ function contextCandidatePreviewProofUrl(serverUrl, workspaceId) {
484
+ const base = serverUrl.replace(/\/+$/, '');
485
+ return `${base}/api/runtime/workspaces/${encodeURIComponent(workspaceId)}/context-vault/candidates/preview-proof`;
486
+ }
487
+ function contextPendingGrantsUrl(serverUrl, workspaceId) {
488
+ const base = serverUrl.replace(/\/+$/, '');
489
+ return `${base}/api/runtime/workspaces/${encodeURIComponent(workspaceId)}/context-vault/grants/pending`;
490
+ }
491
+ function contextMarkGrantEmittedUrl(serverUrl, workspaceId) {
492
+ const base = serverUrl.replace(/\/+$/, '');
493
+ return `${base}/api/runtime/workspaces/${encodeURIComponent(workspaceId)}/context-vault/grants/mark-emitted`;
494
+ }
495
+ function contextPendingRevocationsUrl(serverUrl, workspaceId) {
496
+ const base = serverUrl.replace(/\/+$/, '');
497
+ return `${base}/api/runtime/workspaces/${encodeURIComponent(workspaceId)}/context-vault/grants/revocations/pending`;
498
+ }
499
+ function contextMarkRevokedUrl(serverUrl, workspaceId) {
500
+ const base = serverUrl.replace(/\/+$/, '');
501
+ return `${base}/api/runtime/workspaces/${encodeURIComponent(workspaceId)}/context-vault/grants/mark-revoked`;
502
+ }
503
+ function contextGrantMaterializedUrl(serverUrl, workspaceId) {
504
+ const base = serverUrl.replace(/\/+$/, '');
505
+ return `${base}/api/runtime/workspaces/${encodeURIComponent(workspaceId)}/context-vault/grants/materialized`;
506
+ }
103
507
  async function postJson(fetchImpl, url, body, transportOptions = {}) {
104
508
  const response = await fetchImpl(url, {
105
509
  method: 'POST',
106
- headers: { 'content-type': 'application/json', accept: 'application/json' },
510
+ headers: {
511
+ 'content-type': 'application/json',
512
+ accept: 'application/json',
513
+ [TRUSTED_EDGE_CRYPTO_PROTOCOL_HEADER]: TRUSTED_EDGE_CRYPTO_PROTOCOL_VERSION,
514
+ },
107
515
  body: JSON.stringify(body),
108
516
  timeoutMs: 5_000,
109
517
  ...transportOptions,
@@ -144,6 +552,47 @@ function extractPulledRecords(response) {
144
552
  };
145
553
  });
146
554
  }
555
+ function objectValue(value) {
556
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
557
+ throw new Error('Expected object value');
558
+ }
559
+ return value;
560
+ }
561
+ function objectField(value, field, required = true) {
562
+ const object = objectValue(value);
563
+ const child = object[field];
564
+ if (child === undefined || child === null) {
565
+ if (!required)
566
+ return null;
567
+ }
568
+ return objectValue(child);
569
+ }
570
+ function arrayField(value, field) {
571
+ const object = objectValue(value);
572
+ const child = object[field];
573
+ if (!Array.isArray(child)) {
574
+ throw new Error(`Expected ${field} to be an array`);
575
+ }
576
+ return child;
577
+ }
578
+ function nullableStringField(value, field) {
579
+ const object = objectValue(value);
580
+ const child = object[field];
581
+ if (child === undefined || child === null)
582
+ return null;
583
+ if (typeof child !== 'string') {
584
+ throw new Error(`Expected ${field} to be a string`);
585
+ }
586
+ return child;
587
+ }
588
+ function stringField(value, field) {
589
+ const object = objectValue(value);
590
+ const child = object[field];
591
+ if (typeof child !== 'string' || child.length === 0) {
592
+ throw new Error(`Expected ${field} to be a non-empty string`);
593
+ }
594
+ return child;
595
+ }
147
596
  function extractPulledCandidateDecisions(response, trustedDecisionKeys) {
148
597
  if (!response ||
149
598
  typeof response !== 'object' ||
@@ -180,14 +629,25 @@ function latestReceivedAt(records, decisions = []) {
180
629
  .sort()
181
630
  .at(-1);
182
631
  }
183
- function numberField(response, field) {
184
- if (!response || typeof response !== 'object') {
632
+ function numberField(response, field, required = true) {
633
+ const object = objectValue(response);
634
+ const value = object[field];
635
+ if (value === undefined || value === null) {
636
+ if (!required)
637
+ return null;
185
638
  throw new Error(`Context sync response did not include ${field}`);
186
639
  }
187
- const value = response[field];
188
640
  if (typeof value !== 'number') {
189
641
  throw new Error(`Context sync response ${field} must be a number`);
190
642
  }
191
643
  return value;
192
644
  }
645
+ function numberOrStringField(response, field) {
646
+ const object = objectValue(response);
647
+ const value = object[field];
648
+ if (typeof value !== 'number' && typeof value !== 'string') {
649
+ throw new Error(`Context sync response ${field} must be a number or string`);
650
+ }
651
+ return value;
652
+ }
193
653
  //# sourceMappingURL=local-edge-sync.js.map