@nacre.work/api 0.2.0 → 0.3.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.
@@ -1,7 +1,7 @@
1
- import { aclTags, buildFilter, VectorStore, type CacheStore, type Metadata } from '@nacre.work/core';
1
+ import { buildFilter, VectorStore, type CacheStore, type Metadata } from '@nacre.work/core';
2
2
  import type { Pool } from 'pg';
3
3
  import { type Reranker } from './rerank.js';
4
- import type { Reindex, ReindexOutcome, ReindexStatus, AuditEvent, AuditQuery, AuditReader, AuditRecord, AuditSink, Documents, GrantInput, GrantRecord, Grants, Ingest, IngestOutcome, IngestRequest, Job, Jobs, Layer, LayerOutcome, DocumentView, Layers, Page, PageResult, Workspace, WorkspaceOutcome, Workspaces, SearchHit, SearchOptions, SearchService } from './server.js';
4
+ import type { Reindex, ReindexOutcome, ReindexStatus, ReferenceQueries, ReferenceQuery, AuditEvent, AuditQuery, AuditReader, AuditRecord, AuditWriter, Documents, GrantInput, GrantRecord, Grants, Ingest, IngestOutcome, IngestRefused, IngestRequest, Job, Jobs, Layer, LayerOutcome, DocumentView, Layers, Page, PageResult, Workspace, WorkspaceOutcome, Workspaces, SearchHit, SearchOptions, SearchService } from './server.js';
5
5
  import type { AuthContext } from './auth.js';
6
6
  /**
7
7
  * The adapters that put the permission model on the request path.
@@ -212,7 +212,7 @@ export declare class NacreSearchService implements SearchService {
212
212
  * by migration 0002, so a bug here cannot rewrite history and neither can
213
213
  * anyone holding these credentials.
214
214
  */
215
- export declare class PostgresAudit implements AuditSink {
215
+ export declare class PostgresAudit implements AuditWriter {
216
216
  private readonly pool;
217
217
  private readonly role?;
218
218
  constructor(pool: Pool, role?: string | undefined);
@@ -245,7 +245,6 @@ export declare class HttpEmbedder implements Embedder {
245
245
  static pool(timeoutMs?: number): (provider: EmbeddingProvider) => Embedder;
246
246
  embed(texts: readonly string[]): Promise<readonly (readonly number[])[]>;
247
247
  }
248
- export { aclTags };
249
248
  /**
250
249
  * Queueing a document, with the write permission checked first.
251
250
  *
@@ -289,7 +288,7 @@ export declare class NacreIngest implements Ingest {
289
288
  private get scope();
290
289
  /** The layers this caller may write to, by slug. */
291
290
  private writableLayer;
292
- queue(auth: AuthContext, request: IngestRequest): Promise<IngestOutcome | undefined>;
291
+ queue(auth: AuthContext, request: IngestRequest): Promise<IngestOutcome | IngestRefused | undefined>;
293
292
  remove(auth: AuthContext, documentId: string): Promise<boolean>;
294
293
  }
295
294
  /**
@@ -598,4 +597,33 @@ export declare class PostgresReindex implements Reindex {
598
597
  private remaining;
599
598
  private statusOf;
600
599
  }
600
+ /**
601
+ * The reference query set behind the reindex recall gate.
602
+ *
603
+ * `admin` on the layer for both operations, and that is a decision rather than
604
+ * a copy of the reindex adapter above. Reading the set reveals which documents
605
+ * an operator considers the canonical answers to a query, which is a statement
606
+ * about the layer's contents; rule 7 makes `admin` the permission that implies
607
+ * being allowed to see them, and rule 6 means `write` does not.
608
+ */
609
+ export declare class PostgresReferenceQueries implements ReferenceQueries {
610
+ private readonly pool;
611
+ private readonly role?;
612
+ private readonly principalsCache?;
613
+ constructor(pool: Pool, role?: string | undefined, principalsCache?: PrincipalsCache | undefined);
614
+ private get scope();
615
+ /**
616
+ * The layer, if this caller may administer it.
617
+ *
618
+ * `undefined` for a layer that is not there and for one they may not touch —
619
+ * one answer, which the handler turns into `404`. Deleted layers are not
620
+ * there: a reference set on a deleted layer describes nothing.
621
+ */
622
+ private administrable;
623
+ list(auth: AuthContext, layerId: string): Promise<readonly ReferenceQuery[] | undefined>;
624
+ replace(auth: AuthContext, layerId: string, queries: readonly {
625
+ query: string;
626
+ expected: readonly string[];
627
+ }[]): Promise<readonly ReferenceQuery[] | undefined>;
628
+ }
601
629
  //# sourceMappingURL=adapters.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"adapters.d.ts","sourceRoot":"","sources":["../src/adapters.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"adapters.d.ts","sourceRoot":"","sources":["../src/adapters.ts"],"names":[],"mappings":"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"}
package/dist/adapters.js CHANGED
@@ -1,4 +1,4 @@
1
- import { aclTags, buildFilter, cachedEffectivePrincipals, documentKey, effectivePrincipals, fromStateJson, loadGrants, loadGroupsVersion, loadScopeTree, PostgresGroupGraph, referenceAllows, reindexProgress, resolve, toStateJson, VectorStore, vectorName, withOrg, } from '@nacre.work/core';
1
+ import { buildFilter, cachedEffectivePrincipals, documentKey, effectivePrincipals, fromStateJson, loadGrants, loadGroupsVersion, loadScopeTree, PostgresGroupGraph, referenceAllows, reindexProgress, activeResolver, admitIngest, toStateJson, VectorStore, vectorName, withOrg, } from '@nacre.work/core';
2
2
  import { createHash } from 'node:crypto';
3
3
  import { encodeCursor, pageOf } from './pagination.js';
4
4
  import { applyRanking } from './rerank.js';
@@ -53,7 +53,7 @@ export class PostgresDocuments {
53
53
  // The plan first. Reading the row and then checking is the same
54
54
  // information leak with extra steps if the check is ever forgotten,
55
55
  // and the layer a document sits in is what the grant is on.
56
- const plan = resolve(await contextFor(client, auth, this.principalsCache), 'read');
56
+ const plan = activeResolver().resolve(await contextFor(client, auth, this.principalsCache), 'read');
57
57
  if (plan.kind === 'none')
58
58
  return undefined;
59
59
  const { rows } = await client.query(`SELECT d.id, d.title, d.layer_id, l.slug AS layer, d.status, d.chunk_count,
@@ -127,7 +127,7 @@ export class PostgresDocuments {
127
127
  const collection = orgs[0]?.vector_collection;
128
128
  if (collection === undefined)
129
129
  return false;
130
- const plan = resolve(await contextFor(client, auth, this.principalsCache), 'write');
130
+ const plan = activeResolver().resolve(await contextFor(client, auth, this.principalsCache), 'write');
131
131
  if (plan.kind === 'none')
132
132
  return false;
133
133
  const { rows } = await client.query(`SELECT layer_id FROM documents
@@ -174,7 +174,7 @@ export class NacreSearchService {
174
174
  const principals = await principalsFor(client, auth, this.deps.principalsCache);
175
175
  const grants = await loadGrants(client, auth.orgId, principals);
176
176
  const tree = await loadScopeTree(client, auth.orgId, grants.filter((g) => g.scope.type === 'document').map((g) => g.scope.id));
177
- const plan = resolve({ orgId: auth.orgId, role: auth.role, principals, grants, tree }, 'read');
177
+ const plan = activeResolver().resolve({ orgId: auth.orgId, role: auth.role, principals, grants, tree }, 'read');
178
178
  // Every live layer with the model it is on. Small — an installation
179
179
  // runs tens of layers per organization, not thousands — and it is what
180
180
  // decides how many branches the query has.
@@ -519,6 +519,10 @@ export class PostgresAudit {
519
519
  event.requestId,
520
520
  ]);
521
521
  }, this.role === undefined ? {} : { role: this.role });
522
+ // Forwarding to a module's sinks is deliberately *not* here. It is
523
+ // `withAuditSinks`, applied to whatever port a surface was handed, because
524
+ // a sink wants every recorded event and not every event this adapter
525
+ // happened to record.
522
526
  }
523
527
  }
524
528
  /**
@@ -593,7 +597,6 @@ export class HttpEmbedder {
593
597
  });
594
598
  }
595
599
  }
596
- export { aclTags };
597
600
  export class NacreIngest {
598
601
  deps;
599
602
  constructor(deps) {
@@ -610,7 +613,7 @@ export class NacreIngest {
610
613
  const principals = await principalsFor(client, auth, this.deps.principalsCache);
611
614
  const grants = await loadGrants(client, auth.orgId, principals);
612
615
  const tree = await loadScopeTree(client, auth.orgId, grants.filter((g) => g.scope.type === 'document').map((g) => g.scope.id));
613
- const plan = resolve({ orgId: auth.orgId, role: auth.role, principals, grants, tree }, 'write');
616
+ const plan = activeResolver().resolve({ orgId: auth.orgId, role: auth.role, principals, grants, tree }, 'write');
614
617
  if (plan.kind === 'none')
615
618
  return undefined;
616
619
  const { rows } = await client.query(`SELECT id FROM layers WHERE org_id = $1 AND slug = $2 AND deleted_at IS NULL`, [auth.orgId, layerSlug]);
@@ -631,6 +634,23 @@ export class NacreIngest {
631
634
  const inline = request.content !== undefined;
632
635
  const source = inline ? request.content : request.url;
633
636
  const hash = createHash('sha256').update(source, 'utf8').digest('hex');
637
+ // A module's ingest gate, after write is established and before anything
638
+ // is stored, so a refusal leaves neither a row nor an object behind. No
639
+ // gate registered is the open core admitting every document a caller may
640
+ // write, which is what it did before this point existed. A refusal
641
+ // becomes the handler's 4xx; it is not `undefined`, because that would
642
+ // read as "unwritable" and hide the layer the caller just wrote against.
643
+ const refusal = await admitIngest({
644
+ orgId: auth.orgId,
645
+ layerId,
646
+ principal: auth.principal,
647
+ role: auth.role,
648
+ externalId: request.externalId,
649
+ bytes: new TextEncoder().encode(source).byteLength,
650
+ });
651
+ if (refusal !== undefined) {
652
+ return { refused: true, status: refusal.status, reason: refusal.reason };
653
+ }
634
654
  // Bytes to object storage before the row, never after.
635
655
  //
636
656
  // The reverse fails unrecoverably, which is the same argument the
@@ -713,7 +733,7 @@ export class NacreIngest {
713
733
  const principals = await principalsFor(client, auth, this.deps.principalsCache);
714
734
  const grants = await loadGrants(client, auth.orgId, principals);
715
735
  const tree = await loadScopeTree(client, auth.orgId, [documentId]);
716
- const plan = resolve({ orgId: auth.orgId, role: auth.role, principals, grants, tree }, 'write');
736
+ const plan = activeResolver().resolve({ orgId: auth.orgId, role: auth.role, principals, grants, tree }, 'write');
717
737
  if (plan.kind === 'none')
718
738
  return false;
719
739
  const { rows } = await client.query(`SELECT layer_id FROM documents
@@ -786,7 +806,7 @@ export class PostgresJobs {
786
806
  // different projection — and needs the same check. The status and the
787
807
  // error string are both facts about a document the caller may not be
788
808
  // allowed to know exists.
789
- const plan = resolve(await contextFor(client, auth, this.principalsCache), 'read');
809
+ const plan = activeResolver().resolve(await contextFor(client, auth, this.principalsCache), 'read');
790
810
  if (plan.kind === 'none')
791
811
  return undefined;
792
812
  const { rows } = await client.query(`SELECT id, status, error, chunk_count, layer_id FROM documents
@@ -879,7 +899,7 @@ export class PostgresLayers {
879
899
  */
880
900
  async list(auth, page) {
881
901
  return withOrg(this.pool, auth.orgId, async (client) => {
882
- const plan = resolve(await contextFor(client, auth, this.principalsCache), 'read');
902
+ const plan = activeResolver().resolve(await contextFor(client, auth, this.principalsCache), 'read');
883
903
  if (plan.kind === 'none')
884
904
  return { items: [], nextCursor: null };
885
905
  // The count comes from the same statement. It is what a catalog is
@@ -893,7 +913,12 @@ export class PostgresLayers {
893
913
  // grants exist — a commercial module — this becomes a number that
894
914
  // discloses more than the caller can reach, and it has to be recomputed
895
915
  // per caller or dropped.
916
+ // `created_at::text` beside `created_at`, and the cursor is built from
917
+ // it. See `Position.createdAt` — a timestamp that has been through a
918
+ // JavaScript `Date` is truncated to milliseconds, and a truncated bound
919
+ // matches the row it came from all over again.
896
920
  const projection = `l.id, l.slug, l.name, l.workspace_id, l.description, l.created_at,
921
+ l.created_at::text AS created_at_text,
897
922
  (SELECT count(*) FROM documents d
898
923
  WHERE d.layer_id = l.id AND d.deleted_at IS NULL) AS document_count`;
899
924
  // Ordered by (created_at, id) rather than by slug, because that is the
@@ -921,7 +946,10 @@ export class PostgresLayers {
921
946
  documentCount: Number(r.document_count),
922
947
  createdAt: r.created_at.toISOString(),
923
948
  }));
924
- return pageOf(layers, page, (l) => ({ createdAt: l.createdAt, id: l.id }));
949
+ return pageOf(layers, page, (l, i) => ({
950
+ createdAt: rows[i].created_at_text,
951
+ id: l.id,
952
+ }));
925
953
  }, this.scope);
926
954
  }
927
955
  async update(auth, layerId, input) {
@@ -1101,13 +1129,28 @@ export class PostgresWorkspaces {
1101
1129
  // administer, and therefore unable to create the first layer in it,
1102
1130
  // which is the deadlock this endpoint was added to break. It was
1103
1131
  // written that way first and caught by running it.
1104
- const plan = resolve(context, 'read');
1105
- const { rows } = await client.query(`SELECT w.id, w.slug, w.name, w.created_at,
1132
+ const plan = activeResolver().resolve(context, 'read');
1133
+ // Seek and limit in SQL.
1134
+ //
1135
+ // Neither was here: the statement fetched every workspace in the
1136
+ // organization, ignored `page.after` entirely, and handed the whole
1137
+ // list to `pageOf` — which then answered "there is another page"
1138
+ // because the list was at least as long as the limit. So
1139
+ // `GET /v1/workspaces` returned the same complete list on every page
1140
+ // and never terminated, and a client following `next_cursor` looped
1141
+ // forever. Found by a test that walked the listing one item at a time.
1142
+ const after = page?.after;
1143
+ const seek = after === undefined ? '' : ' AND (w.created_at, w.id) > ($2::timestamptz, $3::uuid)';
1144
+ // One extra, because the filter below removes rows the caller may not
1145
+ // reach: without it a page whose last row is filtered out would report
1146
+ // itself as the end of the collection.
1147
+ const cap = page === undefined ? '' : ` LIMIT ${page.limit + 1}`;
1148
+ const { rows } = await client.query(`SELECT w.id, w.slug, w.name, w.created_at, w.created_at::text AS created_at_text,
1106
1149
  (SELECT array_agg(l.id) FROM layers l
1107
1150
  WHERE l.workspace_id = w.id AND l.deleted_at IS NULL) AS layer_ids
1108
1151
  FROM workspaces w
1109
- WHERE w.org_id = $1 AND w.deleted_at IS NULL
1110
- ORDER BY w.created_at, w.id`, [auth.orgId]);
1152
+ WHERE w.org_id = $1 AND w.deleted_at IS NULL${seek}
1153
+ ORDER BY w.created_at, w.id${cap}`, after === undefined ? [auth.orgId] : [auth.orgId, after.createdAt, after.id]);
1111
1154
  const reachable = rows.filter((w) => {
1112
1155
  if (plan.kind === 'all')
1113
1156
  return true;
@@ -1120,14 +1163,28 @@ export class PostgresWorkspaces {
1120
1163
  // for a workspace with nothing in it yet.
1121
1164
  return referenceAllows(context, { type: 'workspace', id: w.id }, 'read');
1122
1165
  });
1123
- const items = reachable.map((w) => ({
1166
+ const fetched = page === undefined ? rows : rows.slice(0, page.limit);
1167
+ const items = reachable
1168
+ .filter((w) => fetched.some((f) => f.id === w.id))
1169
+ .map((w) => ({
1124
1170
  id: w.id,
1125
1171
  slug: w.slug,
1126
1172
  name: w.name,
1127
1173
  layerCount: (w.layer_ids ?? []).length,
1128
1174
  createdAt: w.created_at.toISOString(),
1129
1175
  }));
1130
- return pageOf(items, page, (w) => ({ createdAt: w.createdAt, id: w.id }));
1176
+ // From the last row **fetched**, not the last returned — the same rule
1177
+ // the grant listing follows and for the same reason: the filter above
1178
+ // removes rows the caller may not reach, and a cursor taken from a
1179
+ // survivor would skip everything the filter dropped after it.
1180
+ //
1181
+ // Which makes a short page normal here rather than a signal, and
1182
+ // `next_cursor` the only thing that says whether more exist.
1183
+ const lastFetched = fetched[fetched.length - 1];
1184
+ const nextCursor = page !== undefined && rows.length > page.limit && lastFetched !== undefined
1185
+ ? encodeCursor({ createdAt: lastFetched.created_at_text, id: lastFetched.id })
1186
+ : null;
1187
+ return { items, nextCursor };
1131
1188
  }, this.scope);
1132
1189
  }
1133
1190
  /**
@@ -1250,14 +1307,14 @@ export class PostgresGrants {
1250
1307
  async list(auth, page) {
1251
1308
  return withOrg(this.pool, auth.orgId, async (client) => {
1252
1309
  const context = await contextFor(client, auth, this.principalsCache);
1253
- const plan = resolve(context, 'admin');
1310
+ const plan = activeResolver().resolve(context, 'admin');
1254
1311
  if (plan.kind === 'none')
1255
1312
  return { items: [], nextCursor: null };
1256
1313
  const after = page?.after;
1257
1314
  const seek = after === undefined ? '' : ' AND (created_at, id) > ($2::timestamptz, $3::uuid)';
1258
1315
  const cap = page === undefined ? '' : ` LIMIT ${page.limit}`;
1259
1316
  const { rows } = await client.query(`SELECT id, principal_type, principal_id, scope_type, scope_id, permission, effect, source,
1260
- created_at
1317
+ created_at, created_at::text AS created_at_text
1261
1318
  FROM grants WHERE org_id = $1${seek} ORDER BY created_at, id${cap}`, after === undefined ? [auth.orgId] : [auth.orgId, after.createdAt, after.id]);
1262
1319
  const visible = rows
1263
1320
  .filter((r) => referenceAllows(context, { type: r.scope_type, id: r.scope_id }, 'admin'))
@@ -1277,7 +1334,7 @@ export class PostgresGrants {
1277
1334
  // it — a page of grants silently missing from an administrator's view.
1278
1335
  const lastFetched = rows[rows.length - 1];
1279
1336
  const nextCursor = page !== undefined && rows.length >= page.limit && lastFetched !== undefined
1280
- ? encodeCursor({ createdAt: lastFetched.created_at.toISOString(), id: lastFetched.id })
1337
+ ? encodeCursor({ createdAt: lastFetched.created_at_text, id: lastFetched.id })
1281
1338
  : null;
1282
1339
  return { items: visible, nextCursor };
1283
1340
  }, this.scope);
@@ -1293,6 +1350,33 @@ export class PostgresGrants {
1293
1350
  if (!referenceAllows(context, { type: input.scopeType, id: input.scopeId }, 'admin')) {
1294
1351
  return undefined;
1295
1352
  }
1353
+ // And the scope has to exist in this organization, which the check
1354
+ // above does not establish. `referenceAllows` returns `true` for
1355
+ // `org_admin` on its third line — rule 3, admin on every scope
1356
+ // implicitly — and it returns *before* the scope is placed, so an
1357
+ // administrator naming any uuid at all passed.
1358
+ //
1359
+ // Not a leak, and the reason is worth stating rather than assumed: a
1360
+ // grant on a scope that is not here becomes `should: layer_id = X`
1361
+ // beside an unconditional `must: org_id = <this tenant>`, and points
1362
+ // carrying layer X belong to another tenant and live in another
1363
+ // collection. The second line of defence holds. What got written was a
1364
+ // row that permits nothing and points at nothing.
1365
+ //
1366
+ // It still has to be refused. An administrator reading `/v1/grants`
1367
+ // sees a grant they cannot explain on an id they cannot look up, and
1368
+ // `404` stops meaning what invariant 4 says it means — "no permission"
1369
+ // and "no such object" are one answer, which requires that naming a
1370
+ // nonexistent object *reaches* that answer rather than succeeding.
1371
+ //
1372
+ // Written out per table rather than assembled, so the table name can
1373
+ // never come from a caller-supplied string. `scopeType` is a checked
1374
+ // union already; this makes it structural.
1375
+ const exists = await client.query(input.scopeType === 'workspace'
1376
+ ? `SELECT 1 FROM workspaces WHERE org_id = $1 AND id = $2 AND deleted_at IS NULL`
1377
+ : `SELECT 1 FROM layers WHERE org_id = $1 AND id = $2 AND deleted_at IS NULL`, [auth.orgId, input.scopeId]);
1378
+ if (exists.rowCount !== 1)
1379
+ return undefined;
1296
1380
  const { rows } = await client.query(`INSERT INTO grants
1297
1381
  (org_id, principal_type, principal_id, scope_type, scope_id, permission, effect, source)
1298
1382
  VALUES ($1,$2,$3,$4,$5,$6,'allow','api')
@@ -1446,7 +1530,8 @@ export class PostgresAuditReader {
1446
1530
  // without a second query and without a count — a count over a
1447
1531
  // retention window is the expensive thing this endpoint must not do on
1448
1532
  // every request.
1449
- const { rows } = await client.query(`SELECT id::text, occurred_at, actor_type, actor_id, actor_label, action,
1533
+ const { rows } = await client.query(`SELECT id::text, occurred_at, occurred_at::text AS occurred_at_text,
1534
+ actor_type, actor_id, actor_label, action,
1450
1535
  surface, client, target, result, detail, request_id
1451
1536
  FROM audit_events
1452
1537
  WHERE ${where.join(' AND ')}
@@ -1466,9 +1551,19 @@ export class PostgresAuditReader {
1466
1551
  detail: r.detail ?? {},
1467
1552
  requestId: r.request_id,
1468
1553
  }));
1554
+ // From the row rather than from the mapped record, because the record
1555
+ // carries an ISO string and this cursor needs full precision.
1556
+ //
1557
+ // This one **skips** rather than repeats, and that is the same bug seen
1558
+ // from the other side: the ordering is descending, so a bound truncated
1559
+ // downwards excludes the row it came from *and* everything between the
1560
+ // truncated value and the real one. On a listing that would lose a page
1561
+ // of layers; on the access log it silently loses events, in the one
1562
+ // place the product promises a precise answer.
1563
+ const lastRow = rows[Math.min(page.limit, rows.length) - 1];
1469
1564
  const last = items[items.length - 1];
1470
- const nextCursor = rows.length > page.limit && last !== undefined
1471
- ? encodeCursor({ createdAt: last.occurredAt, id: last.id })
1565
+ const nextCursor = rows.length > page.limit && last !== undefined && lastRow !== undefined
1566
+ ? encodeCursor({ createdAt: lastRow.occurred_at_text, id: last.id })
1472
1567
  : null;
1473
1568
  return { items, nextCursor };
1474
1569
  }, this.scopeFor());
@@ -1520,7 +1615,7 @@ export class PostgresReindex {
1520
1615
  // vector in the layer and changes which model answers every future
1521
1616
  // query — `read` is nowhere near enough, and `write` is about putting
1522
1617
  // documents in rather than about the layer itself.
1523
- const plan = resolve(await contextFor(client, auth, this.principalsCache), 'admin');
1618
+ const plan = activeResolver().resolve(await contextFor(client, auth, this.principalsCache), 'admin');
1524
1619
  if (plan.kind === 'none')
1525
1620
  return undefined;
1526
1621
  // Locked for the whole decision. Two starts arriving together would
@@ -1635,7 +1730,7 @@ export class PostgresReindex {
1635
1730
  // being migrated is not an administrative fact — it explains why a
1636
1731
  // result set moved — and hiding it from someone who can already read
1637
1732
  // the layer buys nothing.
1638
- const plan = resolve(await contextFor(client, auth, this.principalsCache), 'read');
1733
+ const plan = activeResolver().resolve(await contextFor(client, auth, this.principalsCache), 'read');
1639
1734
  if (plan.kind === 'none')
1640
1735
  return undefined;
1641
1736
  const { rows } = await client.query(`SELECT id, vector_name, reindex_state FROM layers
@@ -1686,7 +1781,87 @@ export class PostgresReindex {
1686
1781
  failed: state.failed,
1687
1782
  progress: reindexProgress(live),
1688
1783
  error: state.error ?? null,
1784
+ check: state.check ?? null,
1689
1785
  };
1690
1786
  }
1691
1787
  }
1788
+ /**
1789
+ * The reference query set behind the reindex recall gate.
1790
+ *
1791
+ * `admin` on the layer for both operations, and that is a decision rather than
1792
+ * a copy of the reindex adapter above. Reading the set reveals which documents
1793
+ * an operator considers the canonical answers to a query, which is a statement
1794
+ * about the layer's contents; rule 7 makes `admin` the permission that implies
1795
+ * being allowed to see them, and rule 6 means `write` does not.
1796
+ */
1797
+ export class PostgresReferenceQueries {
1798
+ pool;
1799
+ role;
1800
+ principalsCache;
1801
+ constructor(pool, role, principalsCache) {
1802
+ this.pool = pool;
1803
+ this.role = role;
1804
+ this.principalsCache = principalsCache;
1805
+ }
1806
+ get scope() {
1807
+ return this.role === undefined ? {} : { role: this.role };
1808
+ }
1809
+ /**
1810
+ * The layer, if this caller may administer it.
1811
+ *
1812
+ * `undefined` for a layer that is not there and for one they may not touch —
1813
+ * one answer, which the handler turns into `404`. Deleted layers are not
1814
+ * there: a reference set on a deleted layer describes nothing.
1815
+ */
1816
+ async administrable(client, auth, layerId) {
1817
+ const plan = activeResolver().resolve(await contextFor(client, auth, this.principalsCache), 'admin');
1818
+ if (plan.kind === 'none')
1819
+ return false;
1820
+ if (plan.kind === 'scoped' && !plan.layers.includes(layerId))
1821
+ return false;
1822
+ const { rows } = await client.query('SELECT id FROM layers WHERE org_id = $1 AND id = $2 AND deleted_at IS NULL', [auth.orgId, layerId]);
1823
+ return rows.length > 0;
1824
+ }
1825
+ async list(auth, layerId) {
1826
+ return withOrg(this.pool, auth.orgId, async (client) => {
1827
+ if (!(await this.administrable(client, auth, layerId)))
1828
+ return undefined;
1829
+ const { rows } = await client.query(`SELECT id, query, expected FROM reference_queries
1830
+ WHERE org_id = $1 AND layer_id = $2 ORDER BY ordinal`, [auth.orgId, layerId]);
1831
+ return rows.map((r) => ({ id: r.id, query: r.query, expected: r.expected }));
1832
+ }, this.scope);
1833
+ }
1834
+ async replace(auth, layerId, queries) {
1835
+ return withOrg(this.pool, auth.orgId, async (client) => {
1836
+ if (!(await this.administrable(client, auth, layerId)))
1837
+ return undefined;
1838
+ // Delete then insert, in the transaction `withOrg` already opened. A
1839
+ // set is one statement about the layer, so there is no moment at which
1840
+ // half of the old one and half of the new one are both in the table —
1841
+ // which matters because the worker reads this to decide whether a layer
1842
+ // has a gate at all.
1843
+ await client.query('DELETE FROM reference_queries WHERE org_id = $1 AND layer_id = $2', [
1844
+ auth.orgId,
1845
+ layerId,
1846
+ ]);
1847
+ const inserted = [];
1848
+ for (const [ordinal, q] of queries.entries()) {
1849
+ const { rows } = await client.query(`INSERT INTO reference_queries (org_id, layer_id, query, expected, ordinal)
1850
+ VALUES ($1, $2, $3, $4, $5) RETURNING id`, [auth.orgId, layerId, q.query, [...q.expected], ordinal]);
1851
+ inserted.push({
1852
+ id: rows[0]?.id,
1853
+ query: q.query,
1854
+ expected: [...q.expected],
1855
+ });
1856
+ }
1857
+ // The external ids are deliberately **not** validated against
1858
+ // `documents` here. A reference set is often written before the
1859
+ // documents it names are ingested, and refusing it then would make the
1860
+ // gate impossible to set up on a new layer. The check resolves them
1861
+ // when it runs, and an entry that resolves to nothing fails the reindex
1862
+ // by name — which is the moment it actually matters.
1863
+ return inserted;
1864
+ }, this.scope);
1865
+ }
1866
+ }
1692
1867
  //# sourceMappingURL=adapters.js.map