@nacre.work/api 0.0.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 (46) hide show
  1. package/README.md +3 -0
  2. package/dist/__tests__/admin-surface.test.d.ts +2 -0
  3. package/dist/__tests__/admin-surface.test.d.ts.map +1 -0
  4. package/dist/__tests__/admin-surface.test.js +431 -0
  5. package/dist/__tests__/admin-surface.test.js.map +1 -0
  6. package/dist/__tests__/init.test.d.ts +2 -0
  7. package/dist/__tests__/init.test.d.ts.map +1 -0
  8. package/dist/__tests__/init.test.js +125 -0
  9. package/dist/__tests__/init.test.js.map +1 -0
  10. package/dist/__tests__/service-keys.test.d.ts +2 -0
  11. package/dist/__tests__/service-keys.test.d.ts.map +1 -0
  12. package/dist/__tests__/service-keys.test.js +204 -0
  13. package/dist/__tests__/service-keys.test.js.map +1 -0
  14. package/dist/adapters.d.ts +197 -0
  15. package/dist/adapters.d.ts.map +1 -0
  16. package/dist/adapters.js +672 -0
  17. package/dist/adapters.js.map +1 -0
  18. package/dist/auth.d.ts +42 -0
  19. package/dist/auth.d.ts.map +1 -0
  20. package/dist/auth.js +106 -0
  21. package/dist/auth.js.map +1 -0
  22. package/dist/errors.d.ts +43 -0
  23. package/dist/errors.d.ts.map +1 -0
  24. package/dist/errors.js +101 -0
  25. package/dist/errors.js.map +1 -0
  26. package/dist/index.d.ts +10 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +6 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/init.d.ts +49 -0
  31. package/dist/init.d.ts.map +1 -0
  32. package/dist/init.js +201 -0
  33. package/dist/init.js.map +1 -0
  34. package/dist/main.d.ts +2 -0
  35. package/dist/main.d.ts.map +1 -0
  36. package/dist/main.js +113 -0
  37. package/dist/main.js.map +1 -0
  38. package/dist/server.d.ts +239 -0
  39. package/dist/server.d.ts.map +1 -0
  40. package/dist/server.js +543 -0
  41. package/dist/server.js.map +1 -0
  42. package/dist/service-keys.d.ts +82 -0
  43. package/dist/service-keys.d.ts.map +1 -0
  44. package/dist/service-keys.js +158 -0
  45. package/dist/service-keys.js.map +1 -0
  46. package/package.json +40 -0
@@ -0,0 +1,204 @@
1
+ import { createPool } from '@nacre.work/core';
2
+ import { afterAll, beforeAll, describe, expect, it } from 'vitest';
3
+ import { authenticate } from '../auth.js';
4
+ import { Problem } from '../errors.js';
5
+ import { generateKey, hashOf, KEY_PREFIX, looksLikeServiceKey, PostgresServiceAccounts, PostgresServiceKeys, prefixOf, } from '../service-keys.js';
6
+ /**
7
+ * Service account keys.
8
+ *
9
+ * The one credential meant to outlive a session, and therefore the one whose
10
+ * failures are worth being paranoid about: a key that survives revocation, a
11
+ * key that authenticates into the wrong organization, or a key recoverable from
12
+ * the database are each worse than anything the token path can do, because a
13
+ * token expires on its own and this does not.
14
+ */
15
+ const url = process.env.NACRE_PG_URL;
16
+ if (!url && process.env.CI) {
17
+ throw new Error('NACRE_PG_URL is not set and CI is; the service key path would go untested.');
18
+ }
19
+ const when = url ? describe : describe.skip;
20
+ const ORG = 'cccccccc-cccc-cccc-cccc-cccccccccccc';
21
+ const OTHER = 'dddddddd-dddd-dddd-dddd-dddddddddddd';
22
+ const admin = { orgId: ORG, principal: { type: 'user', id: 'u1' }, role: 'org_admin' };
23
+ let pool;
24
+ let accounts;
25
+ let keys;
26
+ describe('key shape', () => {
27
+ it('a key is opaque, prefixed, and not a UUID', () => {
28
+ const key = generateKey();
29
+ expect(key.startsWith(KEY_PREFIX)).toBe(true);
30
+ expect(looksLikeServiceKey(key)).toBe(true);
31
+ // 32 bytes from the CSPRNG. A UUID is 122 bits of which several are fixed,
32
+ // and it is routinely treated as an identifier rather than a secret.
33
+ expect(key.length).toBeGreaterThan(KEY_PREFIX.length + 40);
34
+ });
35
+ it('two keys never collide', () => {
36
+ const many = new Set(Array.from({ length: 500 }, () => generateKey()));
37
+ expect(many.size).toBe(500);
38
+ });
39
+ it('the stored prefix is not enough to reconstruct the key', () => {
40
+ const key = generateKey();
41
+ expect(prefixOf(key).length).toBe(KEY_PREFIX.length + 8);
42
+ expect(key.startsWith(prefixOf(key))).toBe(true);
43
+ expect(prefixOf(key).length).toBeLessThan(key.length / 2);
44
+ });
45
+ it('the hash is not the key', () => {
46
+ const key = generateKey();
47
+ expect(hashOf(key)).not.toContain(key.slice(KEY_PREFIX.length));
48
+ expect(hashOf(key)).toBe(hashOf(key));
49
+ expect(hashOf(key)).not.toBe(hashOf(generateKey()));
50
+ });
51
+ });
52
+ when('service accounts, against the database', () => {
53
+ beforeAll(async () => {
54
+ pool = createPool({ connectionString: url });
55
+ accounts = new PostgresServiceAccounts(pool, 'nacre_app');
56
+ keys = new PostgresServiceKeys(pool, 'nacre_app');
57
+ const c = await pool.connect();
58
+ try {
59
+ for (const [id, slug] of [
60
+ [ORG, 'keysone'],
61
+ [OTHER, 'keystwo'],
62
+ ]) {
63
+ await c.query(
64
+ // slug is citext and name is text; one parameter cannot feed both.
65
+ `INSERT INTO organizations (id, slug, name, vector_collection)
66
+ VALUES ($1,$2,$3,$4) ON CONFLICT DO NOTHING`, [id, slug, slug, `org_${slug}`]);
67
+ }
68
+ await c.query(`DELETE FROM service_accounts WHERE org_id IN ($1,$2)`, [ORG, OTHER]);
69
+ }
70
+ finally {
71
+ c.release();
72
+ }
73
+ });
74
+ afterAll(async () => {
75
+ await pool?.end();
76
+ });
77
+ it('a new key authenticates as its own organization, as a member', async () => {
78
+ const { key, account } = await accounts.create(admin, 'agent');
79
+ const resolved = await keys.resolve(key);
80
+ expect(resolved?.orgId).toBe(ORG);
81
+ expect(resolved?.principal).toEqual({ type: 'service_account', id: account.id });
82
+ // Never an admin by virtue of being a service account. Everything it can
83
+ // reach comes from a grant — which is what "permissions are exactly the
84
+ // service account's" has to mean if it means anything.
85
+ expect(resolved?.role).toBe('member');
86
+ });
87
+ it('the key is never readable again', async () => {
88
+ const { key, account } = await accounts.create(admin, 'agent-2');
89
+ const listed = (await accounts.list(admin)).find((a) => a.id === account.id);
90
+ expect(listed).toBeDefined();
91
+ expect(JSON.stringify(listed)).not.toContain(key.slice(KEY_PREFIX.length));
92
+ // The prefix is shown so two keys can be told apart when revoking one.
93
+ expect(listed?.keyPrefix).toBe(prefixOf(key));
94
+ });
95
+ it('a revoked key stops working immediately', async () => {
96
+ const { key, account } = await accounts.create(admin, 'agent-3');
97
+ expect(await keys.resolve(key)).toBeDefined();
98
+ expect(await accounts.revoke(admin, account.id)).toBe(true);
99
+ // No TTL, no propagation window, nothing to wait for. Unlike a token, this
100
+ // credential has no expiry of its own, so revocation is the only thing that
101
+ // ever ends it.
102
+ expect(await keys.resolve(key)).toBeUndefined();
103
+ });
104
+ it('revoking twice is not an error the second time, it is a no', async () => {
105
+ const { account } = await accounts.create(admin, 'agent-4');
106
+ expect(await accounts.revoke(admin, account.id)).toBe(true);
107
+ expect(await accounts.revoke(admin, account.id)).toBe(false);
108
+ });
109
+ it('a revoked key is still listed, and says when', async () => {
110
+ const { account } = await accounts.create(admin, 'agent-5');
111
+ await accounts.revoke(admin, account.id);
112
+ const listed = (await accounts.list(admin)).find((a) => a.id === account.id);
113
+ // A key that vanishes on revocation looks like one that never existed, and
114
+ // the audit log refers to this id — a row that disappears turns every past
115
+ // event into an unresolvable reference.
116
+ expect(listed?.revokedAt).not.toBeNull();
117
+ });
118
+ it('another organization cannot revoke this one’s key', async () => {
119
+ const { key, account } = await accounts.create(admin, 'agent-6');
120
+ const intruder = { ...admin, orgId: OTHER };
121
+ expect(await accounts.revoke(intruder, account.id)).toBe(false);
122
+ expect(await keys.resolve(key), 'the key must still work').toBeDefined();
123
+ });
124
+ it('another organization does not see this one’s keys', async () => {
125
+ await accounts.create(admin, 'agent-7');
126
+ const theirs = await accounts.list({ ...admin, orgId: OTHER });
127
+ expect(theirs.every((a) => a.name !== 'agent-7')).toBe(true);
128
+ });
129
+ it('a key that does not exist, and a mangled one, resolve to nothing', async () => {
130
+ const { key } = await accounts.create(admin, 'agent-8');
131
+ for (const bad of [
132
+ generateKey(),
133
+ `${key}x`,
134
+ key.slice(0, -1),
135
+ `${prefixOf(key)}${'A'.repeat(43)}`,
136
+ 'nacre_sk_',
137
+ 'not-a-key',
138
+ ]) {
139
+ expect(await keys.resolve(bad), bad.slice(0, 20)).toBeUndefined();
140
+ }
141
+ });
142
+ it('a key with the right prefix and the wrong secret is refused', async () => {
143
+ const { key } = await accounts.create(admin, 'agent-9');
144
+ // The prefix is the indexed lookup, not the credential. If the hash
145
+ // comparison were ever dropped, every one of these would authenticate as
146
+ // the real account — and the prefix is stored in clear and shown in
147
+ // listings, so it is not a secret to begin with.
148
+ const forged = `${prefixOf(key)}${'B'.repeat(key.length - prefixOf(key).length)}`;
149
+ expect(forged.startsWith(prefixOf(key))).toBe(true);
150
+ expect(await keys.resolve(forged)).toBeUndefined();
151
+ });
152
+ it('authenticate() answers a bad key exactly as it answers a bad token', async () => {
153
+ const verify = {
154
+ key: new TextEncoder().encode('a'.repeat(32)),
155
+ issuer: 'https://api.nacre.test',
156
+ audience: 'nacre',
157
+ serviceKeys: keys,
158
+ };
159
+ const badKey = await authenticate(`Bearer ${generateKey()}`, verify, '/v1/search', 'r1');
160
+ const badToken = await authenticate('Bearer not-a-token', verify, '/v1/search', 'r1');
161
+ expect(badKey).toBeInstanceOf(Problem);
162
+ expect(badToken).toBeInstanceOf(Problem);
163
+ // "Revoked key", "unknown key" and "expired token" are one answer. Telling
164
+ // them apart says which guess was closest, and a key is guessable in a way
165
+ // a signature is not.
166
+ expect(badKey.toJSON()).toEqual(badToken.toJSON());
167
+ });
168
+ it('authenticate() accepts a real key and carries its organization', async () => {
169
+ const { key, account } = await accounts.create(admin, 'agent-10');
170
+ const resolved = await authenticate(`Bearer ${key}`, {
171
+ key: new TextEncoder().encode('a'.repeat(32)),
172
+ issuer: 'https://api.nacre.test',
173
+ audience: 'nacre',
174
+ serviceKeys: keys,
175
+ }, '/v1/search', 'r1');
176
+ expect(resolved).not.toBeInstanceOf(Problem);
177
+ const context = resolved;
178
+ expect(context.orgId).toBe(ORG);
179
+ // The account it names, not just some account in the right organization.
180
+ // Every grant is keyed on this id, so resolving to the wrong one would
181
+ // hand the caller another service account's permissions.
182
+ expect(context.principal).toEqual({ type: 'service_account', id: account.id });
183
+ });
184
+ it('a surface with no resolver refuses keys rather than ignoring the prefix', async () => {
185
+ const { key } = await accounts.create(admin, 'agent-11');
186
+ const result = await authenticate(`Bearer ${key}`, {
187
+ key: new TextEncoder().encode('a'.repeat(32)),
188
+ issuer: 'https://api.nacre.test',
189
+ audience: 'nacre',
190
+ }, '/v1/search', 'r1');
191
+ // Falling through to the JWT path would try to verify an opaque string as a
192
+ // signed token. That fails today, but it fails for the wrong reason, and
193
+ // "this credential type is not served here" should not depend on that.
194
+ expect(result).toBeInstanceOf(Problem);
195
+ });
196
+ it('using a key records when, without touching the decision', async () => {
197
+ const { key, account } = await accounts.create(admin, 'agent-12');
198
+ expect((await accounts.list(admin)).find((a) => a.id === account.id)?.lastUsedAt).toBeNull();
199
+ expect(await keys.resolve(key)).toBeDefined();
200
+ await new Promise((r) => setTimeout(r, 50));
201
+ expect((await accounts.list(admin)).find((a) => a.id === account.id)?.lastUsedAt).not.toBeNull();
202
+ });
203
+ });
204
+ //# sourceMappingURL=service-keys.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-keys.test.js","sourceRoot":"","sources":["../../src/__tests__/service-keys.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,197 @@
1
+ import { aclTags, buildFilter, VectorStore } from '@nacre.work/core';
2
+ import type { Pool } from 'pg';
3
+ import type { AuditEvent, AuditSink, Documents, GrantInput, GrantRecord, Grants, Ingest, IngestOutcome, IngestRequest, Job, Jobs, Layer, LayerOutcome, DocumentView, Layers, SearchHit, SearchService } from './server.js';
4
+ import type { AuthContext } from './auth.js';
5
+ /**
6
+ * The adapters that put the permission model on the request path.
7
+ *
8
+ * `NacreSearchService` is the one to read. Every step between a token and a
9
+ * result is here, in order, and none of them can be skipped by a caller —
10
+ * there is no method that takes a filter, and no method that takes an
11
+ * organization.
12
+ */
13
+ export interface Embedder {
14
+ embed(texts: readonly string[]): Promise<readonly (readonly number[])[]>;
15
+ }
16
+ export declare class PostgresDocuments implements Documents {
17
+ private readonly pool;
18
+ private readonly role?;
19
+ constructor(pool: Pool, role?: string | undefined);
20
+ /**
21
+ * Undefined for absent, for another organization's, and for one this caller
22
+ * has no grant reaching — one answer for all three.
23
+ *
24
+ * The query is scoped three ways on purpose: `withOrg` sets the row-level
25
+ * security context, `org_id` is named in the WHERE clause anyway, and the
26
+ * plan decides whether the layer it landed in is reachable. Any one alone
27
+ * would do for tenancy; only the third does anything about permissions
28
+ * inside a tenant, and it is the one that was missing.
29
+ */
30
+ read(auth: AuthContext, documentId: string): Promise<DocumentView | undefined>;
31
+ }
32
+ export interface SearchDeps {
33
+ readonly pool: Pool;
34
+ readonly vectors: VectorStore;
35
+ readonly embedder: Embedder;
36
+ /** Resolved per organization; the collection name is derived from it. */
37
+ readonly orgSlug: (orgId: string) => Promise<string | undefined>;
38
+ readonly vectorName: string;
39
+ readonly role?: string;
40
+ }
41
+ export declare class NacreSearchService implements SearchService {
42
+ private readonly deps;
43
+ constructor(deps: SearchDeps);
44
+ search(auth: AuthContext, query: string, topK: number): Promise<readonly SearchHit[]>;
45
+ /**
46
+ * Turn hits into results a caller can read.
47
+ *
48
+ * The vector store holds no text — chunks live in Postgres, and the payload
49
+ * carries only what the filter needs. Returning the payload as the result,
50
+ * which is what happened before this existed, gave a caller identifiers and a
51
+ * score and nothing to show a user, and shipped `acl_tags` and `acl_version`
52
+ * along with it.
53
+ *
54
+ * This is a join and not a filter. The permitted set was decided inside the
55
+ * index traversal and is not narrowed here — with one exception, stated
56
+ * rather than left implicit: a chunk whose document has since been tombstoned
57
+ * in Postgres has no row to join to and does not appear. That is invariant I5
58
+ * enforced a second time on a different store, the same way row-level
59
+ * security backs up invariant I1, and it can only ever remove a document that
60
+ * is already meant to be gone.
61
+ *
62
+ * The order the index returned is kept. Postgres has no reason to preserve
63
+ * it, and re-sorting by anything else here would be a reranking step nobody
64
+ * asked for.
65
+ */
66
+ private hydrate;
67
+ /** Exposed so the filter can be inspected in tests without a vector store. */
68
+ static filterFor(orgId: string, plan: Parameters<typeof buildFilter>[1]): import("@nacre.work/core").VectorFilter;
69
+ }
70
+ /**
71
+ * The audit sink.
72
+ *
73
+ * Writes with the application role, which has INSERT and SELECT on
74
+ * `audit_events` and neither UPDATE nor DELETE — revoked at the database level
75
+ * by migration 0002, so a bug here cannot rewrite history and neither can
76
+ * anyone holding these credentials.
77
+ */
78
+ export declare class PostgresAudit implements AuditSink {
79
+ private readonly pool;
80
+ private readonly role?;
81
+ constructor(pool: Pool, role?: string | undefined);
82
+ write(event: AuditEvent): Promise<void>;
83
+ }
84
+ /**
85
+ * An OpenAI-compatible embedding client.
86
+ *
87
+ * "Bring your own model" means this has to work against anything speaking that
88
+ * shape, so the only thing assumed is `POST /embeddings` returning
89
+ * `{ data: [{ embedding: number[] }] }`.
90
+ */
91
+ export declare class HttpEmbedder implements Embedder {
92
+ private readonly endpoint;
93
+ private readonly model;
94
+ private readonly dimensions;
95
+ constructor(endpoint: string, model: string, dimensions: number);
96
+ embed(texts: readonly string[]): Promise<readonly (readonly number[])[]>;
97
+ }
98
+ export { aclTags };
99
+ /**
100
+ * Queueing a document, with the write permission checked first.
101
+ *
102
+ * `write` is resolved, not `read`. Rule 6 is the whole reason this is a
103
+ * separate resolve: an ingest-only service account has `write` and no `read`,
104
+ * and a check that asked for `read` would refuse exactly the caller this
105
+ * endpoint exists for. Asking for `admin` would refuse them too.
106
+ */
107
+ export interface IngestDeps {
108
+ readonly pool: Pool;
109
+ /**
110
+ * What takes a document out of results. Narrower than `VectorStore` on
111
+ * purpose: the delete path has no business searching, and a port that could
112
+ * would let a later change reach the index without a plan.
113
+ */
114
+ readonly tombstone: DocumentTombstone;
115
+ /** Resolved per organization; the collection name is derived from it. */
116
+ readonly orgSlug: (orgId: string) => Promise<string | undefined>;
117
+ readonly role?: string;
118
+ }
119
+ export interface DocumentTombstone {
120
+ tombstone(orgSlug: string, documentId: string): Promise<void>;
121
+ }
122
+ export declare class NacreIngest implements Ingest {
123
+ private readonly deps;
124
+ constructor(deps: IngestDeps);
125
+ private get pool();
126
+ private get scope();
127
+ /** The layers this caller may write to, by slug. */
128
+ private writableLayer;
129
+ queue(auth: AuthContext, request: IngestRequest): Promise<IngestOutcome | undefined>;
130
+ remove(auth: AuthContext, documentId: string): Promise<boolean>;
131
+ }
132
+ /**
133
+ * Ingest job status.
134
+ *
135
+ * The job id is the document id — ingest writes a row and the worker moves its
136
+ * status, so there is no separate job table to drift out of step with it. That
137
+ * also means the visibility rule is the document's: absent and another
138
+ * organization's answer identically.
139
+ */
140
+ export declare class PostgresJobs implements Jobs {
141
+ private readonly pool;
142
+ private readonly role?;
143
+ constructor(pool: Pool, role?: string | undefined);
144
+ read(auth: AuthContext, jobId: string): Promise<Job | undefined>;
145
+ }
146
+ export declare class PostgresLayers implements Layers {
147
+ private readonly pool;
148
+ private readonly role?;
149
+ constructor(pool: Pool, role?: string | undefined);
150
+ private get scope();
151
+ /**
152
+ * The layers this caller may read.
153
+ *
154
+ * Narrowed by the plan in SQL rather than fetched and filtered afterwards.
155
+ * This is a listing rather than a search, so a post-filter here would not
156
+ * break invariant I2 — but writing it the other way keeps one habit for both
157
+ * and removes the question of which endpoints are allowed to be sloppy.
158
+ */
159
+ list(auth: AuthContext): Promise<readonly Layer[]>;
160
+ create(auth: AuthContext, input: {
161
+ workspaceId: string;
162
+ slug: string;
163
+ name: string;
164
+ }): Promise<LayerOutcome>;
165
+ }
166
+ export declare class PostgresGrants implements Grants {
167
+ private readonly pool;
168
+ private readonly role?;
169
+ constructor(pool: Pool, role?: string | undefined);
170
+ private get scope();
171
+ /**
172
+ * Every grant in the organization, for a caller who may administer it.
173
+ *
174
+ * Not narrowed to the caller's own grants. Someone who can issue a grant on a
175
+ * scope can already see who else holds one there, and a partial list is worse
176
+ * than none — an administrator who cannot see an existing grant revokes the
177
+ * wrong thing.
178
+ */
179
+ list(auth: AuthContext): Promise<readonly GrantRecord[]>;
180
+ issue(auth: AuthContext, input: GrantInput): Promise<GrantRecord | undefined>;
181
+ /**
182
+ * Withdraw a grant.
183
+ *
184
+ * A real DELETE, not an effect flip. Setting `effect = 'deny'` would look
185
+ * equivalent and is not: a deny is a commercial capability this build refuses
186
+ * to issue, and it beats an allow at any depth — so a "revocation" written
187
+ * that way would also suppress a grant the principal holds through a group or
188
+ * a parent scope. Removing the row leaves whatever else they were given.
189
+ *
190
+ * Admin on the grant's own scope, resolved from the row rather than from
191
+ * anything the caller sent. Reading the scope first and checking it after is
192
+ * the only order that works: the caller names an id, and the permission
193
+ * question is about what that id points at.
194
+ */
195
+ revoke(auth: AuthContext, grantId: string): Promise<boolean>;
196
+ }
197
+ //# sourceMappingURL=adapters.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapters.d.ts","sourceRoot":"","sources":["../src/adapters.ts"],"names":[],"mappings":"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"}