@nacre.work/api 0.2.0 → 0.4.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]);
@@ -629,8 +632,42 @@ export class NacreIngest {
629
632
  if (layerId === undefined)
630
633
  return undefined;
631
634
  const inline = request.content !== undefined;
632
- const source = inline ? request.content : request.url;
633
- const hash = createHash('sha256').update(source, 'utf8').digest('hex');
635
+ const binary = request.bytes !== undefined;
636
+ if (binary && this.deps.objects === undefined) {
637
+ // The handler refuses this at the edge, naming NACRE_S3_*; reaching
638
+ // here means a second surface started passing bytes without the
639
+ // check. Refusing keeps the invariant local: the bytes' only home is
640
+ // the bucket, and there is no bucket.
641
+ throw new Error('binary ingest requires object storage, and this deployment has none');
642
+ }
643
+ const source = binary ? undefined : inline ? request.content : request.url;
644
+ // For a binary source the hash is over the uploaded bytes — the worker
645
+ // computes the same, so the upsert's idempotency semantics hold across
646
+ // both halves: re-sending the same file is a no-op, a changed file
647
+ // re-indexes. For text it stays over the text, which is the same
648
+ // statement.
649
+ const hash = binary
650
+ ? createHash('sha256').update(request.bytes).digest('hex')
651
+ : createHash('sha256').update(source, 'utf8').digest('hex');
652
+ // A module's ingest gate, after write is established and before anything
653
+ // is stored, so a refusal leaves neither a row nor an object behind. No
654
+ // gate registered is the open core admitting every document a caller may
655
+ // write, which is what it did before this point existed. A refusal
656
+ // becomes the handler's 4xx; it is not `undefined`, because that would
657
+ // read as "unwritable" and hide the layer the caller just wrote against.
658
+ const refusal = await admitIngest({
659
+ orgId: auth.orgId,
660
+ layerId,
661
+ principal: auth.principal,
662
+ role: auth.role,
663
+ externalId: request.externalId,
664
+ bytes: binary
665
+ ? request.bytes.byteLength
666
+ : new TextEncoder().encode(source).byteLength,
667
+ });
668
+ if (refusal !== undefined) {
669
+ return { refused: true, status: refusal.status, reason: refusal.reason };
670
+ }
634
671
  // Bytes to object storage before the row, never after.
635
672
  //
636
673
  // The reverse fails unrecoverably, which is the same argument the
@@ -643,17 +680,25 @@ export class NacreIngest {
643
680
  // copying somebody else's URL into our bucket at ingest time would be
644
681
  // a fetch on the request path — which is exactly what the worker does
645
682
  // later, with a timeout and a sandbox.
646
- let sourceType = inline ? 'inline' : 'url';
683
+ let sourceType = binary ? 's3' : inline ? 'inline' : 'url';
647
684
  let sourceRef = source;
648
- if (inline && this.deps.objects !== undefined) {
685
+ if (binary) {
686
+ // A binary source has exactly one home. The bytes go up with their
687
+ // real Content-Type, so a presigned GET later answers with the type
688
+ // the caller sent rather than a lie a browser would act on.
689
+ const key = documentKey(auth.orgId, layerId, request.externalId);
690
+ await this.deps.objects.put(key, request.bytes, request.contentType);
691
+ sourceRef = key;
692
+ }
693
+ else if (inline && this.deps.objects !== undefined) {
649
694
  const key = documentKey(auth.orgId, layerId, request.externalId);
650
695
  await this.deps.objects.put(key, new TextEncoder().encode(source), 'text/plain');
651
696
  sourceType = 's3';
652
697
  sourceRef = key;
653
698
  }
654
699
  const { rows } = await client.query(`INSERT INTO documents
655
- (org_id, layer_id, external_id, source_type, source_ref, title, content_hash, metadata, status)
656
- VALUES ($1,$2,$3,$4,$5,$6,$7,$8,'pending')
700
+ (org_id, layer_id, external_id, source_type, source_ref, title, content_hash, metadata, content_type, status)
701
+ VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,'pending')
657
702
  ON CONFLICT (layer_id, external_id) DO UPDATE SET
658
703
  -- Only touch the row when the content actually changed. A repeat
659
704
  -- with the same bytes must not re-queue work: every client that
@@ -663,6 +708,10 @@ export class NacreIngest {
663
708
  title = COALESCE(EXCLUDED.title, documents.title),
664
709
  content_hash = EXCLUDED.content_hash,
665
710
  metadata = EXCLUDED.metadata,
711
+ -- The worker dispatches on this. A document re-sent as a different
712
+ -- format carries a different hash too, so the status CASE below
713
+ -- already re-queues it; this just keeps the row telling the truth.
714
+ content_type = EXCLUDED.content_type,
666
715
  -- Metadata counts as a change, because it is written into the
667
716
  -- vector payload of every point and a filter reads it from there.
668
717
  -- Updating the row alone would leave the two disagreeing: the
@@ -687,6 +736,7 @@ export class NacreIngest {
687
736
  request.title ?? null,
688
737
  `sha256:${hash}`,
689
738
  JSON.stringify(request.metadata),
739
+ binary ? request.contentType : 'text/plain',
690
740
  ]);
691
741
  const row = rows[0];
692
742
  if (row === undefined)
@@ -713,7 +763,7 @@ export class NacreIngest {
713
763
  const principals = await principalsFor(client, auth, this.deps.principalsCache);
714
764
  const grants = await loadGrants(client, auth.orgId, principals);
715
765
  const tree = await loadScopeTree(client, auth.orgId, [documentId]);
716
- const plan = resolve({ orgId: auth.orgId, role: auth.role, principals, grants, tree }, 'write');
766
+ const plan = activeResolver().resolve({ orgId: auth.orgId, role: auth.role, principals, grants, tree }, 'write');
717
767
  if (plan.kind === 'none')
718
768
  return false;
719
769
  const { rows } = await client.query(`SELECT layer_id FROM documents
@@ -786,7 +836,7 @@ export class PostgresJobs {
786
836
  // different projection — and needs the same check. The status and the
787
837
  // error string are both facts about a document the caller may not be
788
838
  // allowed to know exists.
789
- const plan = resolve(await contextFor(client, auth, this.principalsCache), 'read');
839
+ const plan = activeResolver().resolve(await contextFor(client, auth, this.principalsCache), 'read');
790
840
  if (plan.kind === 'none')
791
841
  return undefined;
792
842
  const { rows } = await client.query(`SELECT id, status, error, chunk_count, layer_id FROM documents
@@ -879,7 +929,7 @@ export class PostgresLayers {
879
929
  */
880
930
  async list(auth, page) {
881
931
  return withOrg(this.pool, auth.orgId, async (client) => {
882
- const plan = resolve(await contextFor(client, auth, this.principalsCache), 'read');
932
+ const plan = activeResolver().resolve(await contextFor(client, auth, this.principalsCache), 'read');
883
933
  if (plan.kind === 'none')
884
934
  return { items: [], nextCursor: null };
885
935
  // The count comes from the same statement. It is what a catalog is
@@ -893,7 +943,12 @@ export class PostgresLayers {
893
943
  // grants exist — a commercial module — this becomes a number that
894
944
  // discloses more than the caller can reach, and it has to be recomputed
895
945
  // per caller or dropped.
946
+ // `created_at::text` beside `created_at`, and the cursor is built from
947
+ // it. See `Position.createdAt` — a timestamp that has been through a
948
+ // JavaScript `Date` is truncated to milliseconds, and a truncated bound
949
+ // matches the row it came from all over again.
896
950
  const projection = `l.id, l.slug, l.name, l.workspace_id, l.description, l.created_at,
951
+ l.created_at::text AS created_at_text,
897
952
  (SELECT count(*) FROM documents d
898
953
  WHERE d.layer_id = l.id AND d.deleted_at IS NULL) AS document_count`;
899
954
  // Ordered by (created_at, id) rather than by slug, because that is the
@@ -921,7 +976,10 @@ export class PostgresLayers {
921
976
  documentCount: Number(r.document_count),
922
977
  createdAt: r.created_at.toISOString(),
923
978
  }));
924
- return pageOf(layers, page, (l) => ({ createdAt: l.createdAt, id: l.id }));
979
+ return pageOf(layers, page, (l, i) => ({
980
+ createdAt: rows[i].created_at_text,
981
+ id: l.id,
982
+ }));
925
983
  }, this.scope);
926
984
  }
927
985
  async update(auth, layerId, input) {
@@ -1101,13 +1159,28 @@ export class PostgresWorkspaces {
1101
1159
  // administer, and therefore unable to create the first layer in it,
1102
1160
  // which is the deadlock this endpoint was added to break. It was
1103
1161
  // 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,
1162
+ const plan = activeResolver().resolve(context, 'read');
1163
+ // Seek and limit in SQL.
1164
+ //
1165
+ // Neither was here: the statement fetched every workspace in the
1166
+ // organization, ignored `page.after` entirely, and handed the whole
1167
+ // list to `pageOf` — which then answered "there is another page"
1168
+ // because the list was at least as long as the limit. So
1169
+ // `GET /v1/workspaces` returned the same complete list on every page
1170
+ // and never terminated, and a client following `next_cursor` looped
1171
+ // forever. Found by a test that walked the listing one item at a time.
1172
+ const after = page?.after;
1173
+ const seek = after === undefined ? '' : ' AND (w.created_at, w.id) > ($2::timestamptz, $3::uuid)';
1174
+ // One extra, because the filter below removes rows the caller may not
1175
+ // reach: without it a page whose last row is filtered out would report
1176
+ // itself as the end of the collection.
1177
+ const cap = page === undefined ? '' : ` LIMIT ${page.limit + 1}`;
1178
+ const { rows } = await client.query(`SELECT w.id, w.slug, w.name, w.created_at, w.created_at::text AS created_at_text,
1106
1179
  (SELECT array_agg(l.id) FROM layers l
1107
1180
  WHERE l.workspace_id = w.id AND l.deleted_at IS NULL) AS layer_ids
1108
1181
  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]);
1182
+ WHERE w.org_id = $1 AND w.deleted_at IS NULL${seek}
1183
+ ORDER BY w.created_at, w.id${cap}`, after === undefined ? [auth.orgId] : [auth.orgId, after.createdAt, after.id]);
1111
1184
  const reachable = rows.filter((w) => {
1112
1185
  if (plan.kind === 'all')
1113
1186
  return true;
@@ -1120,14 +1193,28 @@ export class PostgresWorkspaces {
1120
1193
  // for a workspace with nothing in it yet.
1121
1194
  return referenceAllows(context, { type: 'workspace', id: w.id }, 'read');
1122
1195
  });
1123
- const items = reachable.map((w) => ({
1196
+ const fetched = page === undefined ? rows : rows.slice(0, page.limit);
1197
+ const items = reachable
1198
+ .filter((w) => fetched.some((f) => f.id === w.id))
1199
+ .map((w) => ({
1124
1200
  id: w.id,
1125
1201
  slug: w.slug,
1126
1202
  name: w.name,
1127
1203
  layerCount: (w.layer_ids ?? []).length,
1128
1204
  createdAt: w.created_at.toISOString(),
1129
1205
  }));
1130
- return pageOf(items, page, (w) => ({ createdAt: w.createdAt, id: w.id }));
1206
+ // From the last row **fetched**, not the last returned — the same rule
1207
+ // the grant listing follows and for the same reason: the filter above
1208
+ // removes rows the caller may not reach, and a cursor taken from a
1209
+ // survivor would skip everything the filter dropped after it.
1210
+ //
1211
+ // Which makes a short page normal here rather than a signal, and
1212
+ // `next_cursor` the only thing that says whether more exist.
1213
+ const lastFetched = fetched[fetched.length - 1];
1214
+ const nextCursor = page !== undefined && rows.length > page.limit && lastFetched !== undefined
1215
+ ? encodeCursor({ createdAt: lastFetched.created_at_text, id: lastFetched.id })
1216
+ : null;
1217
+ return { items, nextCursor };
1131
1218
  }, this.scope);
1132
1219
  }
1133
1220
  /**
@@ -1250,14 +1337,14 @@ export class PostgresGrants {
1250
1337
  async list(auth, page) {
1251
1338
  return withOrg(this.pool, auth.orgId, async (client) => {
1252
1339
  const context = await contextFor(client, auth, this.principalsCache);
1253
- const plan = resolve(context, 'admin');
1340
+ const plan = activeResolver().resolve(context, 'admin');
1254
1341
  if (plan.kind === 'none')
1255
1342
  return { items: [], nextCursor: null };
1256
1343
  const after = page?.after;
1257
1344
  const seek = after === undefined ? '' : ' AND (created_at, id) > ($2::timestamptz, $3::uuid)';
1258
1345
  const cap = page === undefined ? '' : ` LIMIT ${page.limit}`;
1259
1346
  const { rows } = await client.query(`SELECT id, principal_type, principal_id, scope_type, scope_id, permission, effect, source,
1260
- created_at
1347
+ created_at, created_at::text AS created_at_text
1261
1348
  FROM grants WHERE org_id = $1${seek} ORDER BY created_at, id${cap}`, after === undefined ? [auth.orgId] : [auth.orgId, after.createdAt, after.id]);
1262
1349
  const visible = rows
1263
1350
  .filter((r) => referenceAllows(context, { type: r.scope_type, id: r.scope_id }, 'admin'))
@@ -1277,7 +1364,7 @@ export class PostgresGrants {
1277
1364
  // it — a page of grants silently missing from an administrator's view.
1278
1365
  const lastFetched = rows[rows.length - 1];
1279
1366
  const nextCursor = page !== undefined && rows.length >= page.limit && lastFetched !== undefined
1280
- ? encodeCursor({ createdAt: lastFetched.created_at.toISOString(), id: lastFetched.id })
1367
+ ? encodeCursor({ createdAt: lastFetched.created_at_text, id: lastFetched.id })
1281
1368
  : null;
1282
1369
  return { items: visible, nextCursor };
1283
1370
  }, this.scope);
@@ -1293,6 +1380,33 @@ export class PostgresGrants {
1293
1380
  if (!referenceAllows(context, { type: input.scopeType, id: input.scopeId }, 'admin')) {
1294
1381
  return undefined;
1295
1382
  }
1383
+ // And the scope has to exist in this organization, which the check
1384
+ // above does not establish. `referenceAllows` returns `true` for
1385
+ // `org_admin` on its third line — rule 3, admin on every scope
1386
+ // implicitly — and it returns *before* the scope is placed, so an
1387
+ // administrator naming any uuid at all passed.
1388
+ //
1389
+ // Not a leak, and the reason is worth stating rather than assumed: a
1390
+ // grant on a scope that is not here becomes `should: layer_id = X`
1391
+ // beside an unconditional `must: org_id = <this tenant>`, and points
1392
+ // carrying layer X belong to another tenant and live in another
1393
+ // collection. The second line of defence holds. What got written was a
1394
+ // row that permits nothing and points at nothing.
1395
+ //
1396
+ // It still has to be refused. An administrator reading `/v1/grants`
1397
+ // sees a grant they cannot explain on an id they cannot look up, and
1398
+ // `404` stops meaning what invariant 4 says it means — "no permission"
1399
+ // and "no such object" are one answer, which requires that naming a
1400
+ // nonexistent object *reaches* that answer rather than succeeding.
1401
+ //
1402
+ // Written out per table rather than assembled, so the table name can
1403
+ // never come from a caller-supplied string. `scopeType` is a checked
1404
+ // union already; this makes it structural.
1405
+ const exists = await client.query(input.scopeType === 'workspace'
1406
+ ? `SELECT 1 FROM workspaces WHERE org_id = $1 AND id = $2 AND deleted_at IS NULL`
1407
+ : `SELECT 1 FROM layers WHERE org_id = $1 AND id = $2 AND deleted_at IS NULL`, [auth.orgId, input.scopeId]);
1408
+ if (exists.rowCount !== 1)
1409
+ return undefined;
1296
1410
  const { rows } = await client.query(`INSERT INTO grants
1297
1411
  (org_id, principal_type, principal_id, scope_type, scope_id, permission, effect, source)
1298
1412
  VALUES ($1,$2,$3,$4,$5,$6,'allow','api')
@@ -1446,7 +1560,8 @@ export class PostgresAuditReader {
1446
1560
  // without a second query and without a count — a count over a
1447
1561
  // retention window is the expensive thing this endpoint must not do on
1448
1562
  // every request.
1449
- const { rows } = await client.query(`SELECT id::text, occurred_at, actor_type, actor_id, actor_label, action,
1563
+ const { rows } = await client.query(`SELECT id::text, occurred_at, occurred_at::text AS occurred_at_text,
1564
+ actor_type, actor_id, actor_label, action,
1450
1565
  surface, client, target, result, detail, request_id
1451
1566
  FROM audit_events
1452
1567
  WHERE ${where.join(' AND ')}
@@ -1466,9 +1581,19 @@ export class PostgresAuditReader {
1466
1581
  detail: r.detail ?? {},
1467
1582
  requestId: r.request_id,
1468
1583
  }));
1584
+ // From the row rather than from the mapped record, because the record
1585
+ // carries an ISO string and this cursor needs full precision.
1586
+ //
1587
+ // This one **skips** rather than repeats, and that is the same bug seen
1588
+ // from the other side: the ordering is descending, so a bound truncated
1589
+ // downwards excludes the row it came from *and* everything between the
1590
+ // truncated value and the real one. On a listing that would lose a page
1591
+ // of layers; on the access log it silently loses events, in the one
1592
+ // place the product promises a precise answer.
1593
+ const lastRow = rows[Math.min(page.limit, rows.length) - 1];
1469
1594
  const last = items[items.length - 1];
1470
- const nextCursor = rows.length > page.limit && last !== undefined
1471
- ? encodeCursor({ createdAt: last.occurredAt, id: last.id })
1595
+ const nextCursor = rows.length > page.limit && last !== undefined && lastRow !== undefined
1596
+ ? encodeCursor({ createdAt: lastRow.occurred_at_text, id: last.id })
1472
1597
  : null;
1473
1598
  return { items, nextCursor };
1474
1599
  }, this.scopeFor());
@@ -1520,7 +1645,7 @@ export class PostgresReindex {
1520
1645
  // vector in the layer and changes which model answers every future
1521
1646
  // query — `read` is nowhere near enough, and `write` is about putting
1522
1647
  // documents in rather than about the layer itself.
1523
- const plan = resolve(await contextFor(client, auth, this.principalsCache), 'admin');
1648
+ const plan = activeResolver().resolve(await contextFor(client, auth, this.principalsCache), 'admin');
1524
1649
  if (plan.kind === 'none')
1525
1650
  return undefined;
1526
1651
  // Locked for the whole decision. Two starts arriving together would
@@ -1635,7 +1760,7 @@ export class PostgresReindex {
1635
1760
  // being migrated is not an administrative fact — it explains why a
1636
1761
  // result set moved — and hiding it from someone who can already read
1637
1762
  // the layer buys nothing.
1638
- const plan = resolve(await contextFor(client, auth, this.principalsCache), 'read');
1763
+ const plan = activeResolver().resolve(await contextFor(client, auth, this.principalsCache), 'read');
1639
1764
  if (plan.kind === 'none')
1640
1765
  return undefined;
1641
1766
  const { rows } = await client.query(`SELECT id, vector_name, reindex_state FROM layers
@@ -1686,7 +1811,87 @@ export class PostgresReindex {
1686
1811
  failed: state.failed,
1687
1812
  progress: reindexProgress(live),
1688
1813
  error: state.error ?? null,
1814
+ check: state.check ?? null,
1689
1815
  };
1690
1816
  }
1691
1817
  }
1818
+ /**
1819
+ * The reference query set behind the reindex recall gate.
1820
+ *
1821
+ * `admin` on the layer for both operations, and that is a decision rather than
1822
+ * a copy of the reindex adapter above. Reading the set reveals which documents
1823
+ * an operator considers the canonical answers to a query, which is a statement
1824
+ * about the layer's contents; rule 7 makes `admin` the permission that implies
1825
+ * being allowed to see them, and rule 6 means `write` does not.
1826
+ */
1827
+ export class PostgresReferenceQueries {
1828
+ pool;
1829
+ role;
1830
+ principalsCache;
1831
+ constructor(pool, role, principalsCache) {
1832
+ this.pool = pool;
1833
+ this.role = role;
1834
+ this.principalsCache = principalsCache;
1835
+ }
1836
+ get scope() {
1837
+ return this.role === undefined ? {} : { role: this.role };
1838
+ }
1839
+ /**
1840
+ * The layer, if this caller may administer it.
1841
+ *
1842
+ * `undefined` for a layer that is not there and for one they may not touch —
1843
+ * one answer, which the handler turns into `404`. Deleted layers are not
1844
+ * there: a reference set on a deleted layer describes nothing.
1845
+ */
1846
+ async administrable(client, auth, layerId) {
1847
+ const plan = activeResolver().resolve(await contextFor(client, auth, this.principalsCache), 'admin');
1848
+ if (plan.kind === 'none')
1849
+ return false;
1850
+ if (plan.kind === 'scoped' && !plan.layers.includes(layerId))
1851
+ return false;
1852
+ const { rows } = await client.query('SELECT id FROM layers WHERE org_id = $1 AND id = $2 AND deleted_at IS NULL', [auth.orgId, layerId]);
1853
+ return rows.length > 0;
1854
+ }
1855
+ async list(auth, layerId) {
1856
+ return withOrg(this.pool, auth.orgId, async (client) => {
1857
+ if (!(await this.administrable(client, auth, layerId)))
1858
+ return undefined;
1859
+ const { rows } = await client.query(`SELECT id, query, expected FROM reference_queries
1860
+ WHERE org_id = $1 AND layer_id = $2 ORDER BY ordinal`, [auth.orgId, layerId]);
1861
+ return rows.map((r) => ({ id: r.id, query: r.query, expected: r.expected }));
1862
+ }, this.scope);
1863
+ }
1864
+ async replace(auth, layerId, queries) {
1865
+ return withOrg(this.pool, auth.orgId, async (client) => {
1866
+ if (!(await this.administrable(client, auth, layerId)))
1867
+ return undefined;
1868
+ // Delete then insert, in the transaction `withOrg` already opened. A
1869
+ // set is one statement about the layer, so there is no moment at which
1870
+ // half of the old one and half of the new one are both in the table —
1871
+ // which matters because the worker reads this to decide whether a layer
1872
+ // has a gate at all.
1873
+ await client.query('DELETE FROM reference_queries WHERE org_id = $1 AND layer_id = $2', [
1874
+ auth.orgId,
1875
+ layerId,
1876
+ ]);
1877
+ const inserted = [];
1878
+ for (const [ordinal, q] of queries.entries()) {
1879
+ const { rows } = await client.query(`INSERT INTO reference_queries (org_id, layer_id, query, expected, ordinal)
1880
+ VALUES ($1, $2, $3, $4, $5) RETURNING id`, [auth.orgId, layerId, q.query, [...q.expected], ordinal]);
1881
+ inserted.push({
1882
+ id: rows[0]?.id,
1883
+ query: q.query,
1884
+ expected: [...q.expected],
1885
+ });
1886
+ }
1887
+ // The external ids are deliberately **not** validated against
1888
+ // `documents` here. A reference set is often written before the
1889
+ // documents it names are ingested, and refusing it then would make the
1890
+ // gate impossible to set up on a new layer. The check resolves them
1891
+ // when it runs, and an entry that resolves to nothing fails the reindex
1892
+ // by name — which is the moment it actually matters.
1893
+ return inserted;
1894
+ }, this.scope);
1895
+ }
1896
+ }
1692
1897
  //# sourceMappingURL=adapters.js.map