@bongos/core 1.19.337 → 1.19.339

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.
@@ -61,6 +61,8 @@ const express = require('express');
61
61
  const api = require('../../../src/module-api');
62
62
  const auth = api;
63
63
  const memory = require('../memory');
64
+ // task 1003208: structured logging (pino via the doorway) — was console.*.
65
+ const log = api.logger('memory');
64
66
  const db = api;
65
67
 
66
68
  // Per-builder rate limit for POST /memory/sync, layered ON TOP of the global
@@ -357,7 +359,7 @@ module.exports = function buildMemoryRouter() {
357
359
  } catch (e) {
358
360
  // A throw here means the transaction itself failed (BEGIN/COMMIT/connect),
359
361
  // not a per-file rejection — those come back inside the results array.
360
- console.error('[gds] POST /memory/sync batch transaction error', { builderId, err: e });
362
+ log.error('[gds] POST /memory/sync batch transaction error', { builderId, err: e });
361
363
  return res.fail('store_failed', { status: 500, message: 'memory sync failed; nothing was stored' });
362
364
  }
363
365
 
@@ -397,9 +399,9 @@ module.exports = function buildMemoryRouter() {
397
399
  hint = 'Prune old memory files (the limit is per-builder, summed across all files).';
398
400
  } else if (code !== 'BAD_PATH' && code !== 'STORE_FAILED') {
399
401
  // Unknown helper error code — log for forensics, still report per-file.
400
- console.error('[gds] POST /memory/sync store error', { builderId, path, code, err: e });
402
+ log.error('[gds] POST /memory/sync store error', { builderId, path, code, err: e });
401
403
  } else if (code === 'STORE_FAILED') {
402
- console.error('[gds] POST /memory/sync unexpected error', { builderId, path, err: e });
404
+ log.error('[gds] POST /memory/sync unexpected error', { builderId, path, err: e });
403
405
  }
404
406
  const err = { path, code, message, ...(hint ? { hint } : {}) };
405
407
  errors.push(err);
@@ -452,7 +454,7 @@ module.exports = function buildMemoryRouter() {
452
454
  })),
453
455
  });
454
456
  } catch (err) {
455
- console.error('[gds] GET /memory/files', { builderId, err });
457
+ log.error('[gds] GET /memory/files', { builderId, err });
456
458
  return res.fail('list_failed', { status: 500, message: 'could not list memory files' });
457
459
  }
458
460
  });
@@ -504,7 +506,7 @@ module.exports = function buildMemoryRouter() {
504
506
  created_at: row.created_at,
505
507
  });
506
508
  } catch (err) {
507
- console.error('[gds] GET /memory/file', { builderId, path, err });
509
+ log.error('[gds] GET /memory/file', { builderId, path, err });
508
510
  return res.fail('fetch_failed', { status: 500, message: 'could not fetch memory file' });
509
511
  }
510
512
  });
@@ -537,7 +539,7 @@ module.exports = function buildMemoryRouter() {
537
539
  if (n === 0) return res.fail('not_found', 404, { path });
538
540
  return res.json({ deleted: true, builder_id: builderId, path });
539
541
  } catch (err) {
540
- console.error('[gds] DELETE /memory/file', { builderId, path, err });
542
+ log.error('[gds] DELETE /memory/file', { builderId, path, err });
541
543
  return res.fail('delete_failed', { status: 500, message: 'could not delete memory file' });
542
544
  }
543
545
  });
@@ -568,7 +570,7 @@ module.exports = function buildMemoryRouter() {
568
570
  try {
569
571
  rows = await memory.listMemoryFiles(targetId);
570
572
  } catch (err) {
571
- console.error('[gds] GET /memory/builders/:builderId/files', { targetId, err });
573
+ log.error('[gds] GET /memory/builders/:builderId/files', { targetId, err });
572
574
  return res.fail('list_failed', { status: 500, message: 'could not list memory files' });
573
575
  }
574
576
  // FAIL CLOSED: the audit row must land durably BEFORE we return any of the
@@ -579,7 +581,7 @@ module.exports = function buildMemoryRouter() {
579
581
  try {
580
582
  await recordForensicRead(req, { route, targetBuilderId: targetId, target: 'list', responseStatus: 200 });
581
583
  } catch (auditErr) {
582
- console.error('[gds] forensic-read audit insert failed (read withheld)', {
584
+ log.error('[gds] forensic-read audit insert failed (read withheld)', {
583
585
  route, targetId, err: auditErr && auditErr.message ? auditErr.message : String(auditErr),
584
586
  });
585
587
  return res.fail('audit_failed', { status: 500, message: 'forensic read not served: audit could not be recorded' });
@@ -628,7 +630,7 @@ module.exports = function buildMemoryRouter() {
628
630
  try {
629
631
  row = await memory.fetchMemoryFile(targetId, path);
630
632
  } catch (err) {
631
- console.error('[gds] GET /memory/builders/:builderId/file', { targetId, path, err });
633
+ log.error('[gds] GET /memory/builders/:builderId/file', { targetId, path, err });
632
634
  return res.fail('fetch_failed', { status: 500, message: 'could not fetch memory file' });
633
635
  }
634
636
  // FAIL CLOSED: audit the look — logged even if the path is a 404, because
@@ -640,7 +642,7 @@ module.exports = function buildMemoryRouter() {
640
642
  try {
641
643
  await recordForensicRead(req, { route, targetBuilderId: targetId, target: path, responseStatus: outcomeStatus });
642
644
  } catch (auditErr) {
643
- console.error('[gds] forensic-read audit insert failed (read withheld)', {
645
+ log.error('[gds] forensic-read audit insert failed (read withheld)', {
644
646
  route, targetId, path, err: auditErr && auditErr.message ? auditErr.message : String(auditErr),
645
647
  });
646
648
  return res.fail('audit_failed', { status: 500, message: 'forensic read not served: audit could not be recorded' });
@@ -687,7 +689,7 @@ module.exports = function buildMemoryRouter() {
687
689
  try {
688
690
  rows = await memory.listMemoryFiles(targetId);
689
691
  } catch (err) {
690
- console.error('[gds] GET /memory/bfg/builders/:builderId/files', { targetId, err });
692
+ log.error('[gds] GET /memory/bfg/builders/:builderId/files', { targetId, err });
691
693
  return res.fail('list_failed', { status: 500, message: 'could not list memory files' });
692
694
  }
693
695
  // FAIL CLOSED: the audit row must land before any of the target's data is
@@ -695,7 +697,7 @@ module.exports = function buildMemoryRouter() {
695
697
  try {
696
698
  await recordForensicRead(req, { route, targetBuilderId: targetId, target: 'list', responseStatus: 200, action: 'bfg_memory_read' });
697
699
  } catch (auditErr) {
698
- console.error('[gds] bfg-read audit insert failed (read withheld)', { route, targetId, err: auditErr && auditErr.message ? auditErr.message : String(auditErr) });
700
+ log.error('[gds] bfg-read audit insert failed (read withheld)', { route, targetId, err: auditErr && auditErr.message ? auditErr.message : String(auditErr) });
699
701
  return res.fail('audit_failed', { status: 500, message: 'read not served: audit could not be recorded' });
700
702
  }
701
703
  res.set('Cache-Control', 'no-store');
@@ -731,14 +733,14 @@ module.exports = function buildMemoryRouter() {
731
733
  try {
732
734
  row = await memory.fetchMemoryFile(targetId, path);
733
735
  } catch (err) {
734
- console.error('[gds] GET /memory/bfg/builders/:builderId/file', { targetId, path, err });
736
+ log.error('[gds] GET /memory/bfg/builders/:builderId/file', { targetId, path, err });
735
737
  return res.fail('fetch_failed', { status: 500, message: 'could not fetch memory file' });
736
738
  }
737
739
  const outcomeStatus = row ? 200 : 404;
738
740
  try {
739
741
  await recordForensicRead(req, { route, targetBuilderId: targetId, target: path, responseStatus: outcomeStatus, action: 'bfg_memory_read' });
740
742
  } catch (auditErr) {
741
- console.error('[gds] bfg-read audit insert failed (read withheld)', { route, targetId, path, err: auditErr && auditErr.message ? auditErr.message : String(auditErr) });
743
+ log.error('[gds] bfg-read audit insert failed (read withheld)', { route, targetId, path, err: auditErr && auditErr.message ? auditErr.message : String(auditErr) });
742
744
  return res.fail('audit_failed', { status: 500, message: 'read not served: audit could not be recorded' });
743
745
  }
744
746
  if (!row) return res.fail('not_found', 404, { builder_id: targetId, path });
@@ -830,7 +832,7 @@ module.exports = function buildMemoryRouter() {
830
832
  try {
831
833
  target = await db.getBuilderById(targetId);
832
834
  } catch (err) {
833
- console.error('[gds] bfg-write target lookup', { targetId, err });
835
+ log.error('[gds] bfg-write target lookup', { targetId, err });
834
836
  return res.fail('lookup_failed', 500);
835
837
  }
836
838
  if (!target) return res.fail('target_not_found', 404);
@@ -893,7 +895,7 @@ module.exports = function buildMemoryRouter() {
893
895
  // own assertBfgPath guard mapped to a clean 400 for any direct caller.
894
896
  return res.fail('bad_path', 400, { code });
895
897
  }
896
- console.error('[gds] POST /memory/builders/:builderId/bfg-write', e);
898
+ log.error('[gds] POST /memory/builders/:builderId/bfg-write', e);
897
899
  return res.fail('bfg_write_failed', 500);
898
900
  }
899
901
  }
@@ -19,6 +19,8 @@ const api = require('../../../src/module-api');
19
19
  const pi = require('../platform-identity');
20
20
  const hubKeys = require('../hub-keys');
21
21
  const hubDevice = require('../hub-device');
22
+ // task 1003208: structured logging (pino via the doorway) — was console.*.
23
+ const log = api.logger('platform-identity');
22
24
 
23
25
  const { pool, validateOrRespond, resolveSession } = api;
24
26
 
@@ -65,12 +67,12 @@ async function notifyInstanceLogout(membership, githubId) {
65
67
  body: JSON.stringify({ logout_token: token }),
66
68
  signal: ac.signal,
67
69
  });
68
- if (!resp.ok) console.warn(`[platform-identity] SLO: ${origin} returned ${resp.status}`);
70
+ if (!resp.ok) log.warn(`[platform-identity] SLO: ${origin} returned ${resp.status}`);
69
71
  } finally {
70
72
  clearTimeout(timer);
71
73
  }
72
74
  } catch (err) {
73
- console.warn(`[platform-identity] SLO: notifying ${origin} failed (non-blocking):`, err && err.message ? err.message : err);
75
+ log.warn(`[platform-identity] SLO: notifying ${origin} failed (non-blocking):`, err && err.message ? err.message : err);
74
76
  }
75
77
  }
76
78
 
@@ -90,7 +92,7 @@ async function fanOutBackchannelLogout(githubId) {
90
92
  const targets = (memberships || []).filter((m) => m.project_origin && m.client_id);
91
93
  await Promise.allSettled(targets.map((m) => notifyInstanceLogout(m, githubId)));
92
94
  } catch (err) {
93
- console.error('[platform-identity] SLO fan-out failed (non-blocking):', err && err.message ? err.message : err);
95
+ log.error('[platform-identity] SLO fan-out failed (non-blocking):', err && err.message ? err.message : err);
94
96
  }
95
97
  }
96
98
 
@@ -149,7 +151,7 @@ module.exports = function ssoRoutes() {
149
151
  try {
150
152
  client = await pi.getClient(clientId, { pool });
151
153
  } catch (err) {
152
- console.error('[platform-identity] /sso/authorize getClient', err);
154
+ log.error('[platform-identity] /sso/authorize getClient', err);
153
155
  return res.status(500).type('text/html').send(errorPage('Internal error resolving the project.'));
154
156
  }
155
157
  if (!client || client.status !== 'active') {
@@ -171,7 +173,7 @@ module.exports = function ssoRoutes() {
171
173
  try {
172
174
  session = await resolveSession(req);
173
175
  } catch (err) {
174
- console.error('[platform-identity] /sso/authorize resolveSession', err);
176
+ log.error('[platform-identity] /sso/authorize resolveSession', err);
175
177
  return res.status(500).type('text/html').send(errorPage('Internal error checking your hub session.'));
176
178
  }
177
179
  const usable = pi.sessionUsableForFederation(session);
@@ -213,7 +215,7 @@ module.exports = function ssoRoutes() {
213
215
  if (state) u.searchParams.set('state', state);
214
216
  return res.redirect(302, u.toString());
215
217
  } catch (err) {
216
- console.error('[platform-identity] /sso/authorize mint', err);
218
+ log.error('[platform-identity] /sso/authorize mint', err);
217
219
  return res.status(500).type('text/html').send(errorPage('Could not complete the sign-in handoff.'));
218
220
  }
219
221
  });
@@ -261,7 +263,7 @@ module.exports = function ssoRoutes() {
261
263
  avatar_url: identity.avatar_url,
262
264
  });
263
265
  } catch (err) {
264
- console.error('[platform-identity] POST /sso/token', err);
266
+ log.error('[platform-identity] POST /sso/token', err);
265
267
  return res.fail('token_exchange_failed', { status: 500, message: 'internal error' });
266
268
  }
267
269
  });
@@ -302,7 +304,7 @@ module.exports = function ssoRoutes() {
302
304
  const out = await hubDevice.startHubGithubDevice(client.client_id);
303
305
  res.json(out);
304
306
  } catch (err) {
305
- console.error('[platform-identity] /sso/device/start', err);
307
+ log.error('[platform-identity] /sso/device/start', err);
306
308
  // 424, not 502: Cloudflare replaces origin 5xx bodies with its own page, so a
307
309
  // proxied upstream refusal must ride a 4xx to keep its JSON diagnosis readable
308
310
  // externally (finding W2, task 1003052 — the core device/start twin).
@@ -345,7 +347,7 @@ module.exports = function ssoRoutes() {
345
347
  });
346
348
  res.json({ state: 'ok', assertion });
347
349
  } catch (err) {
348
- console.error('[platform-identity] /sso/device/poll', err);
350
+ log.error('[platform-identity] /sso/device/poll', err);
349
351
  // 424, matching the core device/poll (finding W2, task 1003052): this catch
350
352
  // proxies the hub's GitHub poll refusals, and a 5xx body never survives
351
353
  // Cloudflare. (The !out.client_id branch above keeps its 500: an internal
@@ -399,7 +401,7 @@ module.exports = function ssoRoutes() {
399
401
  await pi.upsertMembership({ clientId: client_id, githubId: github_id, membershipKind }, { pool });
400
402
  res.json({ ok: true });
401
403
  } catch (err) {
402
- console.error('[platform-identity] /sso/membership/check-in', err);
404
+ log.error('[platform-identity] /sso/membership/check-in', err);
403
405
  res.fail('checkin_failed', { status: 500, message: 'internal error' });
404
406
  }
405
407
  });
@@ -453,7 +455,7 @@ module.exports = function ssoRoutes() {
453
455
  }, { pool });
454
456
  res.json({ ok: true });
455
457
  } catch (err) {
456
- console.error('[platform-identity] /sso/activity/rollup', err);
458
+ log.error('[platform-identity] /sso/activity/rollup', err);
457
459
  res.fail('rollup_failed', { status: 500, message: 'internal error' });
458
460
  }
459
461
  });
@@ -34,6 +34,8 @@ const { pool, validateOrRespond, parseId } = api;
34
34
  const provisioning = require('../provisioning');
35
35
  const { onboardPlan, stepsForShape } = require('../onboard-plan');
36
36
  const { makeAskRateLimiter, clientIp } = require('../rate-limit');
37
+ // task 1003208: structured logging (pino via the doorway) — was console.*.
38
+ const log = api.logger('provisioning');
37
39
 
38
40
  // How long a GitHub-App-Manifest `state` token is valid — the owner must click the
39
41
  // pre-filled link + approve on GitHub within this window (task 2080). Comfortably
@@ -279,7 +281,7 @@ module.exports = function provisioningRoutes() {
279
281
  const rows = await provisioning.listAllInstances(pool, { status });
280
282
  res.json({ instances: rows.map(fleetInstance) });
281
283
  } catch (err) {
282
- console.error('[provisioning] GET /provisioning/fleet', err);
284
+ log.error('[provisioning] GET /provisioning/fleet', err);
283
285
  res.fail('fleet_failed', { status: 500, message: 'internal error' });
284
286
  }
285
287
  });
@@ -294,7 +296,7 @@ module.exports = function provisioningRoutes() {
294
296
  const out = await provisioning.costLedger(pool, { limit });
295
297
  res.json(out);
296
298
  } catch (err) {
297
- console.error('[provisioning] GET /provisioning/fleet/cost-ledger', err);
299
+ log.error('[provisioning] GET /provisioning/fleet/cost-ledger', err);
298
300
  res.fail('cost_ledger_failed', { status: 500, message: 'internal error' });
299
301
  }
300
302
  });
@@ -347,7 +349,7 @@ module.exports = function provisioningRoutes() {
347
349
  res.json({ ok: true, queued: r.created, action: 'teardown', status: inst.status, superseded: r.superseded,
348
350
  message: r.created ? 'The instance will be torn down shortly.' : 'A teardown is already queued for that instance.' });
349
351
  } catch (err) {
350
- console.error('[provisioning] POST /provisioning/instances/:id/force-teardown', err);
352
+ log.error('[provisioning] POST /provisioning/instances/:id/force-teardown', err);
351
353
  res.fail('teardown_failed', { status: 500, message: 'internal error' });
352
354
  }
353
355
  });
@@ -522,7 +524,7 @@ module.exports = function provisioningRoutes() {
522
524
  ...(typeIgnored ? { type_ignored: typeIgnored } : {}),
523
525
  });
524
526
  } catch (err) {
525
- console.error('[provisioning] POST /provisioning/instances', err);
527
+ log.error('[provisioning] POST /provisioning/instances', err);
526
528
  res.fail('request_failed', { status: 500, message: 'internal error' });
527
529
  }
528
530
  });
@@ -570,7 +572,7 @@ module.exports = function provisioningRoutes() {
570
572
  // disclosure this route has no reason to make.
571
573
  res.json({ slug, available: reason === null, reason, address: address || null });
572
574
  } catch (err) {
573
- console.error('[provisioning] GET /provisioning/slug-available', err);
575
+ log.error('[provisioning] GET /provisioning/slug-available', err);
574
576
  res.fail('check_failed', { status: 500, message: 'internal error' });
575
577
  }
576
578
  });
@@ -590,7 +592,7 @@ module.exports = function provisioningRoutes() {
590
592
  }
591
593
  res.json({ instances });
592
594
  } catch (err) {
593
- console.error('[provisioning] GET /provisioning/instances', err);
595
+ log.error('[provisioning] GET /provisioning/instances', err);
594
596
  res.fail('list_failed', { status: 500, message: 'internal error' });
595
597
  }
596
598
  });
@@ -622,7 +624,7 @@ module.exports = function provisioningRoutes() {
622
624
  open_intent: open ? { action: open.action, state: open.state } : null,
623
625
  });
624
626
  } catch (err) {
625
- console.error('[provisioning] GET /provisioning/instances/:id', err);
627
+ log.error('[provisioning] GET /provisioning/instances/:id', err);
626
628
  res.fail('read_failed', { status: 500, message: 'internal error' });
627
629
  }
628
630
  });
@@ -644,7 +646,7 @@ module.exports = function provisioningRoutes() {
644
646
  if (instanceAccessDenied(inst, req.builder)) return res.fail('instance_not_found', 404);
645
647
  res.json({ ok: true, settings: provisioning.effectiveSettings(inst), options: provisioning.SETTINGS_VOCAB });
646
648
  } catch (err) {
647
- console.error('[provisioning] GET /provisioning/instances/:id/settings', err);
649
+ log.error('[provisioning] GET /provisioning/instances/:id/settings', err);
648
650
  res.fail('settings_read_failed', { status: 500, message: 'internal error' });
649
651
  }
650
652
  });
@@ -679,7 +681,7 @@ module.exports = function provisioningRoutes() {
679
681
  }).catch(() => {});
680
682
  res.json({ ok: true, settings: provisioning.effectiveSettings(updated) });
681
683
  } catch (err) {
682
- console.error('[provisioning] PATCH /provisioning/instances/:id/settings', err);
684
+ log.error('[provisioning] PATCH /provisioning/instances/:id/settings', err);
683
685
  res.fail('settings_update_failed', { status: 500, message: 'internal error' });
684
686
  }
685
687
  });
@@ -716,7 +718,7 @@ module.exports = function provisioningRoutes() {
716
718
  res.json({ ok: true, queued: r.created, action: 'teardown', status: inst.status, superseded: r.superseded,
717
719
  message: r.created ? 'Your instance will be torn down shortly.' : 'A teardown is already queued for that instance.' });
718
720
  } catch (err) {
719
- console.error('[provisioning] POST /provisioning/instances/:id/teardown', err);
721
+ log.error('[provisioning] POST /provisioning/instances/:id/teardown', err);
720
722
  res.fail('teardown_failed', { status: 500, message: 'internal error' });
721
723
  }
722
724
  });
@@ -841,7 +843,7 @@ module.exports = function provisioningRoutes() {
841
843
  ? 'Your instance is being wired to its new address — this card will show whether it answers.'
842
844
  : 'Address recorded — it will be wired when the instance is provisioned.' });
843
845
  } catch (err) {
844
- console.error('[provisioning] POST /provisioning/instances/:id/domain', err);
846
+ log.error('[provisioning] POST /provisioning/instances/:id/domain', err);
845
847
  res.fail('domain_attach_failed', { status: 500, message: 'internal error' });
846
848
  }
847
849
  });
@@ -899,7 +901,7 @@ module.exports = function provisioningRoutes() {
899
901
  expires_at: expiresAt,
900
902
  });
901
903
  } catch (err) {
902
- console.error('[provisioning] POST /provisioning/instances/:id/github-app', err);
904
+ log.error('[provisioning] POST /provisioning/instances/:id/github-app', err);
903
905
  res.fail('github_app_manifest_failed', { status: 500, message: 'internal error' });
904
906
  }
905
907
  });
@@ -955,7 +957,7 @@ module.exports = function provisioningRoutes() {
955
957
  const ok = await provisioning.isProvisionedHostname(pool, req.query.domain);
956
958
  return res.status(ok ? 200 : 404).json({ ok });
957
959
  } catch (err) {
958
- console.error('[provisioning] GET /provisioning/tls-check', err);
960
+ log.error('[provisioning] GET /provisioning/tls-check', err);
959
961
  return res.status(503).json({ ok: false, error: 'check_failed' });
960
962
  }
961
963
  });
@@ -989,7 +991,7 @@ module.exports = function provisioningRoutes() {
989
991
  app_slug: row.app_slug || null,
990
992
  });
991
993
  } catch (err) {
992
- console.error('[provisioning] GET /provisioning/github-app/status', err);
994
+ log.error('[provisioning] GET /provisioning/github-app/status', err);
993
995
  return res.status(500).json({ ok: false, error: 'status_check_failed' });
994
996
  }
995
997
  });
@@ -1039,7 +1041,7 @@ module.exports = function provisioningRoutes() {
1039
1041
  : callbackPage({ ok: true, title: 'GitHub app created',
1040
1042
  body: '<p>Nice — GitHub created the app. We’re placing its credentials on your instance now; this finishes on its own within a few minutes.</p><p class="muted">You can close this tab.</p>' }));
1041
1043
  } catch (err) {
1042
- console.error('[provisioning] GET /provisioning/github-app/callback', err);
1044
+ log.error('[provisioning] GET /provisioning/github-app/callback', err);
1043
1045
  return sendPage(500, callbackPage({ ok: false, title: 'Something went wrong',
1044
1046
  body: '<p>We couldn’t record GitHub’s response. Try starting the sign-in setup again from your instance’s page.</p>' }));
1045
1047
  }
package/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.19.337",
3
+ "version": "1.19.339",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@bongos/core",
9
- "version": "1.19.337",
9
+ "version": "1.19.339",
10
10
  "license": "AGPL-3.0-or-later",
11
11
  "dependencies": {
12
12
  "express": "^4.21.2",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.19.337",
3
+ "version": "1.19.339",
4
4
  "description": "Cloud Bongos — the AI-first build platform core (GDS + platform surfaces + module system), installed as a versioned dependency (ADR 0108).",
5
5
  "license": "AGPL-3.0-or-later",
6
6
  "main": "src/platform-server.js",
@@ -488,6 +488,12 @@ const KERNEL_FILES = [
488
488
  'src/bongos/goal-scope-check.js', // goal create-gate scope wall (composes the 3 matchers above; ADR 0086 §3, BV1.R60)
489
489
  'src/bongos/secret-box.js', // AES-256-GCM per-builder secret box
490
490
  'src/bongos/pool.js', // the singleton Postgres pool accessor
491
+ // task 1003208: the structured logger is kernel infrastructure in the same sense
492
+ // the pool is — src/module-api.js hands every module a child of it, so it sits
493
+ // BELOW the module boundary, not beside a domain. It imports only node built-ins
494
+ // and pino, so joining the roster costs nothing and buys the import-direction
495
+ // guarantee on the file every module now logs through.
496
+ 'src/bongos/logger.js', // pino + request correlation + ADR 0022 redaction
491
497
  'src/bongos/db-kernel.js', // the kernel db slice — identity/audit/rank/withTx (BV1.R69)
492
498
  'src/bongos/routes/_helpers.js', // shared request validators the doorway re-exports
493
499
  // config / identity (host context a module reads, never hardcodes)
@@ -29,6 +29,9 @@ const seams = require('../module-seams');
29
29
  const ic = require('../instance-config');
30
30
  const { branding, userAgent } = require('../branding');
31
31
  const { loadIdpConfig } = require('./auth-config.js');
32
+ // task 1003208: structured logging — was console.*. A core file logs through
33
+ // the kernel logger directly; only MODULES go via api.logger (the doorway).
34
+ const log = require('./logger').logger.child({ src: 'auth-admission' });
32
35
 
33
36
  function membershipKind({ githubLogin } = {}) {
34
37
  const fa = firstAdminLogin();
@@ -70,7 +73,7 @@ async function reportMembershipCheckIn({ githubId, githubLogin, displayName, ava
70
73
  }),
71
74
  });
72
75
  } catch (err) {
73
- console.log('[auth] hub membership check-in failed (non-blocking):', err.message);
76
+ log.info('[auth] hub membership check-in failed (non-blocking):', err.message);
74
77
  }
75
78
  }
76
79
 
@@ -107,7 +110,7 @@ async function reportActivityRollup({ builderId, githubId }) {
107
110
  }),
108
111
  });
109
112
  } catch (err) {
110
- console.log('[auth] hub activity rollup failed (non-blocking):', err.message);
113
+ log.info('[auth] hub activity rollup failed (non-blocking):', err.message);
111
114
  }
112
115
  }
113
116
 
@@ -180,7 +183,7 @@ async function webAdmissionStatus(login) {
180
183
  // A DB hiccup must not turn the waiting page into a false "you're in".
181
184
  // Fail closed on `admitted`; still let a firstAdmin keep polling, since
182
185
  // that is the bootstrap window this page exists for.
183
- console.error('[auth] webAdmissionStatus', err);
186
+ log.error('[auth] webAdmissionStatus', err);
184
187
  return { admitted: false, expectedSoon: isFirstAdmin };
185
188
  }
186
189
 
@@ -213,11 +216,11 @@ async function maybeSeatFirstAdmin(builder) {
213
216
  try {
214
217
  const seated = await seatFirstAdminIfUnclaimed(builder.id, builder.github_login);
215
218
  if (seated) {
216
- console.log(`[auth] first-admin bootstrap: seated ${builder.github_login} as Archon (configured firstAdmin, no prior Archon).`);
219
+ log.info(`[auth] first-admin bootstrap: seated ${builder.github_login} as Archon (configured firstAdmin, no prior Archon).`);
217
220
  return { ...builder, rank: 'archon' };
218
221
  }
219
222
  } catch (err) {
220
- console.error('[auth] first-admin bootstrap failed (non-fatal):', err.message);
223
+ log.error('[auth] first-admin bootstrap failed (non-fatal):', err.message);
221
224
  }
222
225
  return builder;
223
226
  }
@@ -246,7 +249,7 @@ async function safeSyncRankRole(builder) {
246
249
  await seams.resolveOptional('government.rankSync')
247
250
  ?.syncRank(null, { builderId: builder.id, rank: builder.rank });
248
251
  } catch (err) {
249
- console.error('[auth] rank-role sync failed (non-fatal):', err.message);
252
+ log.error('[auth] rank-role sync failed (non-fatal):', err.message);
250
253
  }
251
254
  }
252
255
 
@@ -7,6 +7,9 @@ const { pairStore } = require('../app-pair');
7
7
  const { publicOrigin, parseCookie, validateOrRespond } = require('./_helpers');
8
8
  const { branding } = require('../../branding');
9
9
  const seams = require('../../module-seams');
10
+ // task 1003208: structured logging — was console.*. A core file logs through
11
+ // the kernel logger directly; only MODULES go via api.logger (the doorway).
12
+ const log = require('../logger').logger.child({ src: 'auth' });
10
13
 
11
14
  function escapeHtml(s) {
12
15
  return String(s ?? '').replace(/[&<>"']/g, (c) => ({
@@ -358,7 +361,7 @@ module.exports = function buildAuthRouter() {
358
361
  const out = await auth.startDeviceFlow();
359
362
  res.json(out);
360
363
  } catch (err) {
361
- console.error('[gds] device/start', err);
364
+ log.error('[gds] device/start', err);
362
365
  // Surface the underlying reason (task 2126). A thrown device-flow start is almost
363
366
  // always a GitHub OAuth-App misconfig (device_flow_disabled / bad client) — safe,
364
367
  // non-secret, and actionable — so return it instead of an opaque "internal error".
@@ -394,7 +397,7 @@ module.exports = function buildAuthRouter() {
394
397
  if (err && err.code === 'NOT_APPROVED') {
395
398
  return res.status(403).json({ state: 'not_approved', error: 'not_approved' });
396
399
  }
397
- console.error('[gds] device/poll', err);
400
+ log.error('[gds] device/poll', err);
398
401
  // 424 for the same reason as device/start above (finding W2, task 1003052):
399
402
  // this catch proxies GitHub's poll refusals too, and a 5xx body never
400
403
  // survives Cloudflare — the JSON envelope (its code at minimum) must.
@@ -432,7 +435,7 @@ module.exports = function buildAuthRouter() {
432
435
  }
433
436
  // A bad / expired / forged / wrong-audience assertion is an auth failure (401),
434
437
  // not a server error — never mint a session for it.
435
- console.error('[gds] /auth/device/exchange', err);
438
+ log.error('[gds] /auth/device/exchange', err);
436
439
  res.fail('invalid_assertion', { status: 401, message: String(err.message || 'invalid assertion').slice(0, 160) });
437
440
  }
438
441
  });
@@ -536,7 +539,7 @@ module.exports = function buildAuthRouter() {
536
539
  return res.json({ admitted: !!admitted });
537
540
  } catch (err) {
538
541
  // Fail closed and quietly — the page just keeps polling.
539
- console.error('[gds] auth/web/admission-status', err);
542
+ log.error('[gds] auth/web/admission-status', err);
540
543
  return res.json({ admitted: false });
541
544
  }
542
545
  });
@@ -594,7 +597,7 @@ module.exports = function buildAuthRouter() {
594
597
  try {
595
598
  await db.upsertGithubToken({ builderId: builder.id, token: githubToken, scope: githubScope });
596
599
  } catch (e) {
597
- console.error('[gds] web/callback: repo-token capture failed (non-blocking):', e.message);
600
+ log.error('[gds] web/callback: repo-token capture failed (non-blocking):', e.message);
598
601
  }
599
602
  }
600
603
  // The state cookie is apex-scoped now (#734) — clear it with the same
@@ -636,7 +639,7 @@ module.exports = function buildAuthRouter() {
636
639
  } catch (probeErr) {
637
640
  // Never let the classifier turn a clean 403 into a 500 — fall through
638
641
  // to the existing page, which is the safe answer for a stranger.
639
- console.error('[gds] web/callback admission classify', probeErr);
642
+ log.error('[gds] web/callback admission classify', probeErr);
640
643
  }
641
644
  if (expectedSoon) {
642
645
  return res.status(202).type('text/html').send(renderAccessPendingPage(err.login, recoveryStartHref(req)));
@@ -657,7 +660,7 @@ module.exports = function buildAuthRouter() {
657
660
  },
658
661
  ));
659
662
  }
660
- console.error('[gds] web/callback', err);
663
+ log.error('[gds] web/callback', err);
661
664
  res.status(500).type('text/html').send(renderAuthErrorPage(
662
665
  'The handshake threw an error',
663
666
  `The server tried to exchange the auth code for a session and something went wrong: ${err.message}. Try again; if it persists, ask the ${admitterLabel()} to investigate the server logs.`,
@@ -778,7 +781,7 @@ module.exports = function buildAuthRouter() {
778
781
  });
779
782
  joined = true;
780
783
  } catch (e) {
781
- console.log('[gds] discord auto-join failed (non-blocking):', e.message);
784
+ log.info('[gds] discord auto-join failed (non-blocking):', e.message);
782
785
  }
783
786
  }
784
787
  // F5 role-sync belt-and-braces (ADR 0033 §4): set/repair the rank-role even
@@ -790,7 +793,7 @@ module.exports = function buildAuthRouter() {
790
793
  // callback returns there (settings.js surfaces the ?discord= toast).
791
794
  res.redirect(joined ? '/builders/settings?discord=joined' : '/builders/settings?discord=linked');
792
795
  } catch (err) {
793
- console.error('[gds] discord/callback', err);
796
+ log.error('[gds] discord/callback', err);
794
797
  res.status(500).type('text/html').send(renderAuthErrorPage(
795
798
  'The Discord link threw an error',
796
799
  `Linking failed unexpectedly. Try again; if it persists, ask the ${admitterLabel()} to check the server logs.`,
@@ -814,13 +817,13 @@ module.exports = function buildAuthRouter() {
814
817
  )(null);
815
818
  if (guildId) {
816
819
  auth.removeDiscordGuildMember({ guildId, discordUserId: link.discord_user_id })
817
- .then((r) => console.log(`[gds] discord unlink kick: ${r.removed ? 'removed' : r.reason}`))
818
- .catch((e) => console.log('[gds] discord unlink kick failed (non-blocking):', e.message));
820
+ .then((r) => log.info(`[gds] discord unlink kick: ${r.removed ? 'removed' : r.reason}`))
821
+ .catch((e) => log.info('[gds] discord unlink kick failed (non-blocking):', e.message));
819
822
  }
820
823
  }
821
824
  res.json({ ok: true, unlinked: removed });
822
825
  } catch (err) {
823
- console.error('[gds] discord/unlink', err);
826
+ log.error('[gds] discord/unlink', err);
824
827
  res.fail('unlink_failed', 500);
825
828
  }
826
829
  });
@@ -897,7 +900,7 @@ module.exports = function buildAuthRouter() {
897
900
  if (builder) await db.revokeAllSessionsForBuilder(builder.id);
898
901
  res.json({ ok: true });
899
902
  } catch (err) {
900
- console.error('[gds] /auth/backchannel-logout revoke', err);
903
+ log.error('[gds] /auth/backchannel-logout revoke', err);
901
904
  res.fail('server_error', { status: 500, message: 'logout failed' });
902
905
  }
903
906
  });
@@ -941,7 +944,7 @@ module.exports = function buildAuthRouter() {
941
944
  paste_command: `node scripts/gds/paste-token.js ${token}`,
942
945
  });
943
946
  } catch (err) {
944
- console.error('[gds] POST /auth/cli-token/issue', err);
947
+ log.error('[gds] POST /auth/cli-token/issue', err);
945
948
  res.fail('issue_failed', { status: 500, message: 'internal error' });
946
949
  }
947
950
  });
@@ -968,7 +971,7 @@ module.exports = function buildAuthRouter() {
968
971
  const revoked = await db.revokeSessionById(id);
969
972
  res.json({ ok: true, revoked });
970
973
  } catch (err) {
971
- console.error('[gds] POST /auth/revoke', err);
974
+ log.error('[gds] POST /auth/revoke', err);
972
975
  res.fail('revoke_failed', { status: 500, message: 'internal error' });
973
976
  }
974
977
  });
@@ -995,7 +998,7 @@ module.exports = function buildAuthRouter() {
995
998
  const revoked = await db.revokeAllSessionsForBuilder(id);
996
999
  res.json({ ok: true, revoked });
997
1000
  } catch (err) {
998
- console.error('[gds] POST /auth/revoke-all', err);
1001
+ log.error('[gds] POST /auth/revoke-all', err);
999
1002
  res.fail('revoke_all_failed', { status: 500, message: 'internal error' });
1000
1003
  }
1001
1004
  });
@@ -1092,7 +1095,7 @@ module.exports = function buildAuthRouter() {
1092
1095
  }
1093
1096
  res.json({ ok: true });
1094
1097
  } catch (err) {
1095
- console.error('[gds] POST /auth/app-pair/approve', err);
1098
+ log.error('[gds] POST /auth/app-pair/approve', err);
1096
1099
  res.fail('approve_failed', { status: 500, message: 'internal error' });
1097
1100
  }
1098
1101
  });