@bongos/core 1.19.765 → 1.19.766
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.
- package/.bongos-core.json +20 -15
- package/docs/module-api-changelog.md +2 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/scripts/gds/provision-config.js +8 -0
- package/scripts/gds/provision-units.js +6 -1
- package/src/bongos/pool.js +18 -2
- package/src/module-api.js +1 -1
- package/tests/pool_max.mjs +79 -0
- package/tests/provision.mjs +21 -0
package/.bongos-core.json
CHANGED
|
@@ -2,22 +2,22 @@
|
|
|
2
2
|
"artifact": "bongos-core",
|
|
3
3
|
"manifest_schema": 1,
|
|
4
4
|
"generator": "scripts/gds/package-core.js",
|
|
5
|
-
"core_version": "1.19.
|
|
6
|
-
"core_contract": "1.19.
|
|
7
|
-
"source_commit": "
|
|
5
|
+
"core_version": "1.19.766",
|
|
6
|
+
"core_contract": "1.19.766",
|
|
7
|
+
"source_commit": "02a01e26d1d999916c8c67e1e02b06d0db40b694",
|
|
8
8
|
"source_ref": "HEAD",
|
|
9
|
-
"built_at": "2026-09-16T13:
|
|
9
|
+
"built_at": "2026-09-16T13:48:32.669Z",
|
|
10
10
|
"redaction": {
|
|
11
11
|
"model": "docs-redacted+functional-verbatim",
|
|
12
12
|
"docs_redacted": 483,
|
|
13
13
|
"agent_docs_stubbed": 25,
|
|
14
|
-
"functional_verbatim":
|
|
14
|
+
"functional_verbatim": 2122,
|
|
15
15
|
"rules": 3,
|
|
16
16
|
"gate_literals": 3,
|
|
17
17
|
"gate": "passed"
|
|
18
18
|
},
|
|
19
|
-
"file_count":
|
|
20
|
-
"tree_sha256": "
|
|
19
|
+
"file_count": 2630,
|
|
20
|
+
"tree_sha256": "56708a327bb43ff2237f2ef0cdd0fac079947882699533e825262e7a16bcf724",
|
|
21
21
|
"files": [
|
|
22
22
|
{
|
|
23
23
|
"path": ".claude/skills/ask-for-help/SKILL.md",
|
|
@@ -2512,7 +2512,7 @@
|
|
|
2512
2512
|
{
|
|
2513
2513
|
"path": "docs/module-api-changelog.md",
|
|
2514
2514
|
"mode": "0000644",
|
|
2515
|
-
"sha256": "
|
|
2515
|
+
"sha256": "10fb4078df266a5663d8079411fdf7698adb1aa8e20a19ca63ff722e1ecb8134"
|
|
2516
2516
|
},
|
|
2517
2517
|
{
|
|
2518
2518
|
"path": "docs/modules-contract.md",
|
|
@@ -7577,12 +7577,12 @@
|
|
|
7577
7577
|
{
|
|
7578
7578
|
"path": "package-lock.json",
|
|
7579
7579
|
"mode": "0000644",
|
|
7580
|
-
"sha256": "
|
|
7580
|
+
"sha256": "6e4cfa58c41e8fbe29dc94bbe4bd7a4eafedb88034b7d1ba3334d4a0764f5b56"
|
|
7581
7581
|
},
|
|
7582
7582
|
{
|
|
7583
7583
|
"path": "package.json",
|
|
7584
7584
|
"mode": "0000644",
|
|
7585
|
-
"sha256": "
|
|
7585
|
+
"sha256": "b61349c8315ac531b941ca35f84a7323b2192e0ab7507c4d76e1c592c2a9f2e7"
|
|
7586
7586
|
},
|
|
7587
7587
|
{
|
|
7588
7588
|
"path": "public-docs/index.html",
|
|
@@ -8412,7 +8412,7 @@
|
|
|
8412
8412
|
{
|
|
8413
8413
|
"path": "scripts/gds/provision-config.js",
|
|
8414
8414
|
"mode": "0000644",
|
|
8415
|
-
"sha256": "
|
|
8415
|
+
"sha256": "c870abebd21411e1fec744281130d116f3b9e8f255ddb6823aae4072a420cc15"
|
|
8416
8416
|
},
|
|
8417
8417
|
{
|
|
8418
8418
|
"path": "scripts/gds/provision-net.js",
|
|
@@ -8432,7 +8432,7 @@
|
|
|
8432
8432
|
{
|
|
8433
8433
|
"path": "scripts/gds/provision-units.js",
|
|
8434
8434
|
"mode": "0000644",
|
|
8435
|
-
"sha256": "
|
|
8435
|
+
"sha256": "e299687a54305e45ea57ff0a439f5ff099cb5d51be64bb0a48056c93309a67e3"
|
|
8436
8436
|
},
|
|
8437
8437
|
{
|
|
8438
8438
|
"path": "scripts/gds/provision.js",
|
|
@@ -9237,7 +9237,7 @@
|
|
|
9237
9237
|
{
|
|
9238
9238
|
"path": "src/bongos/pool.js",
|
|
9239
9239
|
"mode": "0000644",
|
|
9240
|
-
"sha256": "
|
|
9240
|
+
"sha256": "f3fa3e0360c1bd7fb9bdf5756edccb8c5f5cad4d9ef2b89b7f0c6382c5240294"
|
|
9241
9241
|
},
|
|
9242
9242
|
{
|
|
9243
9243
|
"path": "src/bongos/prelaunch-gate.js",
|
|
@@ -9397,7 +9397,7 @@
|
|
|
9397
9397
|
{
|
|
9398
9398
|
"path": "src/module-api.js",
|
|
9399
9399
|
"mode": "0000644",
|
|
9400
|
-
"sha256": "
|
|
9400
|
+
"sha256": "9c4c56d73c466a19a699f6c37e439d54d88211a363a869f9d87e3e840f493a2a"
|
|
9401
9401
|
},
|
|
9402
9402
|
{
|
|
9403
9403
|
"path": "src/module-loader/catalog.js",
|
|
@@ -11874,6 +11874,11 @@
|
|
|
11874
11874
|
"mode": "0000644",
|
|
11875
11875
|
"sha256": "ff9d438a33a32fecc04caa6e652eeaaded6394438bcec0d508ae7c516fa62333"
|
|
11876
11876
|
},
|
|
11877
|
+
{
|
|
11878
|
+
"path": "tests/pool_max.mjs",
|
|
11879
|
+
"mode": "0000644",
|
|
11880
|
+
"sha256": "68e5b941a1889ddc44fd548e10bb1fdb33822628b0da392592f747a32f7ccd1a"
|
|
11881
|
+
},
|
|
11877
11882
|
{
|
|
11878
11883
|
"path": "tests/post_login_landing.mjs",
|
|
11879
11884
|
"mode": "0000644",
|
|
@@ -12052,7 +12057,7 @@
|
|
|
12052
12057
|
{
|
|
12053
12058
|
"path": "tests/provision.mjs",
|
|
12054
12059
|
"mode": "0000644",
|
|
12055
|
-
"sha256": "
|
|
12060
|
+
"sha256": "245deb7b090f528180dde1457b821a8c8c2bd74a643524f441519859659643e8"
|
|
12056
12061
|
},
|
|
12057
12062
|
{
|
|
12058
12063
|
"path": "tests/provision_restart.mjs",
|
|
@@ -2004,5 +2004,7 @@ is load-bearing: the script throws rather than guess if it is missing, and
|
|
|
2004
2004
|
landed since 1.19.763 with no explicit bump. run 35059447999. (task 1002620)
|
|
2005
2005
|
1.19.765 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
2006
2006
|
landed since 1.19.764 with no explicit bump. run 35102327521. (task 1002620)
|
|
2007
|
+
1.19.766 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
2008
|
+
landed since 1.19.765 with no explicit bump. run 35104246900. (task 1002620)
|
|
2007
2009
|
---------------------------------------------------------------------------
|
|
2008
2010
|
```
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.766",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@bongos/core",
|
|
9
|
-
"version": "1.19.
|
|
9
|
+
"version": "1.19.766",
|
|
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.
|
|
3
|
+
"version": "1.19.766",
|
|
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",
|
|
@@ -134,6 +134,14 @@ const CONFIG = {
|
|
|
134
134
|
// mirror an override into web.env so the fleet API reports the same number.
|
|
135
135
|
boxCeiling: numEnv('PROVISION_BOX_CEILING', null),
|
|
136
136
|
memFloorMb: numEnv('PROVISION_MEM_FLOOR_MB', null),
|
|
137
|
+
// The Postgres pool cap written into a CO-LOCATED instance's web.env as PGPOOL_MAX
|
|
138
|
+
// (task 1003928, ADR 0285 §3). pg's default is 10 per process and the shared server
|
|
139
|
+
// allows 97 non-superuser backends, so nine busy projects exhaust it whatever the
|
|
140
|
+
// RAM headroom. Idle projects hold zero backends, so this bounds bursts, not the
|
|
141
|
+
// steady state: 3 × 15 slots + the control plane's own 10 stays under the 97. The
|
|
142
|
+
// control plane keeps pg's default (it is not provisioned by this runner), and a
|
|
143
|
+
// dedicated droplet has its own server, so neither is written.
|
|
144
|
+
instancePoolMax: numEnv('PROVISION_INSTANCE_PGPOOL_MAX', 3),
|
|
137
145
|
};
|
|
138
146
|
|
|
139
147
|
// task 1002757 — the provisioner's git identity resolves from the instance's own
|
|
@@ -267,13 +267,18 @@ GITHUB_APP_PRIVATE_KEY=${String(appPem).replace(/\r?\n/g, '\\n')}
|
|
|
267
267
|
const dbLines = dbPassword
|
|
268
268
|
? `PGUSER=${instanceDbRole(inst)}\nPGPASSWORD=${dbPassword}\n`
|
|
269
269
|
: '';
|
|
270
|
+
// The pool cap (task 1003928, ADR 0285 §3): a co-located instance shares the box's
|
|
271
|
+
// Postgres with every sibling and the control plane, so its pool may not take pg's
|
|
272
|
+
// default 10 of the server's 97. src/bongos/pool.js reads it bare. A dedicated
|
|
273
|
+
// droplet has its own server and keeps the default — the line is not written.
|
|
274
|
+
const poolLine = inst.hosting_shape === 'dedicated' ? '' : `PGPOOL_MAX=${CONFIG.instancePoolMax}\n`;
|
|
270
275
|
return `# /etc/${inst.slug}/web.env — per-instance secrets (chmod 600, OUTSIDE the repo).
|
|
271
276
|
${credComment}
|
|
272
277
|
${prefix}_GITHUB_CLIENT_ID=${clientId || ''}
|
|
273
278
|
${prefix}_GITHUB_CLIENT_SECRET=${clientSecret || ''}
|
|
274
279
|
${firstAdminLine}${originLines}${appLines}PORT=${inst.port}
|
|
275
280
|
PGDATABASE=${dbName(inst)}
|
|
276
|
-
${dbLines}${fedLines}${settingsEnvLines(inst, prefix)}`;
|
|
281
|
+
${poolLine}${dbLines}${fedLines}${settingsEnvLines(inst, prefix)}`;
|
|
277
282
|
}
|
|
278
283
|
function webEnvPath(inst, { etcBase = CONFIG.etcBase } = {}) {
|
|
279
284
|
return path.posix.join(etcBase, inst.slug, 'web.env');
|
package/src/bongos/pool.js
CHANGED
|
@@ -37,11 +37,27 @@ function instanceDbName() {
|
|
|
37
37
|
return undefined;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
// The pool's connection cap (task 1003928, ADR 0285 §3). pg's default `max` is 10 and
|
|
41
|
+
// the shared server allows 97 non-superuser backends, so nine busy co-located
|
|
42
|
+
// instances exhaust it whatever the RAM headroom — connections are the second wall
|
|
43
|
+
// after memory. A provisioned instance's web.env carries PGPOOL_MAX (the runner
|
|
44
|
+
// writes a small cap, PROVISION_INSTANCE_PGPOOL_MAX); UNSET — self-host, dev, the
|
|
45
|
+
// control plane itself — returns undefined so pg keeps its own default and nothing
|
|
46
|
+
// outside a co-located box changes. Read bare like the PG* family beside it: it is
|
|
47
|
+
// a pg option, not a branding one, so it does not ride resolveEnv's prefix.
|
|
48
|
+
// Anything that is not a positive integer is ignored rather than passed to pg.
|
|
49
|
+
function poolMax(env = process.env) {
|
|
50
|
+
const n = Number(env.PGPOOL_MAX);
|
|
51
|
+
return Number.isInteger(n) && n > 0 ? n : undefined;
|
|
52
|
+
}
|
|
53
|
+
|
|
40
54
|
let pool;
|
|
55
|
+
const max = poolMax();
|
|
41
56
|
if (process.env.DATABASE_URL) {
|
|
42
|
-
pool = new Pool({ connectionString: process.env.DATABASE_URL });
|
|
57
|
+
pool = new Pool({ connectionString: process.env.DATABASE_URL, ...(max ? { max } : {}) });
|
|
43
58
|
} else {
|
|
44
59
|
pool = new Pool({
|
|
60
|
+
...(max ? { max } : {}),
|
|
45
61
|
// Peer auth via the local Unix socket on the droplet; PG* env overrides for other environments.
|
|
46
62
|
host: process.env.PGHOST || '/var/run/postgresql',
|
|
47
63
|
database: process.env.PGDATABASE || instanceDbName(),
|
|
@@ -66,4 +82,4 @@ pool.on('error', (err) => {
|
|
|
66
82
|
console.error('[gds/pool] unexpected pool error', err);
|
|
67
83
|
});
|
|
68
84
|
|
|
69
|
-
module.exports = { pool, instanceDbName };
|
|
85
|
+
module.exports = { pool, instanceDbName, poolMax };
|
package/src/module-api.js
CHANGED
|
@@ -71,7 +71,7 @@ const { responsibilityFor, ROLE_RESPONSIBILITIES } = require('./role-responsibil
|
|
|
71
71
|
// there. scripts/gds/bump-version.js still rewrites the literal below; it appends
|
|
72
72
|
// the entry to that file. Look for a version's history there, not here.
|
|
73
73
|
// ---------------------------------------------------------------------------
|
|
74
|
-
const CORE_VERSION = '1.19.
|
|
74
|
+
const CORE_VERSION = '1.19.766'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
|
|
75
75
|
|
|
76
76
|
// A namespaced logger so a module's log lines are attributable + consistent.
|
|
77
77
|
// Usage: const log = api.logger('dev-box'); log.info('mounted');
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// tests/pool_max.mjs
|
|
2
|
+
//
|
|
3
|
+
// The Postgres pool cap (task 1003928, ADR 0285 §3). Every co-located instance used
|
|
4
|
+
// to open pg's default 10 connections against a shared server that allows 97, so
|
|
5
|
+
// nine busy projects exhausted it whatever the RAM headroom. src/bongos/pool.js now
|
|
6
|
+
// honours PGPOOL_MAX; the runner writes it into a co-located instance's web.env
|
|
7
|
+
// (tests/provision.mjs pins that half). These pin the pool half:
|
|
8
|
+
//
|
|
9
|
+
// • the env reaches pg's `max` — asserted on a REAL Pool built by requiring the
|
|
10
|
+
// module in a child process with the env set (pool.js builds its Pool at require
|
|
11
|
+
// time, so the assertion has to cross a process boundary; no connection is ever
|
|
12
|
+
// opened — a Pool connects lazily);
|
|
13
|
+
// • absence leaves pg's default (10), so self-host, dev and the control plane are
|
|
14
|
+
// unchanged;
|
|
15
|
+
// • garbage is ignored, never handed to pg.
|
|
16
|
+
//
|
|
17
|
+
// DB-free. Run: node tests/pool_max.mjs
|
|
18
|
+
|
|
19
|
+
import { strict as assert } from 'node:assert';
|
|
20
|
+
import { execFileSync } from 'node:child_process';
|
|
21
|
+
import path from 'node:path';
|
|
22
|
+
import { fileURLToPath } from 'node:url';
|
|
23
|
+
import { poolMax } from '../src/bongos/pool.js';
|
|
24
|
+
|
|
25
|
+
let passed = 0, failed = 0;
|
|
26
|
+
function t(name, fn) {
|
|
27
|
+
try { fn(); passed++; console.log(` PASS ${name}`); }
|
|
28
|
+
catch (e) { failed++; console.log(` FAIL ${name}\n ${e.message}`); }
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const ROOT = path.resolve(fileURLToPath(new URL('..', import.meta.url)));
|
|
32
|
+
const POOL = path.join(ROOT, 'src', 'bongos', 'pool.js');
|
|
33
|
+
|
|
34
|
+
// Build the pool in a child with a controlled env and report what pg was handed.
|
|
35
|
+
// The DB name is pinned so instanceDbName() never reads a pack; nothing connects.
|
|
36
|
+
function poolMaxInChild(extraEnv) {
|
|
37
|
+
const env = { ...process.env, PGDATABASE: 'pool_max_probe' };
|
|
38
|
+
delete env.PGPOOL_MAX; delete env.DATABASE_URL; delete env.NODE_TEST_CONTEXT;
|
|
39
|
+
Object.assign(env, extraEnv);
|
|
40
|
+
const out = execFileSync(process.execPath, [
|
|
41
|
+
'-e', 'const { pool } = require(process.argv[1]); console.log(JSON.stringify({ max: pool.options.max })); pool.end();',
|
|
42
|
+
POOL,
|
|
43
|
+
], { env, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] });
|
|
44
|
+
return JSON.parse(out.trim().split('\n').pop()).max;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
console.log('poolMax (pure):');
|
|
48
|
+
|
|
49
|
+
t('a positive integer is the cap; absent/blank/garbage/zero/negative/fractional → undefined (pg keeps its default)', () => {
|
|
50
|
+
assert.equal(poolMax({ PGPOOL_MAX: '3' }), 3);
|
|
51
|
+
assert.equal(poolMax({ PGPOOL_MAX: '10' }), 10);
|
|
52
|
+
assert.equal(poolMax({}), undefined);
|
|
53
|
+
assert.equal(poolMax({ PGPOOL_MAX: '' }), undefined);
|
|
54
|
+
assert.equal(poolMax({ PGPOOL_MAX: 'three' }), undefined);
|
|
55
|
+
assert.equal(poolMax({ PGPOOL_MAX: '0' }), undefined);
|
|
56
|
+
assert.equal(poolMax({ PGPOOL_MAX: '-2' }), undefined);
|
|
57
|
+
assert.equal(poolMax({ PGPOOL_MAX: '2.5' }), undefined);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
console.log('\nthe env reaches the real Pool (child process, no connection):');
|
|
61
|
+
|
|
62
|
+
t('PGPOOL_MAX=3 → pool.options.max is 3 (the co-located instance shape)', () => {
|
|
63
|
+
assert.equal(poolMaxInChild({ PGPOOL_MAX: '3' }), 3);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
t('unset → pg\'s default 10 (self-host, dev, the control plane: unchanged)', () => {
|
|
67
|
+
assert.equal(poolMaxInChild({}), 10);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
t('garbage → pg\'s default, never a NaN handed to pg', () => {
|
|
71
|
+
assert.equal(poolMaxInChild({ PGPOOL_MAX: 'lots' }), 10);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
t('the cap applies on the DATABASE_URL branch too', () => {
|
|
75
|
+
assert.equal(poolMaxInChild({ PGPOOL_MAX: '4', DATABASE_URL: 'postgres://u:p@127.0.0.1:1/pool_max_probe' }), 4);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
console.log(`\npool_max: ${passed} passed, ${failed} failed`);
|
|
79
|
+
process.exit(failed ? 1 : 0);
|
package/tests/provision.mjs
CHANGED
|
@@ -2618,6 +2618,27 @@ t('webEnvBody: NO password ⇒ no PG credential lines — an instance provisione
|
|
|
2618
2618
|
assert.match(b, /^PGDATABASE=mercury$/m, 'the DB name is unchanged either way');
|
|
2619
2619
|
});
|
|
2620
2620
|
|
|
2621
|
+
// ── the Postgres pool cap (task 1003928, ADR 0285 §3) ─────────────────────────
|
|
2622
|
+
t('webEnvBody: a CO-LOCATED instance (co-tenant or standalone) carries PGPOOL_MAX at the runner\'s cap, beside PGDATABASE', () => {
|
|
2623
|
+
for (const shape of ['co-tenant', 'standalone']) {
|
|
2624
|
+
const b = P.webEnvBody({ ...coTenant, hosting_shape: shape });
|
|
2625
|
+
assert.match(b, new RegExp(`^PGPOOL_MAX=${P.CONFIG.instancePoolMax}$`, 'm'), `${shape} carries the cap`);
|
|
2626
|
+
const lines = b.split('\n');
|
|
2627
|
+
assert.equal(lines[lines.indexOf('PGDATABASE=mercury') + 1], `PGPOOL_MAX=${P.CONFIG.instancePoolMax}`, 'written right after the DB name, with or without a credential');
|
|
2628
|
+
}
|
|
2629
|
+
assert.equal(P.CONFIG.instancePoolMax, 3, 'the default cap: 3 × 15 slots + the control plane\'s 10 stays under the server\'s 97');
|
|
2630
|
+
// …and it rides along with the credential lines, not instead of them.
|
|
2631
|
+
const withCred = P.webEnvBody(coTenant, { dbPassword: 'pw_ABC-123' });
|
|
2632
|
+
assert.match(withCred, /^PGPOOL_MAX=3$/m);
|
|
2633
|
+
assert.match(withCred, /^PGUSER=bongos-mercury$/m);
|
|
2634
|
+
});
|
|
2635
|
+
|
|
2636
|
+
t('webEnvBody: a DEDICATED droplet has its own Postgres — no PGPOOL_MAX line, pg keeps its default', () => {
|
|
2637
|
+
const b = P.webEnvBody({ ...coTenant, hosting_shape: 'dedicated' });
|
|
2638
|
+
assert.doesNotMatch(b, /^PGPOOL_MAX=/m);
|
|
2639
|
+
assert.match(b, /^PGDATABASE=mercury$/m);
|
|
2640
|
+
});
|
|
2641
|
+
|
|
2621
2642
|
t('dedicatedUserData: the droplet runs the SAME account + role steps, in the same order around the migrate', () => {
|
|
2622
2643
|
const ud = P.dedicatedUserData(coTenant);
|
|
2623
2644
|
const at = (needle) => ud.indexOf(needle);
|