@opendatalabs/personal-server-ts-core 1.6.0 → 1.8.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 (106) hide show
  1. package/dist/api/index.d.ts +53 -2
  2. package/dist/api/index.d.ts.map +1 -1
  3. package/dist/api/index.js +300 -25
  4. package/dist/api/index.js.map +1 -1
  5. package/dist/contracts/data.d.ts +30 -1
  6. package/dist/contracts/data.d.ts.map +1 -1
  7. package/dist/contracts/data.js +126 -33
  8. package/dist/contracts/data.js.map +1 -1
  9. package/dist/errors/catalog.d.ts +40 -0
  10. package/dist/errors/catalog.d.ts.map +1 -1
  11. package/dist/errors/catalog.js +53 -0
  12. package/dist/errors/catalog.js.map +1 -1
  13. package/dist/lineage/attestation.d.ts +42 -0
  14. package/dist/lineage/attestation.d.ts.map +1 -0
  15. package/dist/lineage/attestation.js +25 -0
  16. package/dist/lineage/attestation.js.map +1 -0
  17. package/dist/lineage/gateway.d.ts +105 -0
  18. package/dist/lineage/gateway.d.ts.map +1 -0
  19. package/dist/lineage/gateway.js +242 -0
  20. package/dist/lineage/gateway.js.map +1 -0
  21. package/dist/lineage/index.d.ts +5 -0
  22. package/dist/lineage/index.d.ts.map +1 -0
  23. package/dist/lineage/index.js +5 -0
  24. package/dist/lineage/index.js.map +1 -0
  25. package/dist/lineage/lineage.d.ts +141 -0
  26. package/dist/lineage/lineage.d.ts.map +1 -0
  27. package/dist/lineage/lineage.js +255 -0
  28. package/dist/lineage/lineage.js.map +1 -0
  29. package/dist/logging/access-log.d.ts +7 -1
  30. package/dist/logging/access-log.d.ts.map +1 -1
  31. package/dist/mcp/read-client.d.ts.map +1 -1
  32. package/dist/mcp/read-client.js +25 -1
  33. package/dist/mcp/read-client.js.map +1 -1
  34. package/dist/mcp/session.d.ts.map +1 -1
  35. package/dist/mcp/session.js +10 -1
  36. package/dist/mcp/session.js.map +1 -1
  37. package/dist/ports/index.d.ts +125 -0
  38. package/dist/ports/index.d.ts.map +1 -1
  39. package/dist/ports/index.js.map +1 -1
  40. package/dist/signing/request-signer.d.ts +2 -0
  41. package/dist/signing/request-signer.d.ts.map +1 -1
  42. package/dist/signing/request-signer.js +1 -0
  43. package/dist/signing/request-signer.js.map +1 -1
  44. package/dist/signing/signer.d.ts +8 -0
  45. package/dist/signing/signer.d.ts.map +1 -1
  46. package/dist/signing/signer.js +9 -0
  47. package/dist/signing/signer.js.map +1 -1
  48. package/dist/storage/adapters/index.d.ts +1 -1
  49. package/dist/storage/adapters/index.d.ts.map +1 -1
  50. package/dist/storage/adapters/index.js +1 -1
  51. package/dist/storage/adapters/index.js.map +1 -1
  52. package/dist/storage/adapters/vana.d.ts +2 -0
  53. package/dist/storage/adapters/vana.d.ts.map +1 -1
  54. package/dist/storage/adapters/vana.js +5 -1
  55. package/dist/storage/adapters/vana.js.map +1 -1
  56. package/dist/storage/index/types.d.ts +1 -0
  57. package/dist/storage/index/types.d.ts.map +1 -1
  58. package/dist/sync/data-point-feed.d.ts +35 -0
  59. package/dist/sync/data-point-feed.d.ts.map +1 -0
  60. package/dist/sync/data-point-feed.js +122 -0
  61. package/dist/sync/data-point-feed.js.map +1 -0
  62. package/dist/sync/data-point-id.d.ts +7 -0
  63. package/dist/sync/data-point-id.d.ts.map +1 -0
  64. package/dist/sync/data-point-id.js +16 -0
  65. package/dist/sync/data-point-id.js.map +1 -0
  66. package/dist/sync/delete-data-port.d.ts +50 -0
  67. package/dist/sync/delete-data-port.d.ts.map +1 -0
  68. package/dist/sync/delete-data-port.js +244 -0
  69. package/dist/sync/delete-data-port.js.map +1 -0
  70. package/dist/sync/engine/sync-manager.d.ts +14 -4
  71. package/dist/sync/engine/sync-manager.d.ts.map +1 -1
  72. package/dist/sync/engine/sync-manager.js +53 -8
  73. package/dist/sync/engine/sync-manager.js.map +1 -1
  74. package/dist/sync/index.d.ts +7 -0
  75. package/dist/sync/index.d.ts.map +1 -1
  76. package/dist/sync/index.js +7 -0
  77. package/dist/sync/index.js.map +1 -1
  78. package/dist/sync/pending-blob-deletions.d.ts +29 -0
  79. package/dist/sync/pending-blob-deletions.d.ts.map +1 -0
  80. package/dist/sync/pending-blob-deletions.js +116 -0
  81. package/dist/sync/pending-blob-deletions.js.map +1 -0
  82. package/dist/sync/scope-deletions.d.ts +155 -0
  83. package/dist/sync/scope-deletions.d.ts.map +1 -0
  84. package/dist/sync/scope-deletions.js +225 -0
  85. package/dist/sync/scope-deletions.js.map +1 -0
  86. package/dist/sync/tombstone.d.ts +27 -0
  87. package/dist/sync/tombstone.d.ts.map +1 -0
  88. package/dist/sync/tombstone.js +28 -0
  89. package/dist/sync/tombstone.js.map +1 -0
  90. package/dist/sync/workers/delete.d.ts +152 -31
  91. package/dist/sync/workers/delete.d.ts.map +1 -1
  92. package/dist/sync/workers/delete.js +461 -27
  93. package/dist/sync/workers/delete.js.map +1 -1
  94. package/dist/sync/workers/download.d.ts +38 -1
  95. package/dist/sync/workers/download.d.ts.map +1 -1
  96. package/dist/sync/workers/download.js +106 -2
  97. package/dist/sync/workers/download.js.map +1 -1
  98. package/dist/sync/workers/upload.d.ts +41 -7
  99. package/dist/sync/workers/upload.d.ts.map +1 -1
  100. package/dist/sync/workers/upload.js +147 -21
  101. package/dist/sync/workers/upload.js.map +1 -1
  102. package/dist/write/attribution.d.ts +6 -4
  103. package/dist/write/attribution.d.ts.map +1 -1
  104. package/dist/write/attribution.js +51 -3
  105. package/dist/write/attribution.js.map +1 -1
  106. package/package.json +5 -1
@@ -3,10 +3,14 @@ import type { AccessLogReader } from "../logging/access-reader.js";
3
3
  import { type DataStoragePort, type RuntimeAvailabilityPort } from "../ports/index.js";
4
4
  import type { DataReadPolicyPorts } from "../policy/index.js";
5
5
  import type { SyncManager } from "../sync/index.js";
6
+ import { type ScopeDeletionTracker } from "../sync/scope-deletions.js";
7
+ import type { IndexEntry } from "../storage/index/types.js";
8
+ import { type DeleteScopeResult } from "../sync/workers/delete.js";
6
9
  import { type OAuthDeviceSessionLookup, type OAuthTokenStorePort } from "../contracts/index.js";
7
10
  import type { DataPortabilityGatewayConfig, GatewayClient } from "@opendatalabs/vana-sdk/browser";
8
11
  import type { ServerSigner } from "../signing/index.js";
9
12
  import type { WriterAttribution } from "../write/attribution.js";
13
+ import type { LineageGatewayPort } from "../lineage/gateway.js";
10
14
  import { type X402Challenge } from "../payment/index.js";
11
15
  export interface PersonalServerReadAuthInput {
12
16
  request: Request;
@@ -83,8 +87,12 @@ export interface PersonalServerApiLogger {
83
87
  export interface PersonalServerIngestSyncManager {
84
88
  trigger(): Promise<void>;
85
89
  notifyNewData?(): void;
86
- /** Propagate a scope deletion to R2 + the gateway before the local delete. */
87
- deleteScopeRemote?(scope: string): Promise<void>;
90
+ /**
91
+ * Durable scope deletion (gateway tombstone -> storage blobs -> local).
92
+ * When absent the DELETE route deletes the local copy only and reports
93
+ * `durable: false`.
94
+ */
95
+ deleteScope?(scope: string): Promise<DeleteScopeResult>;
88
96
  }
89
97
  export interface PersonalServerDataApiDeps {
90
98
  storage: DataStoragePort;
@@ -93,6 +101,15 @@ export interface PersonalServerDataApiDeps {
93
101
  syncManager?: PersonalServerIngestSyncManager | null;
94
102
  runtimeAvailability?: RuntimeAvailabilityPort;
95
103
  readFulfillmentReporter?: PersonalServerReadFulfillmentReporter;
104
+ /**
105
+ * Read-side memory of gateway tombstones (see sync/scope-deletions.ts for
106
+ * the consistency window). Every read consults it before serving or
107
+ * charging: a scope the gateway reports deleted answers 410 DATA_DELETED
108
+ * whether or not a stale local copy still exists, and a local miss on a
109
+ * deleted scope answers 410 instead of 404 so SDK readers can tell
110
+ * "deleted" from "never had it". Without it reads are local-only.
111
+ */
112
+ scopeDeletions?: ScopeDeletionTracker;
96
113
  /**
97
114
  * Required when payment is on. Powers two things on GET /v1/data/:scope:
98
115
  * - the X402 challenge generation (fee lookup, accessRecord binding)
@@ -155,6 +172,13 @@ export interface PersonalServerDataApiDeps {
155
172
  now?: () => Date;
156
173
  createLogId?: () => string;
157
174
  logger?: PersonalServerApiLogger;
175
+ /**
176
+ * Gateway access for derivative data (docs/derivative-data-api.md): source
177
+ * lookups for lineage validation on write and the signed lineage read
178
+ * behind GET /v1/data/:scope/lineage. Absent = writes with lineage can
179
+ * only cite local scopes and the lineage read answers 503.
180
+ */
181
+ lineageGateway?: LineageGatewayPort;
158
182
  }
159
183
  export interface PersonalServerAccessLogsApiDeps {
160
184
  auth: Pick<PersonalServerApiAuthPort, "authorizeOwner">;
@@ -229,6 +253,33 @@ export type X402CycleResult = {
229
253
  * 409 (replay) from 400 (amount mismatch).
230
254
  */
231
255
  export declare function handleX402Cycle(input: X402CycleInput): Promise<X402CycleResult>;
256
+ /**
257
+ * The deletion that applies to a read of `scope`, or null when the read may
258
+ * be served. `entry` is the local version the read would serve (undefined
259
+ * on a local miss).
260
+ *
261
+ * - A local copy is refused when the gateway holds a tombstone that covers
262
+ * it (see `isEntryCoveredByTombstone`: registry versions and the ingest
263
+ * marker, never clocks). A local re-add on top of the tombstone is served.
264
+ * - A local miss asks the tracker to consult the gateway (bounded by its
265
+ * per-scope verdict cache and failure back-off): the local index cannot
266
+ * tell "deleted long ago" from "never had it".
267
+ * Gateway unreachability never fails the read: the tracker answers from its
268
+ * last known state, which is the documented offline behaviour.
269
+ */
270
+ export declare function resolveReadDeletion(deps: Pick<PersonalServerDataApiDeps, "scopeDeletions" | "serverOwner">, scope: string, entry: ReadDeletionEntry | null | undefined): Promise<{
271
+ scope: string;
272
+ dataPointId: string | null;
273
+ deletedAt: string;
274
+ } | null>;
275
+ /**
276
+ * Throw 410 DATA_DELETED when `resolveReadDeletion` says the read must not be
277
+ * served. Call it before any payment step: a builder must never be charged
278
+ * for a scope the owner deleted.
279
+ */
280
+ export declare function assertScopeNotDeleted(deps: Pick<PersonalServerDataApiDeps, "scopeDeletions" | "serverOwner">, scope: string, entry: ReadDeletionEntry | null | undefined): Promise<void>;
281
+ /** The index fields the deletion gate needs from the entry a read would serve. */
282
+ export type ReadDeletionEntry = Pick<IndexEntry, "version" | "dataPointId" | "afterTombstoneVersion">;
232
283
  export declare function reportPersonalServerReadFulfillment(deps: PersonalServerDataApiDeps, event: PersonalServerReadFulfillment): void;
233
284
  export declare function handlePersonalServerDataRequest(request: Request, deps: PersonalServerDataApiDeps, options?: PersonalServerApiDispatchOptions): Promise<Response>;
234
285
  export declare function handlePersonalServerAccessLogsRequest(request: Request, deps: PersonalServerAccessLogsApiDeps, options?: PersonalServerApiDispatchOptions): Promise<Response>;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/api/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/api/index.ts"],"names":[],"mappings":"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"}
package/dist/api/index.js CHANGED
@@ -1,5 +1,9 @@
1
- import { ProtocolError } from "../errors/catalog.js";
2
- import { deleteDataScopeContract, ingestDataContract, ingestBinaryDataContract, listAccessLogsContract, listDataScopesContract, listDataVersionsContract, parseDataScopeContract, parseJsonObjectBody, readDataContract, syncFileContract, triggerSyncContract, getSyncStatusContract, configReadErrorContract, configWriteErrorContract, createGrantContract, listGrantsContract, oauthTokenContract, revokeGrantContract, validateServerConfigContract, verifyGrantContract, decodeBinaryEnvelope, isBinaryEnvelope, parseMetadataHeader, stringifyMetadataHeader, IngestPersistedError, } from "../contracts/index.js";
1
+ import { DataDeletedError, DeleteTombstoneFailedError, GrantRequiredError, InvalidCascadeError, LineageCascadeUnavailableError, LineageGatewayError, LineageUnavailableError, ProtocolError, ServerNotConfiguredError, } from "../errors/catalog.js";
2
+ import { computeDataPointId } from "../sync/data-point-id.js";
3
+ import { isEntryCoveredByTombstone, } from "../sync/scope-deletions.js";
4
+ import { deleteScope as deleteScopeLocally, } from "../sync/workers/delete.js";
5
+ import { ingestDataContract, ingestBinaryDataContract, listAccessLogsContract, listDataScopesContract, listDataVersionsContract, parseDataScopeContract, parseJsonObjectBody, readDataContract, syncFileContract, triggerSyncContract, getSyncStatusContract, configReadErrorContract, configWriteErrorContract, createGrantContract, listGrantsContract, oauthTokenContract, revokeGrantContract, validateServerConfigContract, verifyGrantContract, decodeBinaryEnvelope, isBinaryEnvelope, parseMetadataHeader, stringifyMetadataHeader, IngestPersistedError, } from "../contracts/index.js";
6
+ import { extractLineageField, prepareLineage, } from "../lineage/lineage.js";
3
7
  import { binaryFilename, binaryMimeType, isJsonContentType, } from "../contracts/binary.js";
4
8
  import { buildChallenge, parsePaymentHeader, verifyPayment, } from "../payment/index.js";
5
9
  function jsonResponse(body, init) {
@@ -250,6 +254,86 @@ function collectedAt(now) {
250
254
  .toISOString()
251
255
  .replace(/\.\d{3}Z$/, "Z");
252
256
  }
257
+ /**
258
+ * The deletion that applies to a read of `scope`, or null when the read may
259
+ * be served. `entry` is the local version the read would serve (undefined
260
+ * on a local miss).
261
+ *
262
+ * - A local copy is refused when the gateway holds a tombstone that covers
263
+ * it (see `isEntryCoveredByTombstone`: registry versions and the ingest
264
+ * marker, never clocks). A local re-add on top of the tombstone is served.
265
+ * - A local miss asks the tracker to consult the gateway (bounded by its
266
+ * per-scope verdict cache and failure back-off): the local index cannot
267
+ * tell "deleted long ago" from "never had it".
268
+ * Gateway unreachability never fails the read: the tracker answers from its
269
+ * last known state, which is the documented offline behaviour.
270
+ */
271
+ export async function resolveReadDeletion(deps, scope, entry) {
272
+ if (!deps.scopeDeletions)
273
+ return null;
274
+ const verdict = await deps.scopeDeletions.resolve(scope, {
275
+ consultGateway: entry ? "if-stale" : "always",
276
+ });
277
+ if (!verdict.deleted)
278
+ return null;
279
+ if (entry && !isEntryCoveredByTombstone(entry, verdict)) {
280
+ return null;
281
+ }
282
+ return {
283
+ scope,
284
+ dataPointId: deps.serverOwner
285
+ ? computeDataPointId(deps.serverOwner, scope)
286
+ : null,
287
+ deletedAt: verdict.deletedAt,
288
+ };
289
+ }
290
+ /**
291
+ * Throw 410 DATA_DELETED when `resolveReadDeletion` says the read must not be
292
+ * served. Call it before any payment step: a builder must never be charged
293
+ * for a scope the owner deleted.
294
+ */
295
+ export async function assertScopeNotDeleted(deps, scope, entry) {
296
+ const deletion = await resolveReadDeletion(deps, scope, entry);
297
+ if (deletion)
298
+ throw new DataDeletedError(deletion);
299
+ }
300
+ /**
301
+ * Discovery filter for the scope and version listings: an entry a read would
302
+ * refuse with 410 is not listed either. Undefined (no filtering) when no
303
+ * deletion tracker is wired.
304
+ */
305
+ function discoveryVisibility(deps) {
306
+ if (!deps.scopeDeletions)
307
+ return undefined;
308
+ return async (scope, entry) => (await resolveReadDeletion(deps, scope, entry)) === null;
309
+ }
310
+ /**
311
+ * The causal marker a fresh ingest carries: the tombstone version this
312
+ * replica currently knows for the scope, if any, so the new entry is a
313
+ * deliberate re-add that survives that deletion (and only that one). Uses
314
+ * the same verdict path as reads, so it costs a gateway lookup only when the
315
+ * tracker's view is stale. An unknown or unsafe version leaves the marker
316
+ * null: the entry is then covered, never resurrected on a guess.
317
+ */
318
+ async function ingestTombstoneMarker(deps, scope) {
319
+ if (!deps.scopeDeletions)
320
+ return null;
321
+ const verdict = await deps.scopeDeletions.resolve(scope);
322
+ if (!verdict.deleted || verdict.version === null)
323
+ return null;
324
+ const version = Number(verdict.version);
325
+ return Number.isSafeInteger(version) ? version : null;
326
+ }
327
+ // The delete worker wants a full Logger; the API logger is all-optional.
328
+ function apiLoggerAsLogger(logger) {
329
+ const noop = () => undefined;
330
+ return {
331
+ debug: (payload, message) => (logger?.debug ?? noop)(payload, message ?? ""),
332
+ info: (payload, message) => (logger?.info ?? noop)(payload, message ?? ""),
333
+ warn: (payload, message) => (logger?.warn ?? noop)(payload, message ?? ""),
334
+ error: (payload, message) => (logger?.error ?? noop)(payload, message ?? ""),
335
+ };
336
+ }
253
337
  function notifyNewData(syncManager) {
254
338
  if (!syncManager)
255
339
  return;
@@ -283,6 +367,47 @@ export function reportPersonalServerReadFulfillment(deps, event) {
283
367
  warnReadFulfillmentReporterFailed(deps, event, err);
284
368
  }
285
369
  }
370
+ /**
371
+ * Validate a write's caller-supplied `lineage` (JSON body top level, or the
372
+ * binary metadata object) into the `$lineage` record to stamp. `undefined`
373
+ * (or null) means a root record. Throws ProtocolErrors; the write handler
374
+ * lets them propagate before anything is stored.
375
+ */
376
+ async function prepareWriteLineage(deps, scope, field) {
377
+ if (field === undefined || field === null)
378
+ return undefined;
379
+ return prepareLineage({
380
+ scope,
381
+ field,
382
+ serverOwner: deps.serverOwner,
383
+ storage: deps.storage,
384
+ gateway: deps.lineageGateway,
385
+ now: deps.now ?? (() => new Date()),
386
+ });
387
+ }
388
+ /**
389
+ * The lineage view a read auth result entitles the caller to. The auth port
390
+ * answers an owner read with no result or with the "owner" / "policy-bypass"
391
+ * sentinels (see api-auth): those get the full view. A builder read carries
392
+ * the grant the read policy resolved, and that grant names the view. A
393
+ * builder result with no resolved grant (or an "unknown" one) proves
394
+ * nothing and is refused: a missing grantId must never widen to the full
395
+ * view.
396
+ */
397
+ function resolveLineageGrantView(authResult) {
398
+ if (authResult === undefined ||
399
+ authResult.grantId === "owner" ||
400
+ authResult.grantId === "policy-bypass") {
401
+ return { grantId: undefined };
402
+ }
403
+ const grantId = authResult.grantId;
404
+ if (typeof grantId !== "string" || grantId === "" || grantId === "unknown") {
405
+ throw new GrantRequiredError({
406
+ reason: "a lineage read by a builder needs a resolved grant",
407
+ });
408
+ }
409
+ return { grantId };
410
+ }
286
411
  export async function handlePersonalServerDataRequest(request, deps, options = {}) {
287
412
  return withApiErrors(async () => {
288
413
  const url = new URL(request.url);
@@ -296,6 +421,7 @@ export async function handlePersonalServerDataRequest(request, deps, options = {
296
421
  scopePrefix: url.searchParams.get("scopePrefix") ?? undefined,
297
422
  limit: normalizeLimit(url.searchParams.get("limit"), 20),
298
423
  offset: normalizeLimit(url.searchParams.get("offset"), 0),
424
+ isVisible: discoveryVisibility(deps),
299
425
  });
300
426
  return jsonResponse(result.response);
301
427
  }
@@ -304,16 +430,93 @@ export async function handlePersonalServerDataRequest(request, deps, options = {
304
430
  if (request.method !== "GET")
305
431
  return methodNotAllowed();
306
432
  await deps.auth.authorizeBuilderList(request);
307
- const result = listDataVersionsContract({
433
+ const result = await listDataVersionsContract({
308
434
  storage: deps.storage,
309
435
  scopeParam: decodePathPart(parts[0]),
310
436
  limit: normalizeLimit(url.searchParams.get("limit"), 20),
311
437
  offset: normalizeLimit(url.searchParams.get("offset"), 0),
438
+ isVisible: discoveryVisibility(deps),
312
439
  });
313
440
  if (!result.ok)
314
441
  return contractErrorResponse(result);
442
+ if (result.response.total === 0) {
443
+ // Nothing visible: answer 410 rather than an empty list when that is
444
+ // because the scope is tombstoned, exactly as a read would.
445
+ await assertScopeNotDeleted(deps, result.scope, undefined);
446
+ }
315
447
  return jsonResponse(result.response);
316
448
  }
449
+ if ((parts.length === 2 || parts.length === 3) && parts[1] === "lineage") {
450
+ if (request.method !== "GET")
451
+ return methodNotAllowed();
452
+ const scopeResult = parseDataScopeContract(decodePathPart(parts[0]));
453
+ if (!scopeResult.ok)
454
+ return contractErrorResponse(scopeResult);
455
+ // The version is a PATH segment (/v1/data/:scope/lineage/:version), so
456
+ // it is inside the signed request uri: a builder's signature for one
457
+ // version cannot be replayed to read another. The grant view is the
458
+ // signed grantId claim the read auth already honours. Query
459
+ // parameters would be unsigned inputs to the view and are refused.
460
+ if (url.searchParams.has("version")) {
461
+ return errorResponse(400, "INVALID_VERSION", "version is a path segment (/v1/data/:scope/lineage/:version), not a query parameter");
462
+ }
463
+ if (url.search.length > 0) {
464
+ return errorResponse(400, "INVALID_QUERY", "lineage reads take no query parameters; the version is a path segment and the grant view is the signed grantId claim");
465
+ }
466
+ const version = parts.length === 3 ? decodePathPart(parts[2]) : undefined;
467
+ if (version !== undefined && !/^[1-9]\d*$/.test(version)) {
468
+ return errorResponse(400, "INVALID_VERSION", "version must be a positive decimal integer");
469
+ }
470
+ // Same gate as a read of the scope: the owner, or a builder whose grant
471
+ // covers it. The gateway then attests the view that grant sees, so a
472
+ // builder only learns the scopes of nodes its grant covers.
473
+ const authResult = await deps.auth.authorizeBuilderRead({
474
+ request,
475
+ scope: scopeResult.scope,
476
+ grantId: selectedGrantId(request, url),
477
+ });
478
+ if (!deps.serverOwner) {
479
+ throw new ServerNotConfiguredError({
480
+ reason: "serverOwner is required to resolve the data point id",
481
+ });
482
+ }
483
+ if (!deps.lineageGateway)
484
+ throw new LineageUnavailableError();
485
+ const { grantId } = resolveLineageGrantView(authResult);
486
+ let result;
487
+ try {
488
+ result = await deps.lineageGateway.getLineage({
489
+ dataPointId: computeDataPointId(deps.serverOwner, scopeResult.scope),
490
+ version,
491
+ grantId,
492
+ });
493
+ }
494
+ catch (err) {
495
+ // Transport failures (DNS, refused, timeout) are gateway errors to
496
+ // the caller, not internal ones: same 502 as a gateway error body.
497
+ if (err instanceof ProtocolError)
498
+ throw err;
499
+ throw new LineageGatewayError({
500
+ status: 0,
501
+ body: { error: err instanceof Error ? err.message : String(err) },
502
+ });
503
+ }
504
+ if (!result.ok) {
505
+ if (result.status === 404) {
506
+ return errorResponse(404, "NOT_FOUND", version
507
+ ? `Scope "${scopeResult.scope}" has no registered version ${version}`
508
+ : `Scope "${scopeResult.scope}" is not registered at the gateway`);
509
+ }
510
+ if (result.status === 403) {
511
+ throw new ProtocolError(403, "LINEAGE_FORBIDDEN", "The gateway refused the lineage view for this grant", { gateway: result.body });
512
+ }
513
+ throw new LineageGatewayError({
514
+ status: result.status,
515
+ body: result.body,
516
+ });
517
+ }
518
+ return jsonResponse({ data: result.data, proof: result.proof });
519
+ }
317
520
  if (parts.length !== 1)
318
521
  return notFound();
319
522
  const scopeParam = decodePathPart(parts[0]);
@@ -337,6 +540,10 @@ export async function handlePersonalServerDataRequest(request, deps, options = {
337
540
  fileId: url.searchParams.get("fileId") ?? selectedEntry?.fileId ?? undefined,
338
541
  at: url.searchParams.get("at") ?? undefined,
339
542
  });
543
+ // Deletion gate, after auth (so unauthenticated callers learn nothing)
544
+ // and before payment (so nobody is charged for deleted data). Covers
545
+ // both a stale local copy of a tombstoned scope and a local miss.
546
+ await assertScopeNotDeleted(deps, scopeResult.scope, selectedEntry);
340
547
  // X402 payment dance for builder reads. Owner-exempt reads (the
341
548
  // grantId sentinels "owner" / "policy-bypass") skip payment entirely
342
549
  // since there's no payable op to attach the payment to.
@@ -432,8 +639,15 @@ export async function handlePersonalServerDataRequest(request, deps, options = {
432
639
  fileId: url.searchParams.get("fileId") ?? undefined,
433
640
  at: url.searchParams.get("at") ?? undefined,
434
641
  });
435
- if (!result.ok)
642
+ if (!result.ok) {
643
+ if (result.status === 404) {
644
+ // The entry selected above can vanish before the read (a sync
645
+ // reconcile applying a tombstone concurrently); answer 410, not
646
+ // 404, when that is why.
647
+ await assertScopeNotDeleted(deps, scopeResult.scope, undefined);
648
+ }
436
649
  return contractErrorResponse(result);
650
+ }
437
651
  const logId = deps.createLogId?.() ?? crypto.randomUUID();
438
652
  const timestamp = (deps.now ?? (() => new Date()))().toISOString();
439
653
  const ipAddress = request.headers.get("x-forwarded-for") ??
@@ -531,6 +745,7 @@ export async function handlePersonalServerDataRequest(request, deps, options = {
531
745
  return failWrite(contractErrorResponse(scopeResult));
532
746
  const collectedAtValue = collectedAt(deps.now ?? (() => new Date()));
533
747
  const status = deps.syncManager ? "syncing" : "stored";
748
+ const afterTombstoneVersion = await ingestTombstoneMarker(deps, scopeResult.scope);
534
749
  // Builder writes land in the same access log as builder reads, so the
535
750
  // owner sees who wrote what under which grant.
536
751
  // Best-effort: it runs AFTER the record is committed, so a log failure
@@ -569,16 +784,22 @@ export async function handlePersonalServerDataRequest(request, deps, options = {
569
784
  // below still resolves a schema for validation/metadata.)
570
785
  if (!isJsonContentType(request)) {
571
786
  const bytes = new Uint8Array(await request.arrayBuffer());
787
+ const metadata = parseMetadataHeader(request.headers.get("x-vana-metadata"));
788
+ // A binary derivative names its sources inside the metadata
789
+ // object; the validated mirror lands at the top of the record.
790
+ const lineage = await prepareWriteLineage(deps, scopeResult.scope, extractLineageField(metadata));
572
791
  const result = await ingestBinaryDataContract({
573
792
  storage: deps.storage,
574
793
  scopeParam: scopeResult.scope,
575
794
  bytes,
576
795
  mimeType: binaryMimeType(request),
577
796
  filename: binaryFilename(request),
578
- metadata: parseMetadataHeader(request.headers.get("x-vana-metadata")),
797
+ metadata,
579
798
  collectedAt: collectedAtValue,
580
799
  status,
581
800
  attribution: writeAuth?.attribution,
801
+ lineage,
802
+ afterTombstoneVersion,
582
803
  });
583
804
  if (!result.ok)
584
805
  return failWrite(contractErrorResponse(result));
@@ -598,6 +819,9 @@ export async function handlePersonalServerDataRequest(request, deps, options = {
598
819
  const parsed = await parseJsonObjectBody(request, "Request body must be valid JSON");
599
820
  if (!parsed.ok)
600
821
  return failWrite(contractResponse(parsed.result));
822
+ // Derivative writes: validate the caller's `lineage` before anything
823
+ // is stored (a failure here throws, releasing the builder's proof).
824
+ const lineage = await prepareWriteLineage(deps, scopeResult.scope, extractLineageField(parsed.body));
601
825
  // DPv2 is scope-addressed and the gateway records no schemas, so JSON
602
826
  // ingest is schemaless too — no lookup, no schemaId/$schema stamped.
603
827
  const result = await ingestDataContract({
@@ -607,6 +831,8 @@ export async function handlePersonalServerDataRequest(request, deps, options = {
607
831
  collectedAt: collectedAtValue,
608
832
  status,
609
833
  attribution: writeAuth?.attribution,
834
+ lineage,
835
+ afterTombstoneVersion,
610
836
  });
611
837
  if (!result.ok)
612
838
  return failWrite(contractErrorResponse(result));
@@ -642,28 +868,77 @@ export async function handlePersonalServerDataRequest(request, deps, options = {
642
868
  }
643
869
  if (request.method === "DELETE") {
644
870
  await deps.auth.authorizeOwner(request);
645
- // Propagate the deletion to the authoritative stores (R2 blobs + gateway records) BEFORE the
646
- // local delete it reads the local index to find what to remove. Best-effort: a remote
647
- // failure must not block the owner's local delete (and sync would otherwise resurrect it; the
648
- // download-worker reconciliation is the backstop). Parse the scope first so an invalid scope
649
- // still 400s without a wasted remote call.
871
+ const cascade = url.searchParams.get("cascade");
872
+ if (cascade !== null && cascade !== "lineage") {
873
+ throw new InvalidCascadeError({ cascade });
874
+ }
875
+ // Parse the scope first so an invalid scope still 400s without a
876
+ // wasted remote call.
650
877
  const parsed = parseDataScopeContract(scopeParam);
651
- if (parsed.ok && deps.syncManager?.deleteScopeRemote) {
652
- try {
653
- await deps.syncManager.deleteScopeRemote(parsed.scope);
654
- }
655
- catch (err) {
656
- deps.logger?.info?.({ scope: scopeParam, error: err.message }, "Remote scope deletion failed; proceeding with local delete");
657
- }
878
+ if (!parsed.ok)
879
+ return contractErrorResponse(parsed);
880
+ if (cascade === "lineage") {
881
+ // Specified (docs/derivative-data-api.md, "Delete") but not
882
+ // implemented here. The durable per-scope delete below is what a
883
+ // cascade node needs; the missing part is the lineage walk itself
884
+ // (owner view of the gateway graph, deepest derivatives first,
885
+ // truncation and cross-owner handling, all-or-nothing before the
886
+ // first tombstone). Until that lands the only honest answer is 501.
887
+ throw new LineageCascadeUnavailableError({ scope: parsed.scope });
658
888
  }
659
- const result = await deleteDataScopeContract({
660
- storage: deps.storage,
661
- scopeParam,
662
- });
663
- if (!result.ok)
664
- return contractErrorResponse(result);
665
- deps.logger?.info?.({ scope: scopeParam, deletedCount: result.deletedCount }, "Scope deleted");
666
- return new Response(null, { status: 204 });
889
+ // Durable order: gateway tombstone -> storage blobs -> local copy. The
890
+ // sync manager owns the remote ports; without one (sync disabled or
891
+ // no owner key material) only the local copy goes and the result says
892
+ // so (`durable: false`) rather than pretending.
893
+ const result = deps.syncManager?.deleteScope
894
+ ? await deps.syncManager.deleteScope(parsed.scope)
895
+ : await deleteScopeLocally({
896
+ storage: deps.storage,
897
+ serverOwner: deps.serverOwner,
898
+ deleteData: null,
899
+ logger: apiLoggerAsLogger(deps.logger),
900
+ }, parsed.scope);
901
+ if (result.steps.gateway.status === "failed") {
902
+ // Nothing was deleted: the local copy is kept on purpose so sync
903
+ // cannot resurrect a half-deleted scope. Surface it as an upstream
904
+ // failure with the per-step result attached.
905
+ throw new DeleteTombstoneFailedError({
906
+ scope: parsed.scope,
907
+ result,
908
+ });
909
+ }
910
+ // Audit entry, best-effort: the deletion above is irreversible and
911
+ // already done, so a failed log write must not turn it into an error
912
+ // response that invites a misleading retry. The failure is logged.
913
+ try {
914
+ await deps.accessLogWriter.write({
915
+ logId: deps.createLogId?.() ?? crypto.randomUUID(),
916
+ grantId: "owner",
917
+ builder: deps.serverOwner ?? "owner",
918
+ action: "delete",
919
+ scope: parsed.scope,
920
+ timestamp: (deps.now ?? (() => new Date()))().toISOString(),
921
+ ipAddress: request.headers.get("x-forwarded-for") ??
922
+ request.headers.get("x-real-ip") ??
923
+ "unknown",
924
+ userAgent: request.headers.get("user-agent") ?? "unknown",
925
+ });
926
+ }
927
+ catch (err) {
928
+ deps.logger?.warn?.({
929
+ scope: parsed.scope,
930
+ error: err instanceof Error ? err.message : String(err),
931
+ }, "Delete access-log entry failed; scope already deleted");
932
+ }
933
+ deps.logger?.info?.({
934
+ scope: parsed.scope,
935
+ durable: result.durable,
936
+ gateway: result.steps.gateway.status,
937
+ storage: result.steps.storage.status,
938
+ local: result.steps.local.status,
939
+ deletedCount: result.steps.local.deletedCount,
940
+ }, "Scope deleted");
941
+ return jsonResponse(result, { status: 200 });
667
942
  }
668
943
  return methodNotAllowed();
669
944
  });