@viewportai/daemon 0.5.3 → 0.6.1

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 (118) 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 +575 -38
  11. package/dist/cli/context-command.js.map +1 -1
  12. package/dist/cli/context-vault-metadata-command.d.ts.map +1 -1
  13. package/dist/cli/context-vault-metadata-command.js +6 -1
  14. package/dist/cli/context-vault-metadata-command.js.map +1 -1
  15. package/dist/cli/lifecycle-commands.d.ts.map +1 -1
  16. package/dist/cli/lifecycle-commands.js +2 -8
  17. package/dist/cli/lifecycle-commands.js.map +1 -1
  18. package/dist/cli/skills-command.js +3 -3
  19. package/dist/cli/unlock-command.d.ts +2 -0
  20. package/dist/cli/unlock-command.d.ts.map +1 -0
  21. package/dist/cli/unlock-command.js +35 -0
  22. package/dist/cli/unlock-command.js.map +1 -0
  23. package/dist/context/local-edge-auto-sync.d.ts +17 -0
  24. package/dist/context/local-edge-auto-sync.d.ts.map +1 -0
  25. package/dist/context/local-edge-auto-sync.js +94 -0
  26. package/dist/context/local-edge-auto-sync.js.map +1 -0
  27. package/dist/context/local-edge-store.d.ts +11 -0
  28. package/dist/context/local-edge-store.d.ts.map +1 -1
  29. package/dist/context/local-edge-store.js +25 -0
  30. package/dist/context/local-edge-store.js.map +1 -1
  31. package/dist/context/local-edge-sync.d.ts +2 -15
  32. package/dist/context/local-edge-sync.d.ts.map +1 -1
  33. package/dist/context/local-edge-sync.js +306 -86
  34. package/dist/context/local-edge-sync.js.map +1 -1
  35. package/dist/context/local-edge-types.d.ts +12 -0
  36. package/dist/context/local-edge-types.d.ts.map +1 -1
  37. package/dist/context-providers/viewport-vault-provider.d.ts.map +1 -1
  38. package/dist/context-providers/viewport-vault-provider.js +11 -0
  39. package/dist/context-providers/viewport-vault-provider.js.map +1 -1
  40. package/dist/core/session-context-prompt.d.ts.map +1 -1
  41. package/dist/core/session-context-prompt.js +8 -0
  42. package/dist/core/session-context-prompt.js.map +1 -1
  43. package/dist/hooks/trusted-edge-plan-artifacts.d.ts +30 -27
  44. package/dist/hooks/trusted-edge-plan-artifacts.d.ts.map +1 -1
  45. package/dist/hooks/trusted-edge-plan-artifacts.js +71 -89
  46. package/dist/hooks/trusted-edge-plan-artifacts.js.map +1 -1
  47. package/dist/index.d.ts +1 -0
  48. package/dist/index.d.ts.map +1 -1
  49. package/dist/index.js +3 -1
  50. package/dist/index.js.map +1 -1
  51. package/dist/relay/bridge-daemon-key-registration.d.ts.map +1 -1
  52. package/dist/relay/bridge-daemon-key-registration.js +27 -7
  53. package/dist/relay/bridge-daemon-key-registration.js.map +1 -1
  54. package/dist/security/epoch-enrollment.d.ts +48 -0
  55. package/dist/security/epoch-enrollment.d.ts.map +1 -0
  56. package/dist/security/epoch-enrollment.js +290 -0
  57. package/dist/security/epoch-enrollment.js.map +1 -0
  58. package/dist/security/epoch-protocol.d.ts +181 -0
  59. package/dist/security/epoch-protocol.d.ts.map +1 -0
  60. package/dist/security/epoch-protocol.js +285 -0
  61. package/dist/security/epoch-protocol.js.map +1 -0
  62. package/dist/security/epoch-public-pins.d.ts +19 -0
  63. package/dist/security/epoch-public-pins.d.ts.map +1 -0
  64. package/dist/security/epoch-public-pins.js +129 -0
  65. package/dist/security/epoch-public-pins.js.map +1 -0
  66. package/dist/security/epoch-recovery.d.ts +56 -0
  67. package/dist/security/epoch-recovery.d.ts.map +1 -0
  68. package/dist/security/epoch-recovery.js +314 -0
  69. package/dist/security/epoch-recovery.js.map +1 -0
  70. package/dist/security/epoch-store.d.ts +111 -0
  71. package/dist/security/epoch-store.d.ts.map +1 -0
  72. package/dist/security/epoch-store.js +224 -0
  73. package/dist/security/epoch-store.js.map +1 -0
  74. package/dist/security/epoch-sync.d.ts +47 -0
  75. package/dist/security/epoch-sync.d.ts.map +1 -0
  76. package/dist/security/epoch-sync.js +371 -0
  77. package/dist/security/epoch-sync.js.map +1 -0
  78. package/dist/security/team-epoch-grant-payloads.d.ts +44 -0
  79. package/dist/security/team-epoch-grant-payloads.d.ts.map +1 -0
  80. package/dist/security/team-epoch-grant-payloads.js +100 -0
  81. package/dist/security/team-epoch-grant-payloads.js.map +1 -0
  82. package/dist/security/team-epoch-grants.d.ts +31 -0
  83. package/dist/security/team-epoch-grants.d.ts.map +1 -0
  84. package/dist/security/team-epoch-grants.js +194 -0
  85. package/dist/security/team-epoch-grants.js.map +1 -0
  86. package/dist/server/http-context-routes.d.ts +2 -1
  87. package/dist/server/http-context-routes.d.ts.map +1 -1
  88. package/dist/server/http-context-routes.js +57 -15
  89. package/dist/server/http-context-routes.js.map +1 -1
  90. package/dist/server/http-server.js +1 -1
  91. package/dist/server/http-server.js.map +1 -1
  92. package/dist/server/rate-limiter.d.ts.map +1 -1
  93. package/dist/server/rate-limiter.js +2 -1
  94. package/dist/server/rate-limiter.js.map +1 -1
  95. package/dist/server/trusted-edge-command-capability.d.ts +2 -1
  96. package/dist/server/trusted-edge-command-capability.d.ts.map +1 -1
  97. package/dist/server/trusted-edge-command-capability.js +15 -0
  98. package/dist/server/trusted-edge-command-capability.js.map +1 -1
  99. package/dist/server/ws-command-handlers.d.ts.map +1 -1
  100. package/dist/server/ws-command-handlers.js +200 -28
  101. package/dist/server/ws-command-handlers.js.map +1 -1
  102. package/dist/server/ws-protocol.d.ts +281 -44
  103. package/dist/server/ws-protocol.d.ts.map +1 -1
  104. package/dist/server/ws-protocol.js +89 -19
  105. package/dist/server/ws-protocol.js.map +1 -1
  106. package/dist/startup.d.ts.map +1 -1
  107. package/dist/startup.js +0 -17
  108. package/dist/startup.js.map +1 -1
  109. package/docs/README.md +18 -0
  110. package/docs/configuration.md +3 -3
  111. package/docs/protocol-matrix.json +53 -8
  112. package/docs/security.md +11 -8
  113. package/node_modules/@viewportai/context-engine/src/repo/identities.js +7 -3
  114. package/node_modules/@viewportai/context-engine/src/repo/materializer.js +20 -5
  115. package/node_modules/@viewportai/context-engine/src/repo/membership.js +15 -0
  116. package/node_modules/@viewportai/context-engine/src/repo/sync.js +4 -4
  117. package/node_modules/@viewportai/context-engine/src/repo/vault.js +8 -3
  118. 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;
@@ -42,7 +43,7 @@ export declare function recordContextGrantMaterialization(options: {
42
43
  serverUrl: string;
43
44
  credential: string;
44
45
  contextResourceId: string;
45
- grantEventIds: string[];
46
+ receipts: SignedContextGrantMaterialization[];
46
47
  tlsVerify?: TlsVerifyMode;
47
48
  caCertPath?: string;
48
49
  tlsPins?: string[];
@@ -64,20 +65,6 @@ export declare function recordContextCandidatePreviewProof(options: {
64
65
  previewProofId: string;
65
66
  expiresAt: string | null;
66
67
  }>;
67
- export declare function publishContextPublicIdentity(options: {
68
- workspaceId: string;
69
- serverUrl: string;
70
- credential: string;
71
- identityName: string;
72
- tlsVerify?: TlsVerifyMode;
73
- caCertPath?: string;
74
- tlsPins?: string[];
75
- home?: string;
76
- fetchImpl?: typeof transportFetch;
77
- }): Promise<{
78
- identityId: string;
79
- fingerprint: string | null;
80
- }>;
81
68
  export declare function processPendingContextGrants(options: {
82
69
  contextResourceId: string;
83
70
  workspaceId: string;
@@ -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;AAgBvE,OAAO,KAAK,EAEV,kBAAkB,EAGnB,MAAM,uBAAuB,CAAC;AAI/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,kBAAkB,EAAE,MAAM,CAAC;IAC3B,yBAAyB,EAAE,MAAM,CAAC;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC,CAkGD;AAED,wBAAsB,iCAAiC,CAAC,OAAO,EAAE;IAC/D,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,cAAc,CAAC;CACnC,GAAG,OAAO,CAAC,MAAM,CAAC,CAoBlB;AAED,wBAAsB,kCAAkC,CAAC,OAAO,EAAE;IAChE,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,cAAc,CAAC;CACnC,GAAG,OAAO,CAAC;IAAE,cAAc,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CA+BhE;AAED,wBAAsB,4BAA4B,CAAC,OAAO,EAAE;IAC1D,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,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,UAAU,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAwB9D;AAED,wBAAsB,2BAA2B,CAAC,OAAO,EAAE;IACzD,iBAAiB,EAAE,MAAM,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,kBAAkB,CAAC;IAChC,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,OAAO,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CA6ExE;AAED,wBAAsB,gCAAgC,CAAC,OAAO,EAAE;IAC9D,iBAAiB,EAAE,MAAM,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,kBAAkB,CAAC;IAChC,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,OAAO,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAgFxE"}
1
+ {"version":3,"file":"local-edge-sync.d.ts","sourceRoot":"","sources":["../../src/context/local-edge-sync.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import { createHash } from 'node:crypto';
1
2
  import { configDir } from '../core/config.js';
2
3
  import { transportFetch } from '../cli/network.js';
3
4
  import { assertCredentialsOrApprovedDevice, createVault, ensureUserOrApprovedDevice, } from './local-edge-engine.js';
@@ -5,13 +6,23 @@ import { applyContextCandidateDecision } from './local-edge-candidates.js';
5
6
  import { readCandidateDecisionApplications } from './local-edge-decision-applications.js';
6
7
  import { verifyContextCandidateDecision } from './local-edge-decision-signature.js';
7
8
  import { readContextMetadata, touchContextMetadata } from './local-edge-metadata.js';
8
- import { exportContextIdentity, grantContextUser, importContextIdentity, revokeContextUser, } from './local-edge-store.js';
9
+ import { grantContextHpkeRecipient, revokeContextUser } from './local-edge-store.js';
10
+ import { validateAndPinPublicEpoch } from '../security/epoch-public-pins.js';
11
+ import { getActiveLocalUserEpoch, listActiveLocalTeamEpochs } from '../security/epoch-store.js';
12
+ import { TRUSTED_EDGE_CRYPTO_PROTOCOL_HEADER, TRUSTED_EDGE_CRYPTO_PROTOCOL_VERSION, contextGrantMaterializationPayload, signContextGrantMaterialization, } from '../security/epoch-protocol.js';
9
13
  const CONTEXT_GRANT_EVENT_TYPES = new Set(['member.granted', 'key.rotated']);
10
14
  export async function pushContextEvents(options) {
11
15
  const home = options.home ?? configDir();
12
16
  const metadata = await readContextMetadata(options.contextResourceId, home);
13
17
  const vault = createVault(home, metadata.keyStore);
14
18
  const events = vault.listSyncEvents({ repoId: metadata.repoId });
19
+ const publicIdentities = collectPublicIdentities(vault, [
20
+ metadata.userName,
21
+ metadata.deviceName,
22
+ ...events
23
+ .map((event) => nullableStringField(objectValue(event), 'actorName'))
24
+ .filter((name) => !!name),
25
+ ]);
15
26
  const candidateDecisionApplications = await readCandidateDecisionApplications({
16
27
  home,
17
28
  contextResourceId: options.contextResourceId,
@@ -23,6 +34,7 @@ export async function pushContextEvents(options) {
23
34
  credential: options.credential,
24
35
  ...(options.workspaceId ? { target_workspace_id: options.workspaceId } : {}),
25
36
  events,
37
+ ...(publicIdentities.length > 0 ? { public_identities: publicIdentities } : {}),
26
38
  ...(candidateDecisionApplications.length > 0
27
39
  ? { candidate_decision_applications: candidateDecisionApplications }
28
40
  : {}),
@@ -65,22 +77,31 @@ export async function pullContextEvents(options) {
65
77
  tlsPins: options.tlsPins,
66
78
  });
67
79
  const records = extractPulledRecords(response);
80
+ importPulledPublicIdentities(vault, response);
68
81
  const events = records.map((record) => record.signedEvent);
82
+ const grantIdentities = await contextGrantIdentitiesForWorkspace({
83
+ workspaceId: options.workspaceId ?? options.contextResourceId,
84
+ home,
85
+ });
69
86
  const imported = await vault.importSyncEvents({
70
87
  repoId: metadata.repoId,
71
88
  events,
72
89
  actorName: options.actorName,
90
+ grantIdentities,
91
+ });
92
+ const materializationReceipts = contextGrantMaterializationReceipts({
93
+ workspaceId: options.workspaceId ?? options.contextResourceId,
94
+ contextResourceId: options.contextResourceId,
95
+ events,
96
+ grantIdentities,
73
97
  });
74
- const materializedGrantEventIds = events
75
- .filter((event) => isGrantEventForUser(event, metadata.userName))
76
- .map((event) => event.id);
77
- const materializedGrants = materializedGrantEventIds.length > 0
98
+ const materializedGrants = materializationReceipts.length > 0
78
99
  ? await recordContextGrantMaterialization({
79
100
  workspaceId: options.workspaceId ?? options.contextResourceId,
80
101
  serverUrl: options.serverUrl,
81
102
  credential: options.credential,
82
103
  contextResourceId: options.contextResourceId,
83
- grantEventIds: materializedGrantEventIds,
104
+ receipts: materializationReceipts,
84
105
  tlsVerify: options.tlsVerify,
85
106
  caCertPath: options.caCertPath,
86
107
  tlsPins: options.tlsPins,
@@ -115,14 +136,67 @@ export async function pullContextEvents(options) {
115
136
  repoId: metadata.repoId,
116
137
  };
117
138
  }
139
+ function collectPublicIdentities(vault, names) {
140
+ const identities = new Map();
141
+ for (const name of names) {
142
+ if (!name)
143
+ continue;
144
+ try {
145
+ const publicIdentity = objectValue(vault.exportPublicIdentity(name));
146
+ const identityName = stringField(publicIdentity, 'name');
147
+ identities.set(identityName, {
148
+ name: identityName,
149
+ fingerprint: publicIdentityFingerprint(publicIdentity),
150
+ public_identity: publicIdentity,
151
+ });
152
+ }
153
+ catch (error) {
154
+ if (!isUnknownIdentityError(error)) {
155
+ throw error;
156
+ }
157
+ }
158
+ }
159
+ return [...identities.values()];
160
+ }
161
+ function importPulledPublicIdentities(vault, response) {
162
+ const object = objectValue(response);
163
+ const identities = Array.isArray(object.public_identities) ? object.public_identities : [];
164
+ for (const item of identities) {
165
+ const record = objectValue(item);
166
+ const identity = objectField(record, 'public_identity');
167
+ vault.importPublicIdentity(identity);
168
+ }
169
+ }
170
+ function publicIdentityFingerprint(identity) {
171
+ const canonical = JSON.stringify(sortJson(identity));
172
+ return `sha256:${createHash('sha256').update(canonical).digest('base64url')}`;
173
+ }
174
+ function sortJson(value) {
175
+ if (Array.isArray(value)) {
176
+ return value.map(sortJson);
177
+ }
178
+ if (value && typeof value === 'object') {
179
+ return Object.fromEntries(Object.entries(value)
180
+ .sort(([left], [right]) => left.localeCompare(right))
181
+ .map(([key, child]) => [key, sortJson(child)]));
182
+ }
183
+ return value;
184
+ }
185
+ function isUnknownIdentityError(error) {
186
+ return error instanceof Error && error.message.startsWith('Unknown identity:');
187
+ }
118
188
  export async function recordContextGrantMaterialization(options) {
119
- if (options.grantEventIds.length === 0) {
189
+ if (options.receipts.length === 0) {
120
190
  return 0;
121
191
  }
122
192
  const response = await postJson(options.fetchImpl ?? transportFetch, contextGrantMaterializedUrl(options.serverUrl, options.workspaceId), {
123
193
  credential: options.credential,
124
194
  context_resource_id: options.contextResourceId,
125
- grant_event_ids: options.grantEventIds,
195
+ receipts: options.receipts.map((receipt) => ({
196
+ payload: receipt.payload,
197
+ signature: receipt.signature,
198
+ signed_by_epoch_fingerprint: receipt.signedByEpochFingerprint,
199
+ })),
126
200
  }, {
127
201
  tlsVerify: options.tlsVerify,
128
202
  caCertPath: options.caCertPath,
@@ -155,26 +229,6 @@ export async function recordContextCandidatePreviewProof(options) {
155
229
  expiresAt: typeof expiresAt === 'string' ? expiresAt : null,
156
230
  };
157
231
  }
158
- export async function publishContextPublicIdentity(options) {
159
- const publicIdentity = exportContextIdentity({
160
- name: options.identityName,
161
- home: options.home,
162
- });
163
- const response = await postJson(options.fetchImpl ?? transportFetch, contextPublicIdentityUrl(options.serverUrl, options.workspaceId), {
164
- credential: options.credential,
165
- name: options.identityName,
166
- public_identity: publicIdentity,
167
- }, {
168
- tlsVerify: options.tlsVerify,
169
- caCertPath: options.caCertPath,
170
- tlsPins: options.tlsPins,
171
- });
172
- const identity = objectField(response, 'identity');
173
- return {
174
- identityId: stringField(identity, 'id'),
175
- fingerprint: nullableStringField(identity, 'fingerprint'),
176
- };
177
- }
178
232
  export async function processPendingContextGrants(options) {
179
233
  const fetchImpl = options.fetchImpl ?? transportFetch;
180
234
  const response = await postJson(fetchImpl, contextPendingGrantsUrl(options.serverUrl, options.workspaceId), {
@@ -191,49 +245,137 @@ export async function processPendingContextGrants(options) {
191
245
  let pushed = 0;
192
246
  for (const grant of grants) {
193
247
  const record = objectValue(grant);
194
- const recipient = objectField(record, 'recipient_identity', false);
195
- if (!recipient) {
196
- missingIdentity++;
248
+ const userEpoch = objectField(record, 'user_epoch', false);
249
+ if (userEpoch) {
250
+ const userEpochId = String(numberOrStringField(userEpoch, 'id'));
251
+ const userId = String(numberOrStringField(userEpoch, 'user_id'));
252
+ const epoch = numberField(userEpoch, 'epoch');
253
+ const fingerprint = stringField(userEpoch, 'fingerprint');
254
+ const encryptionPublicKeyJwk = objectField(userEpoch, 'encryption_public_key_jwk');
255
+ const signingPublicKeyJwk = objectField(userEpoch, 'signing_public_key_jwk');
256
+ await validateAndPinPublicEpoch({
257
+ platformEpochId: userEpochId,
258
+ workspaceId: options.workspaceId,
259
+ subjectType: 'user',
260
+ subjectId: userId,
261
+ epoch,
262
+ schema: 'viewport.user_crypto_epoch/v1',
263
+ fingerprint,
264
+ encryptionPublicKeyJwk: encryptionPublicKeyJwk,
265
+ signingPublicKeyJwk: signingPublicKeyJwk,
266
+ previousEpochFingerprint: nullableStringField(userEpoch, 'previous_epoch_fingerprint'),
267
+ continuityPayload: objectField(userEpoch, 'continuity_payload', false),
268
+ continuitySignature: nullableStringField(userEpoch, 'continuity_signature'),
269
+ signedByEpochFingerprint: nullableStringField(userEpoch, 'signed_by_epoch_fingerprint'),
270
+ }, options.home);
271
+ const recipientName = contextUserEpochRecipientName({ userEpochId, fingerprint });
272
+ const result = await grantContextHpkeRecipient({
273
+ contextResourceId: options.contextResourceId,
274
+ actorName: options.actorName,
275
+ recipientName,
276
+ recipientHpkePublicKey: jwkPublicXToBase64(encryptionPublicKeyJwk),
277
+ credentials: options.credentials,
278
+ home: options.home,
279
+ });
280
+ const event = objectValue(result.event);
281
+ const grantEventId = stringField(event, 'id');
282
+ const grantPayload = objectField(event, 'grant', false);
283
+ const keyEpoch = grantPayload ? numberField(grantPayload, 'keyEpoch', false) : null;
284
+ const pushResult = await pushContextEvents({
285
+ contextResourceId: options.contextResourceId,
286
+ workspaceId: options.workspaceId,
287
+ serverUrl: options.serverUrl,
288
+ credential: options.credential,
289
+ tlsVerify: options.tlsVerify,
290
+ caCertPath: options.caCertPath,
291
+ tlsPins: options.tlsPins,
292
+ home: options.home,
293
+ fetchImpl,
294
+ });
295
+ pushed += pushResult.accepted;
296
+ await postJson(fetchImpl, contextMarkGrantEmittedUrl(options.serverUrl, options.workspaceId), {
297
+ credential: options.credential,
298
+ crypto_grant_id: stringField(record, 'id'),
299
+ grant_event_id: grantEventId,
300
+ recipient_identity_name: recipientName,
301
+ recipient_type: 'user_epoch',
302
+ recipient_epoch_id: userEpochId,
303
+ recipient_fingerprint: fingerprint,
304
+ ...(keyEpoch !== null ? { key_epoch: keyEpoch } : {}),
305
+ }, {
306
+ tlsVerify: options.tlsVerify,
307
+ caCertPath: options.caCertPath,
308
+ tlsPins: options.tlsPins,
309
+ });
310
+ emitted++;
197
311
  continue;
198
312
  }
199
- const publicIdentity = objectField(recipient, 'public_identity');
200
- const recipientName = stringField(recipient, 'name');
201
- importContextIdentity({ identity: publicIdentity, home: options.home });
202
- const result = await grantContextUser({
203
- contextResourceId: options.contextResourceId,
204
- actorName: options.actorName,
205
- recipientName,
206
- credentials: options.credentials,
207
- home: options.home,
208
- });
209
- const event = objectValue(result.event);
210
- const grantEventId = stringField(event, 'id');
211
- const grantPayload = objectField(event, 'grant', false);
212
- const keyEpoch = grantPayload ? numberField(grantPayload, 'keyEpoch', false) : null;
213
- const pushResult = await pushContextEvents({
214
- contextResourceId: options.contextResourceId,
215
- workspaceId: options.workspaceId,
216
- serverUrl: options.serverUrl,
217
- credential: options.credential,
218
- tlsVerify: options.tlsVerify,
219
- caCertPath: options.caCertPath,
220
- tlsPins: options.tlsPins,
221
- home: options.home,
222
- fetchImpl,
223
- });
224
- pushed += pushResult.accepted;
225
- await postJson(fetchImpl, contextMarkGrantEmittedUrl(options.serverUrl, options.workspaceId), {
226
- credential: options.credential,
227
- crypto_grant_id: stringField(record, 'id'),
228
- grant_event_id: grantEventId,
229
- recipient_identity_name: recipientName,
230
- ...(keyEpoch !== null ? { key_epoch: keyEpoch } : {}),
231
- }, {
232
- tlsVerify: options.tlsVerify,
233
- caCertPath: options.caCertPath,
234
- tlsPins: options.tlsPins,
235
- });
236
- emitted++;
313
+ const teamEpoch = objectField(record, 'team_epoch', false);
314
+ if (teamEpoch) {
315
+ const teamEpochId = String(numberOrStringField(teamEpoch, 'id'));
316
+ const teamId = String(numberOrStringField(teamEpoch, 'team_id'));
317
+ const epoch = numberField(teamEpoch, 'epoch');
318
+ const fingerprint = stringField(teamEpoch, 'fingerprint');
319
+ const encryptionPublicKeyJwk = objectField(teamEpoch, 'encryption_public_key_jwk');
320
+ const signingPublicKeyJwk = objectField(teamEpoch, 'signing_public_key_jwk');
321
+ await validateAndPinPublicEpoch({
322
+ platformEpochId: teamEpochId,
323
+ workspaceId: options.workspaceId,
324
+ subjectType: 'team',
325
+ subjectId: teamId,
326
+ epoch,
327
+ schema: 'viewport.team_crypto_epoch/v1',
328
+ fingerprint,
329
+ encryptionPublicKeyJwk: encryptionPublicKeyJwk,
330
+ signingPublicKeyJwk: signingPublicKeyJwk,
331
+ previousEpochFingerprint: nullableStringField(teamEpoch, 'previous_epoch_fingerprint'),
332
+ continuityPayload: objectField(teamEpoch, 'continuity_payload', false),
333
+ continuitySignature: nullableStringField(teamEpoch, 'continuity_signature'),
334
+ signedByEpochFingerprint: nullableStringField(teamEpoch, 'signed_by_epoch_fingerprint'),
335
+ }, options.home);
336
+ const recipientName = contextTeamEpochRecipientName({ teamEpochId, fingerprint });
337
+ const result = await grantContextHpkeRecipient({
338
+ contextResourceId: options.contextResourceId,
339
+ actorName: options.actorName,
340
+ recipientName,
341
+ recipientHpkePublicKey: jwkPublicXToBase64(encryptionPublicKeyJwk),
342
+ credentials: options.credentials,
343
+ home: options.home,
344
+ });
345
+ const event = objectValue(result.event);
346
+ const grantEventId = stringField(event, 'id');
347
+ const grantPayload = objectField(event, 'grant', false);
348
+ const keyEpoch = grantPayload ? numberField(grantPayload, 'keyEpoch', false) : null;
349
+ const pushResult = await pushContextEvents({
350
+ contextResourceId: options.contextResourceId,
351
+ workspaceId: options.workspaceId,
352
+ serverUrl: options.serverUrl,
353
+ credential: options.credential,
354
+ tlsVerify: options.tlsVerify,
355
+ caCertPath: options.caCertPath,
356
+ tlsPins: options.tlsPins,
357
+ home: options.home,
358
+ fetchImpl,
359
+ });
360
+ pushed += pushResult.accepted;
361
+ await postJson(fetchImpl, contextMarkGrantEmittedUrl(options.serverUrl, options.workspaceId), {
362
+ credential: options.credential,
363
+ crypto_grant_id: stringField(record, 'id'),
364
+ grant_event_id: grantEventId,
365
+ recipient_identity_name: recipientName,
366
+ recipient_type: 'team_epoch',
367
+ recipient_epoch_id: teamEpochId,
368
+ recipient_fingerprint: fingerprint,
369
+ ...(keyEpoch !== null ? { key_epoch: keyEpoch } : {}),
370
+ }, {
371
+ tlsVerify: options.tlsVerify,
372
+ caCertPath: options.caCertPath,
373
+ tlsPins: options.tlsPins,
374
+ });
375
+ emitted++;
376
+ continue;
377
+ }
378
+ missingIdentity++;
237
379
  }
238
380
  return { emitted, missingIdentity, pushed };
239
381
  }
@@ -253,9 +395,7 @@ export async function processPendingContextRevocations(options) {
253
395
  let pushed = 0;
254
396
  for (const revocation of revocations) {
255
397
  const record = objectValue(revocation);
256
- const recipient = objectField(record, 'recipient_identity', false);
257
- const recipientName = nullableStringField(record, 'recipient_identity_name') ??
258
- (recipient ? nullableStringField(recipient, 'name') : null);
398
+ const recipientName = nullableStringField(record, 'recipient_identity_name');
259
399
  if (!recipientName) {
260
400
  missingIdentity++;
261
401
  continue;
@@ -317,6 +457,84 @@ function contextGrantRotationReceipt(event) {
317
457
  ...(recipientName ? { recipient_identity_name: recipientName } : {}),
318
458
  };
319
459
  }
460
+ async function contextGrantIdentitiesForWorkspace(options) {
461
+ const userEpoch = await getActiveLocalUserEpoch(options.workspaceId, options.home);
462
+ const teamEpochs = await listActiveLocalTeamEpochs(options.workspaceId, options.home);
463
+ return [
464
+ ...(userEpoch?.platformEpochId
465
+ ? [
466
+ {
467
+ kind: 'user_epoch',
468
+ name: contextUserEpochRecipientName({
469
+ userEpochId: userEpoch.platformEpochId,
470
+ fingerprint: userEpoch.fingerprint,
471
+ }),
472
+ hpkePrivateKey: jwkPrivateDToBase64(objectValue(userEpoch.encryptionPrivateKeyJwk)),
473
+ signingPrivateKeyJwk: userEpoch.signingPrivateKeyJwk,
474
+ signerFingerprint: userEpoch.fingerprint,
475
+ },
476
+ ]
477
+ : []),
478
+ ...teamEpochs.map((epoch) => ({
479
+ kind: 'team_epoch',
480
+ name: contextTeamEpochRecipientName({
481
+ teamEpochId: epoch.platformEpochId ?? `${epoch.platformTeamId ?? epoch.teamId}:${epoch.epoch}`,
482
+ fingerprint: epoch.fingerprint,
483
+ }),
484
+ hpkePrivateKey: jwkPrivateDToBase64(objectValue(epoch.encryptionPrivateKeyJwk)),
485
+ signingPrivateKeyJwk: epoch.signingPrivateKeyJwk,
486
+ signerFingerprint: epoch.fingerprint,
487
+ })),
488
+ ];
489
+ }
490
+ function contextGrantMaterializationReceipts(options) {
491
+ const receipts = [];
492
+ for (const event of options.events) {
493
+ const match = grantEventRecipientMatch(event, options.grantIdentities);
494
+ if (!match)
495
+ continue;
496
+ const keyEpoch = numberField(event, 'keyEpoch', false);
497
+ const payload = contextGrantMaterializationPayload({
498
+ workspaceId: options.workspaceId,
499
+ contextResourceId: options.contextResourceId,
500
+ grantEventId: stringField(event, 'id'),
501
+ recipientName: match.identity.name,
502
+ keyEpoch,
503
+ });
504
+ receipts.push(signContextGrantMaterialization({
505
+ payload,
506
+ signingPrivateKeyJwk: match.identity.signingPrivateKeyJwk,
507
+ signedByEpochFingerprint: match.identity.signerFingerprint,
508
+ }));
509
+ }
510
+ return receipts;
511
+ }
512
+ function grantEventRecipientMatch(event, grantIdentities) {
513
+ if (!CONTEXT_GRANT_EVENT_TYPES.has(event.type))
514
+ return null;
515
+ const grant = event.grant;
516
+ if (!grant || typeof grant !== 'object' || Array.isArray(grant))
517
+ return null;
518
+ const recipientName = grant.recipientName;
519
+ if (typeof recipientName !== 'string' || recipientName === '')
520
+ return null;
521
+ const identity = grantIdentities.find((candidate) => candidate.name === recipientName);
522
+ return identity ? { grant: grant, identity } : null;
523
+ }
524
+ function contextUserEpochRecipientName(input) {
525
+ return `user-epoch:${input.userEpochId}:${input.fingerprint}`;
526
+ }
527
+ function contextTeamEpochRecipientName(input) {
528
+ return `team-epoch:${input.teamEpochId}:${input.fingerprint}`;
529
+ }
530
+ function jwkPublicXToBase64(jwk) {
531
+ const x = stringField(jwk, 'x');
532
+ return Buffer.from(x, 'base64url').toString('base64');
533
+ }
534
+ function jwkPrivateDToBase64(jwk) {
535
+ const d = stringField(jwk, 'd');
536
+ return Buffer.from(d, 'base64url').toString('base64');
537
+ }
320
538
  function contextRuntimeUrl(serverUrl, workspaceId, operation) {
321
539
  const base = serverUrl.replace(/\/+$/, '');
322
540
  return `${base}/api/runtime/workspaces/${encodeURIComponent(workspaceId)}/context-vault/events/${operation}`;
@@ -325,10 +543,6 @@ function contextCandidatePreviewProofUrl(serverUrl, workspaceId) {
325
543
  const base = serverUrl.replace(/\/+$/, '');
326
544
  return `${base}/api/runtime/workspaces/${encodeURIComponent(workspaceId)}/context-vault/candidates/preview-proof`;
327
545
  }
328
- function contextPublicIdentityUrl(serverUrl, workspaceId) {
329
- const base = serverUrl.replace(/\/+$/, '');
330
- return `${base}/api/runtime/workspaces/${encodeURIComponent(workspaceId)}/context-vault/identities`;
331
- }
332
546
  function contextPendingGrantsUrl(serverUrl, workspaceId) {
333
547
  const base = serverUrl.replace(/\/+$/, '');
334
548
  return `${base}/api/runtime/workspaces/${encodeURIComponent(workspaceId)}/context-vault/grants/pending`;
@@ -352,7 +566,11 @@ function contextGrantMaterializedUrl(serverUrl, workspaceId) {
352
566
  async function postJson(fetchImpl, url, body, transportOptions = {}) {
353
567
  const response = await fetchImpl(url, {
354
568
  method: 'POST',
355
- headers: { 'content-type': 'application/json', accept: 'application/json' },
569
+ headers: {
570
+ 'content-type': 'application/json',
571
+ accept: 'application/json',
572
+ [TRUSTED_EDGE_CRYPTO_PROTOCOL_HEADER]: TRUSTED_EDGE_CRYPTO_PROTOCOL_VERSION,
573
+ },
356
574
  body: JSON.stringify(body),
357
575
  timeoutMs: 5_000,
358
576
  ...transportOptions,
@@ -367,7 +585,9 @@ async function postJson(fetchImpl, url, body, transportOptions = {}) {
367
585
  if (!response.ok) {
368
586
  const reason = typeof payload === 'object' && payload && 'reason' in payload
369
587
  ? String(payload.reason)
370
- : `${response.status} ${response.statusText}`;
588
+ : typeof payload === 'object' && payload && 'message' in payload
589
+ ? String(payload.message)
590
+ : `${response.status} ${response.statusText}`;
371
591
  throw new Error(`Context sync request failed: ${reason}`);
372
592
  }
373
593
  return payload;
@@ -458,14 +678,6 @@ function extractPulledCandidateDecisions(response, trustedDecisionKeys) {
458
678
  return record;
459
679
  });
460
680
  }
461
- function isGrantEventForUser(event, userName) {
462
- if (!CONTEXT_GRANT_EVENT_TYPES.has(event.type))
463
- return false;
464
- const grant = event.grant;
465
- if (!grant || typeof grant !== 'object' || Array.isArray(grant))
466
- return false;
467
- return grant.recipientName === userName;
468
- }
469
681
  function latestReceivedAt(records, decisions = []) {
470
682
  return [
471
683
  ...records
@@ -491,4 +703,12 @@ function numberField(response, field, required = true) {
491
703
  }
492
704
  return value;
493
705
  }
706
+ function numberOrStringField(response, field) {
707
+ const object = objectValue(response);
708
+ const value = object[field];
709
+ if (typeof value !== 'number' && typeof value !== 'string') {
710
+ throw new Error(`Context sync response ${field} must be a number or string`);
711
+ }
712
+ return value;
713
+ }
494
714
  //# sourceMappingURL=local-edge-sync.js.map