@aifabrix/builder 2.48.0 → 2.49.1
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/.cursor/skills/README.md +14 -0
- package/jest.isolated-projects.js +3 -0
- package/jest.projects.js +12 -0
- package/lib/api/applications.api.js +23 -0
- package/lib/api/dev.api.js +1 -1
- package/lib/api/index.js +5 -2
- package/lib/api/types/dev.types.js +3 -2
- package/lib/app/deploy-config.js +6 -4
- package/lib/app/register.js +1 -1
- package/lib/app/run-container-start.js +0 -9
- package/lib/app/run-helpers.js +8 -4
- package/lib/app/run-reload-sync.js +16 -18
- package/lib/app/run-stop-containers.js +48 -5
- package/lib/app/run.js +6 -3
- package/lib/app/show.js +2 -2
- package/lib/cli/infra-guided-auth.js +50 -12
- package/lib/cli/infra-guided-footers.js +164 -0
- package/lib/cli/infra-guided-spinners.js +57 -0
- package/lib/cli/infra-guided.js +52 -181
- package/lib/cli/setup-app.js +1 -1
- package/lib/cli/setup-dev.js +3 -2
- package/lib/cli/setup-infra.js +3 -2
- package/lib/cli/setup-platform.js +3 -0
- package/lib/commands/login-device.js +6 -2
- package/lib/commands/login.js +4 -1
- package/lib/commands/setup-modes.js +19 -3
- package/lib/commands/setup-platform-auth.js +25 -0
- package/lib/commands/setup-prompts-admin.js +9 -0
- package/lib/commands/setup.js +60 -8
- package/lib/commands/teardown.js +3 -5
- package/lib/commands/up-common.js +8 -11
- package/lib/commands/up-dataplane-bootstrap.js +149 -0
- package/lib/commands/up-dataplane-credentials.js +19 -4
- package/lib/commands/up-dataplane.js +155 -24
- package/lib/commands/up-miso.js +6 -2
- package/lib/core/secrets-env-declarative-expand.js +35 -23
- package/lib/core/setup-platform-mode.js +43 -0
- package/lib/deployment/deployer-status.js +26 -3
- package/lib/deployment/deployer.js +7 -14
- package/lib/external-system/download-helpers.js +10 -1
- package/lib/infrastructure/compose.js +40 -12
- package/lib/infrastructure/services.js +15 -3
- package/lib/infrastructure/traefik-tls-dynamic.js +88 -0
- package/lib/utils/app-bootstrap-api.js +53 -0
- package/lib/utils/app-run-containers.js +14 -3
- package/lib/utils/applications-config-defaults.js +19 -3
- package/lib/utils/compose-generate-docker-compose.js +16 -5
- package/lib/utils/compose-generator.js +34 -15
- package/lib/utils/compose-traefik-ingress-base.js +13 -14
- package/lib/utils/config-paths.js +6 -1
- package/lib/utils/dev-user-groups.js +1 -0
- package/lib/utils/docker-daemon-tls-ca.js +94 -57
- package/lib/utils/docker-reload-mount.js +12 -1
- package/lib/utils/external-env-template.js +53 -18
- package/lib/utils/health-check-poll-ui.js +119 -0
- package/lib/utils/health-check-url.js +54 -34
- package/lib/utils/health-check.js +91 -35
- package/lib/utils/infra-optional-service-flags.js +31 -2
- package/lib/utils/mutagen.js +7 -4
- package/lib/utils/paths.js +26 -36
- package/lib/utils/platform-controller-url.js +23 -1
- package/lib/utils/refresh-remote-settings.js +67 -0
- package/lib/utils/remote-host-for-front-door.js +43 -0
- package/lib/utils/run-cli-flags.js +37 -1
- package/lib/utils/url-declarative-public-base.js +11 -4
- package/lib/utils/url-declarative-resolve-build-urls.js +11 -2
- package/lib/utils/url-declarative-resolve-expand-token.js +12 -1
- package/lib/utils/url-declarative-resolve-load-doc.js +30 -11
- package/lib/utils/url-declarative-resolve.js +2 -0
- package/lib/utils/urls-local-registry-scan.js +95 -10
- package/lib/utils/urls-local-registry.js +24 -1
- package/lib/utils/worker-sidecars.js +2 -2
- package/lib/validation/external-system-auth-rules.js +45 -7
- package/package.json +2 -1
- package/scripts/test-dataplane-bootstrap.js +331 -0
- package/templates/applications/dataplane/application.yaml +1 -1
- package/templates/applications/dataplane/env.template +24 -6
- package/templates/applications/dataplane/rbac.yaml +11 -0
- package/templates/applications/miso-controller/application.yaml +1 -1
- package/templates/applications/miso-controller/env.template +5 -0
- package/templates/infra/compose.yaml.hbs +13 -9
- package/templates/python/docker-compose.hbs +3 -0
- package/templates/typescript/docker-compose.hbs +3 -0
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/* eslint-disable no-console, require-jsdoc, max-lines-per-function, max-statements */
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Diagnose and optionally exercise dataplane bootstrap provisioning (plan 197.0).
|
|
6
|
+
*
|
|
7
|
+
* @fileoverview Manual test / diagnostic for dataplane bootstrap API
|
|
8
|
+
*
|
|
9
|
+
* Usage (from aifabrix-builder repo root):
|
|
10
|
+
* node scripts/test-dataplane-bootstrap.js
|
|
11
|
+
* node scripts/test-dataplane-bootstrap.js --probe-api
|
|
12
|
+
* node scripts/test-dataplane-bootstrap.js --call-bootstrap
|
|
13
|
+
* node scripts/test-dataplane-bootstrap.js --controller-url http://localhost:3100 --probe-api
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
'use strict';
|
|
17
|
+
|
|
18
|
+
const path = require('path');
|
|
19
|
+
|
|
20
|
+
const ROOT = path.join(__dirname, '..');
|
|
21
|
+
|
|
22
|
+
function parseArgs(argv) {
|
|
23
|
+
const out = {
|
|
24
|
+
controllerUrl: null,
|
|
25
|
+
envKey: 'dev',
|
|
26
|
+
probeApi: false,
|
|
27
|
+
callBootstrap: false,
|
|
28
|
+
help: false
|
|
29
|
+
};
|
|
30
|
+
for (let i = 2; i < argv.length; i += 1) {
|
|
31
|
+
const arg = argv[i];
|
|
32
|
+
if (arg === '--help' || arg === '-h') {
|
|
33
|
+
out.help = true;
|
|
34
|
+
} else if (arg === '--probe-api') {
|
|
35
|
+
out.probeApi = true;
|
|
36
|
+
} else if (arg === '--call-bootstrap') {
|
|
37
|
+
out.callBootstrap = true;
|
|
38
|
+
out.probeApi = true;
|
|
39
|
+
} else if (arg === '--controller-url' && argv[i + 1]) {
|
|
40
|
+
out.controllerUrl = String(argv[++i]).trim();
|
|
41
|
+
} else if (arg === '--env-key' && argv[i + 1]) {
|
|
42
|
+
out.envKey = String(argv[++i]).trim().toLowerCase();
|
|
43
|
+
} else {
|
|
44
|
+
throw new Error(`Unknown argument: ${arg}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return out;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function printHelp() {
|
|
51
|
+
console.log(`\
|
|
52
|
+
Dataplane bootstrap diagnostic
|
|
53
|
+
|
|
54
|
+
node scripts/test-dataplane-bootstrap.js [options]
|
|
55
|
+
|
|
56
|
+
Options:
|
|
57
|
+
--controller-url <url> Override controller base URL (default: ~/.aifabrix/config.yaml)
|
|
58
|
+
--env-key <key> Environment for bootstrap (default: dev)
|
|
59
|
+
--probe-api POST .../applications/bootstrap (idempotent register/check)
|
|
60
|
+
--call-bootstrap Same as --probe-api + save credentials locally (up-dataplane path)
|
|
61
|
+
-h, --help Show this help
|
|
62
|
+
|
|
63
|
+
Default (no flags): read-only gate checks — no API mutation.
|
|
64
|
+
|
|
65
|
+
Bootstrap avoids device login when ALL are true:
|
|
66
|
+
- environment is dev
|
|
67
|
+
- Docker CLI + Compose on this machine
|
|
68
|
+
- merged secrets have miso-controller-secrets-encryptionKeyVault (= controller ENCRYPTION_KEY)
|
|
69
|
+
- controller /health OK
|
|
70
|
+
`);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function line(label, value, ok) {
|
|
74
|
+
const mark = ok === true ? 'OK' : ok === false ? 'FAIL' : '—';
|
|
75
|
+
console.log(` [${mark}] ${label}: ${value}`);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function loadBuilderModules() {
|
|
79
|
+
return {
|
|
80
|
+
config: require(path.join(ROOT, 'lib/core/config')),
|
|
81
|
+
docker: require(path.join(ROOT, 'lib/utils/docker')),
|
|
82
|
+
loadSecrets: require(path.join(ROOT, 'lib/core/secrets-load')).loadSecrets,
|
|
83
|
+
checkHealthEndpoint: require(path.join(ROOT, 'lib/utils/health-check-request')).checkHealthEndpoint,
|
|
84
|
+
resolveControllerUrl: require(path.join(ROOT, 'lib/utils/controller-url')).resolveControllerUrl,
|
|
85
|
+
canUseBootstrapProvision: require(path.join(ROOT, 'lib/commands/up-dataplane-bootstrap'))
|
|
86
|
+
.canUseBootstrapProvision,
|
|
87
|
+
resolveBootstrapEncryptionKey: require(path.join(ROOT, 'lib/commands/up-dataplane-bootstrap'))
|
|
88
|
+
.resolveBootstrapEncryptionKey,
|
|
89
|
+
bootstrapDataplaneApplication: require(path.join(ROOT, 'lib/commands/up-dataplane-bootstrap'))
|
|
90
|
+
.bootstrapDataplaneApplication,
|
|
91
|
+
buildDataplaneImageRef: require(path.join(ROOT, 'lib/commands/up-dataplane')).buildDataplaneImageRef,
|
|
92
|
+
bootstrapApplication: require(path.join(ROOT, 'lib/api/applications.api')).bootstrapApplication,
|
|
93
|
+
loadVariablesYaml: require(path.join(ROOT, 'lib/utils/app-register-config')).loadVariablesYaml,
|
|
94
|
+
extractAppConfiguration: require(path.join(ROOT, 'lib/utils/app-register-config')).extractAppConfiguration,
|
|
95
|
+
buildRegistrationData: require(path.join(ROOT, 'lib/app/register')).buildRegistrationData,
|
|
96
|
+
applyLocalhostPortAdjustment: require(path.join(ROOT, 'lib/app/register')).applyLocalhostPortAdjustment
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async function runGateChecks(mods, controllerOverride) {
|
|
101
|
+
console.log('Prerequisites (canUseBootstrapProvision gates):');
|
|
102
|
+
const gates = {
|
|
103
|
+
devEnv: await checkEnvironmentGate(mods.config),
|
|
104
|
+
docker: await checkDockerGate(mods.docker),
|
|
105
|
+
encryptionKey: await checkEncryptionKeyGate(
|
|
106
|
+
mods.resolveBootstrapEncryptionKey,
|
|
107
|
+
mods.loadSecrets
|
|
108
|
+
)
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const controllerUrl = await resolveControllerBaseUrl(
|
|
112
|
+
mods.config,
|
|
113
|
+
mods.resolveControllerUrl,
|
|
114
|
+
controllerOverride
|
|
115
|
+
);
|
|
116
|
+
line('controller URL', controllerUrl, null);
|
|
117
|
+
gates.controllerHealthy = await checkControllerHealth(controllerUrl, mods.checkHealthEndpoint);
|
|
118
|
+
|
|
119
|
+
const eligible = await mods.canUseBootstrapProvision({ skipInfraCheck: true });
|
|
120
|
+
console.log('');
|
|
121
|
+
line('canUseBootstrapProvision', eligible ? 'true' : 'false', eligible);
|
|
122
|
+
printDeviceLoginReasons(gates);
|
|
123
|
+
|
|
124
|
+
return { gates, controllerUrl, eligible };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async function checkEnvironmentGate(config) {
|
|
128
|
+
const cfg = await config.getConfig();
|
|
129
|
+
const env = cfg && cfg.environment ? String(cfg.environment) : 'dev';
|
|
130
|
+
const ok = env === 'dev';
|
|
131
|
+
line('config environment', env, ok);
|
|
132
|
+
if (!ok) {
|
|
133
|
+
console.log(' → Bootstrap only runs in dev. Set: aifabrix auth --set-environment dev');
|
|
134
|
+
}
|
|
135
|
+
return ok;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async function checkDockerGate(docker) {
|
|
139
|
+
try {
|
|
140
|
+
await docker.ensureDockerAndCompose();
|
|
141
|
+
line('Docker CLI + Compose', 'available', true);
|
|
142
|
+
return true;
|
|
143
|
+
} catch (err) {
|
|
144
|
+
line('Docker CLI + Compose', err.message || 'unavailable', false);
|
|
145
|
+
console.log(' → Bootstrap requires local Docker (same machine as aifabrix run dataplane).');
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
async function checkEncryptionKeyGate(resolveBootstrapEncryptionKey, loadSecrets) {
|
|
151
|
+
const key = await resolveBootstrapEncryptionKey();
|
|
152
|
+
const ok = Boolean(key && String(key).trim());
|
|
153
|
+
line(
|
|
154
|
+
'bootstrap ENCRYPTION_KEY (merged secrets)',
|
|
155
|
+
ok ? `present (${String(key).length} chars, not shown)` : 'missing',
|
|
156
|
+
ok
|
|
157
|
+
);
|
|
158
|
+
if (!ok) {
|
|
159
|
+
const secrets = await loadSecrets(undefined);
|
|
160
|
+
const keys = secrets && typeof secrets === 'object' ? Object.keys(secrets) : [];
|
|
161
|
+
const hints = keys.filter((k) => /encryption/i.test(k));
|
|
162
|
+
console.log(
|
|
163
|
+
' → Need miso-controller-secrets-encryptionKeyVault in secrets (after up-miso resolve).'
|
|
164
|
+
);
|
|
165
|
+
if (hints.length) {
|
|
166
|
+
console.log(` → Secret keys containing "encryption": ${hints.join(', ')}`);
|
|
167
|
+
}
|
|
168
|
+
console.log(
|
|
169
|
+
' → config.yaml secrets-encryption is CLI file crypto only — not the bootstrap header key.'
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
return ok;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
async function resolveControllerBaseUrl(config, resolveControllerUrl, override) {
|
|
176
|
+
if (override) {
|
|
177
|
+
return override.replace(/\/+$/, '');
|
|
178
|
+
}
|
|
179
|
+
const cfg = await config.getConfig();
|
|
180
|
+
const platform = cfg.platformController || cfg.controller;
|
|
181
|
+
if (platform && String(platform).trim()) {
|
|
182
|
+
return String(platform).trim().replace(/\/+$/, '');
|
|
183
|
+
}
|
|
184
|
+
return (await resolveControllerUrl()).replace(/\/+$/, '');
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
async function checkControllerHealth(controllerUrl, checkHealthEndpoint) {
|
|
188
|
+
const healthUrl = `${controllerUrl}/health`;
|
|
189
|
+
try {
|
|
190
|
+
const ok = await checkHealthEndpoint(healthUrl, false);
|
|
191
|
+
line('controller health', healthUrl, ok);
|
|
192
|
+
if (!ok) {
|
|
193
|
+
console.log(' → Start platform: aifabrix up-infra && aifabrix up-miso (or af setup).');
|
|
194
|
+
}
|
|
195
|
+
return ok;
|
|
196
|
+
} catch (err) {
|
|
197
|
+
line('controller health', `${healthUrl} (${err.message})`, false);
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
async function buildRegistrationPayload(mods, controllerUrl, envKey) {
|
|
203
|
+
const { variables } = await mods.loadVariablesYaml('dataplane');
|
|
204
|
+
const appConfig = await mods.extractAppConfiguration(variables, 'dataplane', {});
|
|
205
|
+
await mods.applyLocalhostPortAdjustment(appConfig, controllerUrl, {});
|
|
206
|
+
const imageOverride = mods.buildDataplaneImageRef({});
|
|
207
|
+
return mods.buildRegistrationData(appConfig, {
|
|
208
|
+
imageOverride,
|
|
209
|
+
image: imageOverride,
|
|
210
|
+
envKey
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
async function probeBootstrapApi(controllerUrl, encryptionKey, envKey, mods) {
|
|
215
|
+
const pathSuffix = `/api/v1/environments/${envKey}/applications/bootstrap`;
|
|
216
|
+
console.log('');
|
|
217
|
+
console.log('Bootstrap API probe (--probe-api):');
|
|
218
|
+
line('endpoint', `${controllerUrl}${pathSuffix}`, null);
|
|
219
|
+
|
|
220
|
+
const registrationData = await buildRegistrationPayload(mods, controllerUrl, envKey);
|
|
221
|
+
const response = await mods.bootstrapApplication(
|
|
222
|
+
controllerUrl,
|
|
223
|
+
envKey,
|
|
224
|
+
encryptionKey,
|
|
225
|
+
registrationData
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
if (!response.success) {
|
|
229
|
+
return reportBootstrapFailure(response);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const data = response.data?.data || response.data;
|
|
233
|
+
const status = data?.status || (data?.application ? 'created' : 'ok');
|
|
234
|
+
const appKey = data?.application?.key || registrationData.key;
|
|
235
|
+
line('bootstrap API', `OK (${status}, app: ${appKey})`, true);
|
|
236
|
+
return true;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function reportBootstrapFailure(response) {
|
|
240
|
+
const msg = response.formattedError || response.error || 'request failed';
|
|
241
|
+
if (/401|403|encryption|Unauthorized/i.test(String(msg))) {
|
|
242
|
+
line('x-encryption-key', 'rejected by controller', false);
|
|
243
|
+
console.log(' → Controller ENCRYPTION_KEY does not match merged secrets vault key.');
|
|
244
|
+
return false;
|
|
245
|
+
}
|
|
246
|
+
if (/503|not configured/i.test(String(msg))) {
|
|
247
|
+
line('bootstrap on controller', 'disabled (ENCRYPTION_KEY unset on controller)', false);
|
|
248
|
+
return false;
|
|
249
|
+
}
|
|
250
|
+
line('bootstrap API', msg, false);
|
|
251
|
+
return false;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function printDeviceLoginReasons(gates) {
|
|
255
|
+
console.log('');
|
|
256
|
+
console.log('Why setup may ask for device login instead of bootstrap:');
|
|
257
|
+
if (!gates.devEnv) console.log(' • environment is not dev');
|
|
258
|
+
if (!gates.docker) console.log(' • Docker/Compose not available on this host');
|
|
259
|
+
if (!gates.encryptionKey) console.log(' • bootstrap encryption key missing from merged secrets');
|
|
260
|
+
if (!gates.controllerHealthy) {
|
|
261
|
+
console.log(' • controller not healthy (setup may still run auth before dataplane step)');
|
|
262
|
+
}
|
|
263
|
+
if (gates.devEnv && gates.docker && gates.encryptionKey && gates.controllerHealthy) {
|
|
264
|
+
console.log(' • All bootstrap gates pass — setup should show "Bootstrap provision (no device login)"');
|
|
265
|
+
console.log(' • If dataplane still missing, check setup logs after "Starting Dataplane..."');
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
async function main() {
|
|
270
|
+
const args = parseArgs(process.argv);
|
|
271
|
+
if (args.help) {
|
|
272
|
+
printHelp();
|
|
273
|
+
process.exit(0);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
console.log('Dataplane bootstrap diagnostic');
|
|
277
|
+
console.log('================================');
|
|
278
|
+
console.log('');
|
|
279
|
+
|
|
280
|
+
const mods = loadBuilderModules();
|
|
281
|
+
await mods.config.ensureSecretsEncryptionKey();
|
|
282
|
+
|
|
283
|
+
const { gates, controllerUrl, eligible } = await runGateChecks(mods, args.controllerUrl);
|
|
284
|
+
|
|
285
|
+
if (!gates.controllerHealthy || !gates.encryptionKey) {
|
|
286
|
+
console.log('');
|
|
287
|
+
console.log('Fix failing gates above, then re-run with --probe-api.');
|
|
288
|
+
process.exit(1);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (!args.probeApi) {
|
|
292
|
+
console.log('');
|
|
293
|
+
console.log('Read-only checks done. To exercise bootstrap API:');
|
|
294
|
+
console.log(' node scripts/test-dataplane-bootstrap.js --probe-api');
|
|
295
|
+
process.exit(eligible ? 0 : 1);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const encryptionKey = await mods.resolveBootstrapEncryptionKey();
|
|
299
|
+
const apiOk = await probeBootstrapApi(controllerUrl, encryptionKey, args.envKey, mods);
|
|
300
|
+
if (!apiOk) {
|
|
301
|
+
process.exit(1);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
if (!args.callBootstrap) {
|
|
305
|
+
process.exit(0);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
if (!eligible) {
|
|
309
|
+
console.error('');
|
|
310
|
+
console.error('Refusing --call-bootstrap: canUseBootstrapProvision is false.');
|
|
311
|
+
process.exit(1);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
console.log('');
|
|
315
|
+
console.log('Saving credentials via bootstrapDataplaneApplication...');
|
|
316
|
+
await mods.bootstrapDataplaneApplication(
|
|
317
|
+
{ skipInfraCheck: true },
|
|
318
|
+
controllerUrl,
|
|
319
|
+
args.envKey,
|
|
320
|
+
mods.buildDataplaneImageRef
|
|
321
|
+
);
|
|
322
|
+
console.log('');
|
|
323
|
+
console.log('Done. Next: aifabrix deploy dataplane --local or aifabrix run dataplane');
|
|
324
|
+
process.exit(0);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
main().catch((err) => {
|
|
328
|
+
console.error('');
|
|
329
|
+
console.error(`Error: ${err.message}`);
|
|
330
|
+
process.exit(1);
|
|
331
|
+
});
|
|
@@ -5,7 +5,7 @@ app:
|
|
|
5
5
|
description: "AI Fabrix Dataplane is a secure, in-tenant integration and automation layer that supplies governed, normalized, and explainable enterprise data to AI agents. Using CIP as a declarative standard, it enforces RBAC and ABAC, executes integrations, and exposes trusted data via MCP and OpenAPI."
|
|
6
6
|
type: webapp
|
|
7
7
|
language: python # Explicitly specify Python language
|
|
8
|
-
version:
|
|
8
|
+
version: 2.0.0
|
|
9
9
|
|
|
10
10
|
# Image Configuration
|
|
11
11
|
# Set tag to match your build (e.g. aifabrix build dataplane -t 1.0.0 then tag: 1.0.0)
|
|
@@ -21,24 +21,29 @@ LOG_FORMAT=json
|
|
|
21
21
|
# Production with persistent logs: use /mnt/data/logs/app.log and mount a volume at /mnt/data.
|
|
22
22
|
LOG_FILE_PATH=/mnt/data/logs/app.log
|
|
23
23
|
# If true, run without Redis/Celery (single process). Set false on API/worker/beat in production.
|
|
24
|
-
LOCAL_MODE=
|
|
24
|
+
LOCAL_MODE=false
|
|
25
25
|
|
|
26
26
|
# Celery worker process tuning (dataplane-worker container; optional overrides)
|
|
27
27
|
# CELERY_WORKER_QUEUES=external_data,document_sync,document_processing,audit
|
|
28
28
|
# CELERY_WORKER_CONCURRENCY=2
|
|
29
|
+
# CELERY_WORKER_RUNTIME is set automatically by python -m app.core.celery_worker_main (NullPool per task).
|
|
29
30
|
# CELERY_LOG_LEVEL=info
|
|
30
31
|
|
|
31
32
|
# When API_KEY is set, a matching Bearer token bypasses OAuth2 validation
|
|
32
33
|
# Same kv:// as miso-controller API_KEY so pipeline Bearer bypass uses one secrets.local entry
|
|
33
34
|
API_KEY=kv://miso-controller-secrets-apiKeyVault
|
|
34
35
|
# Comma-separated realm roles attached to API_KEY bypass (JWT realm_access.roles). Default: operator.
|
|
35
|
-
#
|
|
36
|
-
|
|
37
|
-
#
|
|
36
|
+
# Dev manual tests (437 governance, enterprise-llm llm.call): use full-access locally; restrict in tst/pro.
|
|
37
|
+
API_KEY_BYPASS_ROLES=full-access
|
|
38
|
+
# Example narrower dev set: operator,openai-admin,azure-openai-admin,ollama-admin
|
|
39
|
+
|
|
40
|
+
# Host-run manual test suite: PG idle cleanup + lifecycle certification DB snapshot (dev02 example).
|
|
41
|
+
# Inside Docker containers use postgres:5432 in DATABASE_URL; on host use localhost:5632 or docker exec.
|
|
42
|
+
# AIFABRIX_PG_DOCKER_CONTAINER=aifabrix-dev02-postgres
|
|
38
43
|
|
|
39
44
|
# API Configuration
|
|
40
45
|
API_V1_STR=/api/v1
|
|
41
|
-
VERSION=
|
|
46
|
+
VERSION=2.0.0
|
|
42
47
|
# Minimum supported aifabrix-builder CLI semver (advertised on /health and /api/v1/health; empty = no gate)
|
|
43
48
|
# MIN_BUILDER_CLI_VERSION=2.45.0
|
|
44
49
|
MIN_BUILDER_CLI_VERSION=2.45.5
|
|
@@ -86,6 +91,11 @@ DB_2_PASSWORD=kv://databases-dataplane-2-passwordKeyVault
|
|
|
86
91
|
RECORDS_DATABASE_URL=kv://databases-dataplane-3-urlKeyVault
|
|
87
92
|
DB_3_PASSWORD=kv://databases-dataplane-3-passwordKeyVault
|
|
88
93
|
|
|
94
|
+
# Pool budget when main/logs/records/vector share one PostgreSQL host (default 24).
|
|
95
|
+
# Host-native make dev (PORT 3011 + developerId×100, e.g. 3611) auto-caps to 12 on shared dev Postgres
|
|
96
|
+
# so Docker dataplane + miso-controller do not exhaust max_connections alongside make dev.
|
|
97
|
+
# SAME_SERVER_POOL_BUDGET=24
|
|
98
|
+
|
|
89
99
|
# =============================================================================
|
|
90
100
|
# REDIS CONFIGURATION
|
|
91
101
|
# =============================================================================
|
|
@@ -145,6 +155,7 @@ MISO_PIPELINE_ENV_KEY=
|
|
|
145
155
|
|
|
146
156
|
# LLM: chat/embed route through governed enterprise-llm only. Provider credentials
|
|
147
157
|
# live on LLM provider external systems (integration/openai, azure-openai, ollama).
|
|
158
|
+
# Shared secret for integration/openai: secrets-openaiApiKeyVault (af secret set ... --shared)
|
|
148
159
|
AI_VALIDATION_LLM_MODEL=gpt-4o-mini
|
|
149
160
|
|
|
150
161
|
# =============================================================================
|
|
@@ -182,7 +193,14 @@ AUDIT_ASYNC_SHADOW_ENQUEUE=false
|
|
|
182
193
|
# Set true for local dev policy CRUD; pair with VITE_POLICY_MANAGEMENT_ENABLED=true in app-ui.
|
|
183
194
|
POLICY_MANAGEMENT_API_ENABLED=false
|
|
184
195
|
|
|
185
|
-
#
|
|
196
|
+
# VIEW_MODULES — optional comma-separated UI module allowlist (unset = all modules).
|
|
197
|
+
# Keys: dashboard, work, content-review, assistants, connected-systems, templates,
|
|
198
|
+
# audit-logs, ai-trust, administration (deep link only; never sidebar in V1).
|
|
199
|
+
# Example pilot: VIEW_MODULES=dashboard,work,content-review
|
|
200
|
+
# Pair with VITE_VIEW_MODULES in app-ui (same value).
|
|
201
|
+
# VIEW_MODULES=
|
|
202
|
+
# VITE_VIEW_MODULES=
|
|
203
|
+
|
|
186
204
|
# VITE_DEVELOPER_GUIDE_URL=
|
|
187
205
|
# VITE_API_EXAMPLES_URL=
|
|
188
206
|
# VITE_MCP_INTEGRATION_GUIDE_URL=
|
|
@@ -241,6 +241,17 @@ permissions:
|
|
|
241
241
|
- name: "group:read"
|
|
242
242
|
roles: ["aifabrix-platform-admin", "aifabrix-security-admin", "aifabrix-compliance-admin", "aifabrix-observer"]
|
|
243
243
|
description: "Read group information"
|
|
244
|
+
|
|
245
|
+
# Administration workspace (Dataplane UI /api/v1/admin/* shell)
|
|
246
|
+
- name: "administration:read"
|
|
247
|
+
roles:
|
|
248
|
+
[
|
|
249
|
+
"aifabrix-platform-admin",
|
|
250
|
+
"aifabrix-security-admin",
|
|
251
|
+
"aifabrix-deployment-admin",
|
|
252
|
+
"aifabrix-compliance-admin",
|
|
253
|
+
]
|
|
254
|
+
description: "Access Administration workspace (environment settings, diagnostics, secrets navigation)"
|
|
244
255
|
|
|
245
256
|
# OpenAPI / MCP spec bundle (mounted specs under /api/v1/specs)
|
|
246
257
|
- name: "spec:read"
|
|
@@ -59,7 +59,7 @@ build:
|
|
|
59
59
|
dockerfile: builder/miso-controller/Dockerfile # Dockerfile name (relative to project root)
|
|
60
60
|
envOutputPath: ../../packages/miso-controller/.env # Copy .env to repo root for local dev (relative to builder/) (if null, no .env file is copied) (if empty, .env file is copied to repo root)
|
|
61
61
|
language: typescript # Runtime language for template selection (typescript or python)
|
|
62
|
-
reloadStart: pnpm run
|
|
62
|
+
reloadStart: pnpm --filter @aifabrix/miso-controller run dev:watch:public # Docker --reload: nodemon backend + pre-built UI (no Vite in container)
|
|
63
63
|
|
|
64
64
|
# Repository Configuration (pipeline validate: REPOSITORY_URL_MISMATCH)
|
|
65
65
|
repository:
|
|
@@ -157,6 +157,8 @@ WAIT_FOR_KEYCLOAK=true
|
|
|
157
157
|
# For production: use public domain (e.g., https://miso.example.com)
|
|
158
158
|
# url://public includes front-door path from application.yaml (e.g. /controller).
|
|
159
159
|
MISO_WEB_SERVER_URL=url://public
|
|
160
|
+
# Provider-grant OAuth redirect_uri base (Azure/start-dev also set this explicitly; code falls back to MISO_WEB_SERVER_URL)
|
|
161
|
+
CONTROLLER_BASE_URL=url://public
|
|
160
162
|
MISO_CONTROLLER_URL=url://internal
|
|
161
163
|
MISO_RELATIVE_PATH=url://vdir-public
|
|
162
164
|
ALLOWED_ORIGINS=http://localhost:*,url://host-public,url://host-private,url://dataplane-host-public,url://dataplane-host-private
|
|
@@ -241,6 +243,9 @@ DEPLOYMENT=database
|
|
|
241
243
|
# Encryption Key for Database Secrets
|
|
242
244
|
ENCRYPTION_KEY=kv://miso-controller-secrets-encryptionKeyVault
|
|
243
245
|
|
|
246
|
+
# Comma-separated application keys allowed for POST .../applications/bootstrap (install-time only)
|
|
247
|
+
BOOTSTRAP_APPKEYS=dataplane
|
|
248
|
+
|
|
244
249
|
# Key Vault Integration (for security parameter encryption)
|
|
245
250
|
# Set to true to enable Azure Key Vault for storing security parameters
|
|
246
251
|
# When false (default), uses local AES-256-GCM encryption with ENCRYPTION_KEY
|
|
@@ -116,6 +116,9 @@ services:
|
|
|
116
116
|
- "--providers.docker=true"
|
|
117
117
|
- "--providers.docker.exposedbydefault=false"
|
|
118
118
|
- "--providers.docker.network={{networkName}}"
|
|
119
|
+
{{#if traefikScoped}}
|
|
120
|
+
- "--providers.docker.constraints=Label(`aifabrix.traefik.network`,`{{networkName}}`)"
|
|
121
|
+
{{/if}}
|
|
119
122
|
- "--providers.docker.allowEmptyServices=true"
|
|
120
123
|
- "--log.level=INFO"
|
|
121
124
|
- "--entrypoints.web.address=:80"
|
|
@@ -123,29 +126,30 @@ services:
|
|
|
123
126
|
{{#if traefik.trustForwardedHeaders}}
|
|
124
127
|
- "--entrypoints.web.forwardedHeaders.insecure=true"
|
|
125
128
|
{{/if}}
|
|
126
|
-
{{#if traefik.
|
|
127
|
-
{{
|
|
128
|
-
- "--certificatesstores.{{traefik.certStore}}.defaultcertificate.certfile={{traefik.certFile}}"
|
|
129
|
-
{{/if}}
|
|
130
|
-
{{#if traefik.keyFile}}
|
|
131
|
-
- "--certificatesstores.{{traefik.certStore}}.defaultcertificate.keyfile={{traefik.keyFile}}"
|
|
132
|
-
{{/if}}
|
|
129
|
+
{{#if traefik.tlsDynamicContainerPath}}
|
|
130
|
+
- "--providers.file.filename={{traefik.tlsDynamicContainerPath}}"
|
|
133
131
|
{{/if}}
|
|
134
132
|
ports:
|
|
135
133
|
- "{{traefikHttpPort}}:80"
|
|
136
134
|
- "{{traefikHttpsPort}}:443"
|
|
137
135
|
volumes:
|
|
138
136
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
|
137
|
+
{{#if traefik.tlsDynamicBind}}
|
|
138
|
+
- type: bind
|
|
139
|
+
source: "{{traefik.tlsDynamicBind}}"
|
|
140
|
+
target: "{{traefik.tlsDynamicContainerPath}}"
|
|
141
|
+
read_only: true
|
|
142
|
+
{{/if}}
|
|
139
143
|
{{#if traefik.certFile}}
|
|
140
144
|
- type: bind
|
|
141
145
|
source: "{{traefik.certFile}}"
|
|
142
|
-
target: "{{traefik.
|
|
146
|
+
target: "{{traefik.certContainerPath}}"
|
|
143
147
|
read_only: true
|
|
144
148
|
{{/if}}
|
|
145
149
|
{{#if traefik.keyFile}}
|
|
146
150
|
- type: bind
|
|
147
151
|
source: "{{traefik.keyFile}}"
|
|
148
|
-
target: "{{traefik.
|
|
152
|
+
target: "{{traefik.keyContainerPath}}"
|
|
149
153
|
read_only: true
|
|
150
154
|
{{/if}}
|
|
151
155
|
networks:
|
|
@@ -9,6 +9,9 @@ services:
|
|
|
9
9
|
{{#if traefik.enabled}}
|
|
10
10
|
labels:
|
|
11
11
|
- "traefik.enable=true"
|
|
12
|
+
{{#if traefikScoped}}
|
|
13
|
+
- "aifabrix.traefik.network={{networkName}}"
|
|
14
|
+
{{/if}}
|
|
12
15
|
- "traefik.http.routers.{{app.key}}.rule=Host(`{{traefik.host}}`) && PathPrefix(`{{traefik.path}}`)"
|
|
13
16
|
{{#if traefik.tls}}
|
|
14
17
|
- "traefik.http.routers.{{app.key}}.entrypoints=websecure"
|
|
@@ -9,6 +9,9 @@ services:
|
|
|
9
9
|
{{#if traefik.enabled}}
|
|
10
10
|
labels:
|
|
11
11
|
- "traefik.enable=true"
|
|
12
|
+
{{#if traefikScoped}}
|
|
13
|
+
- "aifabrix.traefik.network={{networkName}}"
|
|
14
|
+
{{/if}}
|
|
12
15
|
- "traefik.http.routers.{{app.key}}.rule=Host(`{{traefik.host}}`) && PathPrefix(`{{traefik.path}}`)"
|
|
13
16
|
{{#if traefik.tls}}
|
|
14
17
|
- "traefik.http.routers.{{app.key}}.entrypoints=websecure"
|