@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
package/dist/server.js ADDED
@@ -0,0 +1,543 @@
1
+ import { createServer } from 'node:http';
2
+ import { randomUUID } from 'node:crypto';
3
+ // Imported rather than taken from the global scope: `lib` is ES2023 with no
4
+ // DOM, so the global URL is not typed here.
5
+ import { URL } from 'node:url';
6
+ import { authenticate, rejectTenantOverride } from './auth.js';
7
+ import { badRequest, internal, notFound, Problem } from './errors.js';
8
+ const MAX_BODY_BYTES = 1_000_000;
9
+ const PRINCIPAL_TYPES = ['user', 'group', 'service_account'];
10
+ const PERMISSIONS = ['read', 'write', 'admin'];
11
+ function accountJson(a) {
12
+ return {
13
+ id: a.id,
14
+ name: a.name,
15
+ // Never the key. It exists in the create response and nowhere else.
16
+ key_prefix: a.keyPrefix,
17
+ created_at: a.createdAt,
18
+ last_used_at: a.lastUsedAt,
19
+ revoked_at: a.revokedAt,
20
+ };
21
+ }
22
+ function grantJson(g) {
23
+ return {
24
+ id: g.id,
25
+ principal_type: g.principalType,
26
+ principal_id: g.principalId,
27
+ scope_type: g.scopeType,
28
+ scope_id: g.scopeId,
29
+ permission: g.permission,
30
+ effect: g.effect,
31
+ source: g.source,
32
+ };
33
+ }
34
+ /**
35
+ * A grant body, or the reason it is not one.
36
+ *
37
+ * Two of the rejections here are the open/commercial boundary rather than
38
+ * validation. `docs/licensing.md` puts document-level ACLs and deny rules in
39
+ * the commercial half; the core is workspace- and layer-scoped allow-only RBAC.
40
+ * The resolver can already represent both — an enterprise resolver registered
41
+ * through `registerAuthzResolver` produces them — but the core must not issue
42
+ * what it does not implement the propagation for.
43
+ *
44
+ * `400` and not `404`: the caller is an administrator asking for a capability
45
+ * this build does not have, which is not a question about whether an object
46
+ * exists, so invariant I4 has no bearing and saying so plainly is right.
47
+ */
48
+ function parseGrant(body) {
49
+ const principalType = body.principal_type;
50
+ const principalId = body.principal_id;
51
+ const scopeType = body.scope_type;
52
+ const scopeId = body.scope_id;
53
+ const permission = body.permission;
54
+ const effect = body.effect;
55
+ if (typeof principalId !== 'string' || typeof scopeId !== 'string') {
56
+ return "'principal_id' and 'scope_id' are required.";
57
+ }
58
+ if (typeof principalType !== 'string' || !PRINCIPAL_TYPES.includes(principalType)) {
59
+ return `'principal_type' must be one of ${PRINCIPAL_TYPES.join(', ')}.`;
60
+ }
61
+ if (typeof permission !== 'string' || !PERMISSIONS.includes(permission)) {
62
+ return `'permission' must be one of ${PERMISSIONS.join(', ')}.`;
63
+ }
64
+ if (scopeType === 'document') {
65
+ return 'Document-level grants are not available in this build. Grant on the layer or the workspace instead.';
66
+ }
67
+ if (scopeType !== 'workspace' && scopeType !== 'layer') {
68
+ return "'scope_type' must be 'workspace' or 'layer'.";
69
+ }
70
+ if (effect !== undefined && effect !== 'allow') {
71
+ return 'Deny rules are not available in this build. Grants are allow-only; remove the grant to revoke it.';
72
+ }
73
+ return {
74
+ principalType: principalType,
75
+ principalId,
76
+ scopeType,
77
+ scopeId,
78
+ permission: permission,
79
+ };
80
+ }
81
+ async function readBody(req) {
82
+ const chunks = [];
83
+ let size = 0;
84
+ for await (const chunk of req) {
85
+ size += chunk.length;
86
+ if (size > MAX_BODY_BYTES)
87
+ throw new Error('body too large');
88
+ chunks.push(chunk);
89
+ }
90
+ if (chunks.length === 0)
91
+ return undefined;
92
+ return JSON.parse(Buffer.concat(chunks).toString('utf8'));
93
+ }
94
+ function send(res, status, body, requestId) {
95
+ const json = JSON.stringify(body);
96
+ res.writeHead(status, {
97
+ 'content-type': status >= 400 ? 'application/problem+json' : 'application/json',
98
+ 'x-request-id': requestId,
99
+ });
100
+ res.end(json);
101
+ }
102
+ export function createApi(options) {
103
+ return createServer((req, res) => {
104
+ void handle(req, res, options).catch(() => {
105
+ // handle() converts everything it can into a Problem. Reaching here means
106
+ // the failure was in the error path itself; say nothing about it.
107
+ if (!res.headersSent)
108
+ send(res, 500, internal(req.url ?? '/', 'unknown').toJSON(), 'unknown');
109
+ });
110
+ });
111
+ }
112
+ async function handle(req, res, options) {
113
+ const requestId = randomUUID();
114
+ const url = new URL(req.url ?? '/', 'http://localhost');
115
+ const instance = url.pathname;
116
+ if (req.method === 'GET' && instance === '/metrics') {
117
+ // Unauthenticated, like every Prometheus endpoint, and therefore carrying
118
+ // nothing that is not already a count. No document ids, no query text, no
119
+ // organization ids — organizations appear by slug, which is in the URL of
120
+ // every request that tenant makes anyway.
121
+ if (options.metrics === undefined) {
122
+ const problem = notFound(instance, requestId);
123
+ send(res, problem.status, problem.toJSON(), requestId);
124
+ return;
125
+ }
126
+ const body = await options.metrics.render();
127
+ res.writeHead(200, { 'content-type': 'text/plain; version=0.0.4; charset=utf-8' });
128
+ res.end(body);
129
+ return;
130
+ }
131
+ if (req.method === 'GET' && instance === '/v1/health') {
132
+ // Liveness touches no dependency. A health check that calls Postgres turns
133
+ // one slow database into a cascading restart loop.
134
+ send(res, 200, { status: 'ok' }, requestId);
135
+ return;
136
+ }
137
+ const auth = await authenticate(req.headers.authorization, options.verify, instance, requestId);
138
+ if (auth instanceof Problem) {
139
+ send(res, auth.status, auth.toJSON(), requestId);
140
+ return;
141
+ }
142
+ let body;
143
+ try {
144
+ body = await readBody(req);
145
+ }
146
+ catch {
147
+ const problem = badRequest(instance, requestId, 'The request body could not be read.');
148
+ send(res, problem.status, problem.toJSON(), requestId);
149
+ return;
150
+ }
151
+ // T2. Before routing, before validation: a request that names an organization
152
+ // is not a malformed request, it is an attempt to act as another tenant.
153
+ const override = rejectTenantOverride(body, url.searchParams, req.headers, instance, requestId);
154
+ if (override !== undefined) {
155
+ await options.audit.write({
156
+ orgId: auth.orgId,
157
+ actor: `${auth.principal.type}:${auth.principal.id}`,
158
+ action: 'tenant_override_attempt',
159
+ result: 'deny',
160
+ detail: { path: instance },
161
+ requestId,
162
+ });
163
+ send(res, override.status, override.toJSON(), requestId);
164
+ return;
165
+ }
166
+ try {
167
+ if (req.method === 'POST' && instance === '/v1/search') {
168
+ const query = body?.query;
169
+ const topK = body?.top_k;
170
+ if (typeof query !== 'string' || query.length === 0) {
171
+ const problem = badRequest(instance, requestId, "'query' is required.");
172
+ send(res, problem.status, problem.toJSON(), requestId);
173
+ return;
174
+ }
175
+ const results = await options.search.search(auth, query, typeof topK === 'number' ? topK : 10);
176
+ await options.audit.write({
177
+ orgId: auth.orgId,
178
+ actor: `${auth.principal.type}:${auth.principal.id}`,
179
+ action: 'search',
180
+ result: 'allow',
181
+ detail: { returned: results.length },
182
+ requestId,
183
+ });
184
+ send(res, 200, { items: results }, requestId);
185
+ return;
186
+ }
187
+ if (req.method === 'POST' && instance === '/v1/documents') {
188
+ const body_ = (body ?? {});
189
+ const layer = body_.layer;
190
+ const externalId = body_.external_id;
191
+ const content = body_.content;
192
+ const url_ = body_.url;
193
+ if (typeof layer !== 'string' || typeof externalId !== 'string') {
194
+ const problem = badRequest(instance, requestId, "'layer' and 'external_id' are required.");
195
+ send(res, problem.status, problem.toJSON(), requestId);
196
+ return;
197
+ }
198
+ if ((typeof content === 'string') === (typeof url_ === 'string')) {
199
+ // Accepting both would mean choosing silently, and the choice would
200
+ // differ from whatever the caller assumed.
201
+ const problem = badRequest(instance, requestId, "Exactly one of 'content' or 'url' is required.");
202
+ send(res, problem.status, problem.toJSON(), requestId);
203
+ return;
204
+ }
205
+ const outcome = await options.ingest.queue(auth, {
206
+ layer,
207
+ externalId,
208
+ ...(typeof body_.title === 'string' ? { title: body_.title } : {}),
209
+ ...(typeof content === 'string' ? { content } : {}),
210
+ ...(typeof url_ === 'string' ? { url: url_ } : {}),
211
+ });
212
+ if (outcome === undefined) {
213
+ // Not 403. A caller without write access must not learn which layers
214
+ // exist by seeing which ones refuse differently from which ones are
215
+ // absent.
216
+ await options.audit.write({
217
+ orgId: auth.orgId,
218
+ actor: `${auth.principal.type}:${auth.principal.id}`,
219
+ action: 'ingest',
220
+ result: 'deny',
221
+ detail: { layer },
222
+ requestId,
223
+ });
224
+ const problem = notFound(instance, requestId);
225
+ send(res, problem.status, problem.toJSON(), requestId);
226
+ return;
227
+ }
228
+ await options.audit.write({
229
+ orgId: auth.orgId,
230
+ actor: `${auth.principal.type}:${auth.principal.id}`,
231
+ action: 'ingest',
232
+ result: 'allow',
233
+ detail: { document_id: outcome.documentId, unchanged: outcome.unchanged },
234
+ requestId,
235
+ });
236
+ // 200 for an idempotent repeat, 202 for work actually queued. The
237
+ // difference is what lets a client tell "already indexed" from "wait for
238
+ // the job" without polling to find out.
239
+ send(res, outcome.unchanged ? 200 : 202, { document_id: outcome.documentId, job_id: outcome.jobId, status: outcome.unchanged ? 'indexed' : 'queued' }, requestId);
240
+ return;
241
+ }
242
+ const documentMatch = /^\/v1\/documents\/([^/]+)$/.exec(instance);
243
+ if (req.method === 'DELETE' && documentMatch) {
244
+ const id = decodeURIComponent(documentMatch[1]);
245
+ const removed = await options.ingest.remove(auth, id);
246
+ await options.audit.write({
247
+ orgId: auth.orgId,
248
+ actor: `${auth.principal.type}:${auth.principal.id}`,
249
+ action: 'delete_document',
250
+ result: removed ? 'allow' : 'deny',
251
+ detail: { document_id: id },
252
+ requestId,
253
+ });
254
+ if (!removed) {
255
+ const problem = notFound(instance, requestId);
256
+ send(res, problem.status, problem.toJSON(), requestId);
257
+ return;
258
+ }
259
+ res.writeHead(204, { 'x-request-id': requestId });
260
+ res.end();
261
+ return;
262
+ }
263
+ if (req.method === 'GET' && documentMatch) {
264
+ const id = decodeURIComponent(documentMatch[1]);
265
+ const document = await options.documents.read(auth, id);
266
+ if (document === undefined) {
267
+ // T8. The same response for "no such document" and "another
268
+ // organization's document". Same status, same body, one code path — two
269
+ // call sites with two messages is how the oracle comes back.
270
+ await options.audit.write({
271
+ orgId: auth.orgId,
272
+ actor: `${auth.principal.type}:${auth.principal.id}`,
273
+ action: 'get_document',
274
+ result: 'deny',
275
+ detail: { document_id: id },
276
+ requestId,
277
+ });
278
+ const problem = notFound(instance, requestId);
279
+ send(res, problem.status, problem.toJSON(), requestId);
280
+ return;
281
+ }
282
+ await options.audit.write({
283
+ orgId: auth.orgId,
284
+ actor: `${auth.principal.type}:${auth.principal.id}`,
285
+ action: 'get_document',
286
+ result: 'allow',
287
+ detail: { document_id: id },
288
+ requestId,
289
+ });
290
+ send(res, 200, document, requestId);
291
+ return;
292
+ }
293
+ const jobMatch = /^\/v1\/jobs\/([^/]+)$/.exec(instance);
294
+ if (req.method === 'GET' && jobMatch && options.jobs !== undefined) {
295
+ const id = decodeURIComponent(jobMatch[1]);
296
+ const job = await options.jobs.read(auth, id);
297
+ if (job === undefined) {
298
+ // A job names a document, so it is as much of an oracle as the document
299
+ // is. Absent and another organization's answer identically.
300
+ const problem = notFound(instance, requestId);
301
+ send(res, problem.status, problem.toJSON(), requestId);
302
+ return;
303
+ }
304
+ send(res, 200, {
305
+ job_id: job.jobId,
306
+ document_id: job.documentId,
307
+ status: job.status,
308
+ progress: job.progress,
309
+ ...(job.error === undefined ? {} : { error: job.error }),
310
+ }, requestId);
311
+ return;
312
+ }
313
+ if (req.method === 'GET' && instance === '/v1/layers' && options.layers !== undefined) {
314
+ const layers = await options.layers.list(auth);
315
+ // No audit event: this returns what the caller may already read, and one
316
+ // event per listing buries the ones that matter.
317
+ send(res, 200, {
318
+ items: layers.map((l) => ({
319
+ id: l.id,
320
+ slug: l.slug,
321
+ name: l.name,
322
+ workspace_id: l.workspaceId,
323
+ description: l.description,
324
+ document_count: l.documentCount,
325
+ })),
326
+ }, requestId);
327
+ return;
328
+ }
329
+ if (req.method === 'POST' && instance === '/v1/layers' && options.layers !== undefined) {
330
+ const body_ = (body ?? {});
331
+ const workspaceId = body_.workspace_id;
332
+ const slug = body_.slug;
333
+ const name = body_.name;
334
+ if (typeof workspaceId !== 'string' || typeof slug !== 'string' || typeof name !== 'string') {
335
+ const problem = badRequest(instance, requestId, "'workspace_id', 'slug' and 'name' are required.");
336
+ send(res, problem.status, problem.toJSON(), requestId);
337
+ return;
338
+ }
339
+ const outcome = await options.layers.create(auth, { workspaceId, slug, name });
340
+ await options.audit.write({
341
+ orgId: auth.orgId,
342
+ actor: `${auth.principal.type}:${auth.principal.id}`,
343
+ action: 'create_layer',
344
+ result: outcome.kind === 'created' ? 'allow' : 'deny',
345
+ detail: { workspace_id: workspaceId, slug, outcome: outcome.kind },
346
+ requestId,
347
+ });
348
+ if (outcome.kind === 'denied') {
349
+ // 404, not 403. A caller who may not administer a workspace must not be
350
+ // able to tell it apart from one that does not exist.
351
+ const problem = notFound(instance, requestId);
352
+ send(res, problem.status, problem.toJSON(), requestId);
353
+ return;
354
+ }
355
+ if (outcome.kind === 'conflict') {
356
+ const problem = new Problem({
357
+ type: 'https://nacre.work/errors/conflict',
358
+ title: 'Conflict',
359
+ status: 409,
360
+ detail: `A layer with the slug '${slug}' already exists in this organization.`,
361
+ instance,
362
+ requestId,
363
+ });
364
+ send(res, problem.status, problem.toJSON(), requestId);
365
+ return;
366
+ }
367
+ const created = outcome.layer;
368
+ send(res, 201, {
369
+ id: created.id,
370
+ slug: created.slug,
371
+ name: created.name,
372
+ workspace_id: created.workspaceId,
373
+ description: created.description,
374
+ document_count: created.documentCount,
375
+ }, requestId);
376
+ return;
377
+ }
378
+ if (req.method === 'GET' && instance === '/v1/grants' && options.grants !== undefined) {
379
+ const grants = await options.grants.list(auth);
380
+ send(res, 200, { items: grants.map(grantJson) }, requestId);
381
+ return;
382
+ }
383
+ if (req.method === 'POST' && instance === '/v1/grants' && options.grants !== undefined) {
384
+ const body_ = (body ?? {});
385
+ const parsed = parseGrant(body_);
386
+ if (typeof parsed === 'string') {
387
+ const problem = badRequest(instance, requestId, parsed);
388
+ send(res, problem.status, problem.toJSON(), requestId);
389
+ return;
390
+ }
391
+ const issued = await options.grants.issue(auth, parsed);
392
+ await options.audit.write({
393
+ orgId: auth.orgId,
394
+ actor: `${auth.principal.type}:${auth.principal.id}`,
395
+ action: 'issue_grant',
396
+ result: issued === undefined ? 'deny' : 'allow',
397
+ detail: {
398
+ principal: `${parsed.principalType}:${parsed.principalId}`,
399
+ scope: `${parsed.scopeType}:${parsed.scopeId}`,
400
+ permission: parsed.permission,
401
+ },
402
+ requestId,
403
+ });
404
+ if (issued === undefined) {
405
+ const problem = notFound(instance, requestId);
406
+ send(res, problem.status, problem.toJSON(), requestId);
407
+ return;
408
+ }
409
+ send(res, 201, grantJson(issued), requestId);
410
+ return;
411
+ }
412
+ const grantMatch = /^\/v1\/grants\/([^/]+)$/.exec(instance);
413
+ if (req.method === 'DELETE' && grantMatch && options.grants !== undefined) {
414
+ const id = decodeURIComponent(grantMatch[1]);
415
+ const revoked = await options.grants.revoke(auth, id);
416
+ // Written before the response either way. A revocation nobody can prove
417
+ // happened is not a revocation an auditor will accept, and a *refused*
418
+ // one is what an attempt to revoke someone else's grant looks like.
419
+ await options.audit.write({
420
+ orgId: auth.orgId,
421
+ actor: `${auth.principal.type}:${auth.principal.id}`,
422
+ action: 'revoke_grant',
423
+ result: revoked ? 'allow' : 'deny',
424
+ detail: { grant_id: id },
425
+ requestId,
426
+ });
427
+ if (!revoked) {
428
+ // 404 for absent, for another organization's, and for one whose scope
429
+ // this caller cannot administer. Distinguishing them would let an
430
+ // administrator of one layer enumerate grants across the organization.
431
+ const problem = notFound(instance, requestId);
432
+ send(res, problem.status, problem.toJSON(), requestId);
433
+ return;
434
+ }
435
+ send(res, 204, null, requestId);
436
+ return;
437
+ }
438
+ if (instance === '/v1/service-accounts' && options.serviceAccounts !== undefined) {
439
+ // org_admin, not "admin on some scope". A service account is a principal
440
+ // in the organization rather than an object inside a workspace, and there
441
+ // is no scope to check it against — someone holding admin on one layer
442
+ // must not be able to mint credentials.
443
+ if (auth.role !== 'org_admin') {
444
+ const problem = notFound(instance, requestId);
445
+ send(res, problem.status, problem.toJSON(), requestId);
446
+ return;
447
+ }
448
+ if (req.method === 'GET') {
449
+ send(res, 200, { items: (await options.serviceAccounts.list(auth)).map(accountJson) }, requestId);
450
+ return;
451
+ }
452
+ if (req.method === 'POST') {
453
+ const name = (body ?? {}).name;
454
+ if (typeof name !== 'string' || name.trim().length === 0) {
455
+ const problem = badRequest(instance, requestId, "'name' is required.");
456
+ send(res, problem.status, problem.toJSON(), requestId);
457
+ return;
458
+ }
459
+ const { account, key } = await options.serviceAccounts.create(auth, name.trim());
460
+ await options.audit.write({
461
+ orgId: auth.orgId,
462
+ actor: `${auth.principal.type}:${auth.principal.id}`,
463
+ action: 'create_service_account',
464
+ result: 'allow',
465
+ // The prefix, never the key. This row is readable by anyone with the
466
+ // audit log, and the key is not recoverable from anywhere else by
467
+ // design — putting it here would undo that.
468
+ detail: { service_account_id: account.id, key_prefix: account.keyPrefix },
469
+ requestId,
470
+ });
471
+ // The only time the key exists outside the caller's process.
472
+ send(res, 201, { ...accountJson(account), key }, requestId);
473
+ return;
474
+ }
475
+ }
476
+ const accountMatch = /^\/v1\/service-accounts\/([^/]+)$/.exec(instance);
477
+ if (req.method === 'DELETE' && accountMatch && options.serviceAccounts !== undefined) {
478
+ if (auth.role !== 'org_admin') {
479
+ const problem = notFound(instance, requestId);
480
+ send(res, problem.status, problem.toJSON(), requestId);
481
+ return;
482
+ }
483
+ const id = decodeURIComponent(accountMatch[1]);
484
+ const revoked = await options.serviceAccounts.revoke(auth, id);
485
+ await options.audit.write({
486
+ orgId: auth.orgId,
487
+ actor: `${auth.principal.type}:${auth.principal.id}`,
488
+ action: 'revoke_service_account',
489
+ result: revoked ? 'allow' : 'deny',
490
+ detail: { service_account_id: id },
491
+ requestId,
492
+ });
493
+ if (!revoked) {
494
+ const problem = notFound(instance, requestId);
495
+ send(res, problem.status, problem.toJSON(), requestId);
496
+ return;
497
+ }
498
+ send(res, 204, null, requestId);
499
+ return;
500
+ }
501
+ const problem = notFound(instance, requestId);
502
+ send(res, problem.status, problem.toJSON(), requestId);
503
+ }
504
+ catch (error) {
505
+ // The 500 body says "whatever went wrong is in the journal under this
506
+ // request_id", and until this line nothing wrote it there: the error was
507
+ // discarded by a bare `catch`, and the audit row carried an empty detail.
508
+ // A caller reporting a 500 with a request id had nothing to be joined to.
509
+ //
510
+ // The message, not the request. A handler failure can carry a query string
511
+ // or a document body in its cause, and neither belongs in a log — see the
512
+ // list in CLAUDE.md. `String(error)` is the class and the message; the
513
+ // stack goes with it because that is what names the line.
514
+ console.error(JSON.stringify({
515
+ msg: 'request failed',
516
+ request_id: requestId,
517
+ method: req.method,
518
+ instance,
519
+ org_id: auth.orgId,
520
+ error: String(error).slice(0, 500),
521
+ stack: error instanceof Error ? error.stack?.split('\n').slice(0, 6).join('\n') : undefined,
522
+ }));
523
+ await options.audit
524
+ .write({
525
+ orgId: auth.orgId,
526
+ actor: `${auth.principal.type}:${auth.principal.id}`,
527
+ action: instance,
528
+ result: 'error',
529
+ // The class alone. An audit row is read by more people than a log line
530
+ // and is retained far longer, so it says what kind of failure it was
531
+ // and points at the log for the rest.
532
+ detail: { error: error instanceof Error ? error.name : 'unknown' },
533
+ requestId,
534
+ })
535
+ .catch((cause) => {
536
+ // Losing the audit row of a failed request is itself worth a line.
537
+ console.error(JSON.stringify({ msg: 'audit write failed', request_id: requestId, error: String(cause) }));
538
+ });
539
+ const problem = internal(instance, requestId);
540
+ send(res, problem.status, problem.toJSON(), requestId);
541
+ }
542
+ }
543
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,82 @@
1
+ import type { Pool } from 'pg';
2
+ import type { AuthContext } from './auth.js';
3
+ /**
4
+ * Service account keys.
5
+ *
6
+ * The one credential in the system meant to outlive a session. `init` issues a
7
+ * token that expires in an hour, which is right for getting through the
8
+ * quickstart and useless for an agent that runs for a week — until this
9
+ * existed, `NACRE_SERVICE_KEY` had nothing to put in it.
10
+ *
11
+ * The shape is not invented here: migration 0001 gave `service_accounts` a
12
+ * `key_hash`, a `key_prefix`, and a `revoked_at`, which is an opaque key stored
13
+ * hashed and looked up by a non-secret prefix. This implements that.
14
+ *
15
+ * A key is shown once, at creation, and never again. Storing anything
16
+ * reversible would make a database backup a set of live credentials, and the
17
+ * whole point of `key_hash` is that a stolen dump is not one.
18
+ */
19
+ /** Identifies the credential type on sight, in a log or a pasted variable. */
20
+ export declare const KEY_PREFIX = "nacre_sk_";
21
+ /**
22
+ * The lookup prefix: the marker plus the first 8 characters of the secret.
23
+ *
24
+ * Long enough that the indexed lookup returns one row in practice, short enough
25
+ * that it is not a meaningful head start — 8 base64url characters is 48 bits,
26
+ * against a 256-bit secret. It is stored in clear and shown in listings on
27
+ * purpose: an operator revoking a key needs to tell two apart, and the whole
28
+ * key is gone.
29
+ */
30
+ export declare function prefixOf(key: string): string;
31
+ export declare function hashOf(key: string): string;
32
+ /** 32 bytes from the CSPRNG. Not a UUID — those are not secrets. */
33
+ export declare function generateKey(): string;
34
+ export declare const looksLikeServiceKey: (value: string) => boolean;
35
+ export interface ServiceKeyResolver {
36
+ /** The caller this key belongs to, or undefined for every kind of failure. */
37
+ resolve(key: string): Promise<AuthContext | undefined>;
38
+ }
39
+ /**
40
+ * Resolve a key against the database.
41
+ *
42
+ * Undefined for unknown, revoked, malformed and wrong alike — the caller turns
43
+ * all of them into the same 401 a bad JWT gets, so which one it was cannot be
44
+ * read off the response.
45
+ */
46
+ export declare class PostgresServiceKeys implements ServiceKeyResolver {
47
+ private readonly pool;
48
+ private readonly role?;
49
+ constructor(pool: Pool, role?: string | undefined);
50
+ resolve(key: string): Promise<AuthContext | undefined>;
51
+ }
52
+ export interface ServiceAccount {
53
+ readonly id: string;
54
+ readonly name: string;
55
+ readonly keyPrefix: string;
56
+ readonly createdAt: string;
57
+ readonly lastUsedAt: string | null;
58
+ readonly revokedAt: string | null;
59
+ }
60
+ export interface ServiceAccounts {
61
+ list(auth: AuthContext): Promise<readonly ServiceAccount[]>;
62
+ /** The key is in the result and nowhere else, ever again. */
63
+ create(auth: AuthContext, name: string): Promise<{
64
+ account: ServiceAccount;
65
+ key: string;
66
+ }>;
67
+ /** False when it does not exist in this organization, or is already revoked. */
68
+ revoke(auth: AuthContext, id: string): Promise<boolean>;
69
+ }
70
+ export declare class PostgresServiceAccounts implements ServiceAccounts {
71
+ private readonly pool;
72
+ private readonly role?;
73
+ constructor(pool: Pool, role?: string | undefined);
74
+ private get scope();
75
+ list(auth: AuthContext): Promise<readonly ServiceAccount[]>;
76
+ create(auth: AuthContext, name: string): Promise<{
77
+ account: ServiceAccount;
78
+ key: string;
79
+ }>;
80
+ revoke(auth: AuthContext, id: string): Promise<boolean>;
81
+ }
82
+ //# sourceMappingURL=service-keys.d.ts.map