@aifabrix/builder 2.61.2 → 2.63.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.
- package/lib/api/applications.api.js +22 -2
- package/lib/api/bootstrap-snapshot.api.js +115 -0
- package/lib/api/onboarding-status.api.js +117 -0
- package/lib/app/deploy.js +14 -5
- package/lib/app/register.js +24 -2
- package/lib/app/run-env-compose.js +44 -8
- package/lib/app/run-env-recovery.js +1 -0
- package/lib/app/run-helpers.js +1 -1
- package/lib/app/run.js +6 -4
- package/lib/build/standard-docker-build.js +4 -0
- package/lib/channels/channel-artifact-generator.js +13 -3
- package/lib/cli/infra-guided-footers.js +10 -8
- package/lib/cli/infra-guided.js +60 -12
- package/lib/cli/setup-infra-up-platform-action.js +14 -1
- package/lib/cli/setup-platform.js +20 -3
- package/lib/commands/auth-config.js +2 -0
- package/lib/commands/login.js +4 -0
- package/lib/commands/repair-datasource-apply.js +3 -1
- package/lib/commands/repair-datasource-legacy.js +94 -0
- package/lib/commands/setup-image-refresh.js +26 -0
- package/lib/commands/setup-modes.js +9 -2
- package/lib/commands/setup-onboarding-readiness.js +83 -0
- package/lib/commands/setup-prompts-platform-mode.js +34 -0
- package/lib/commands/setup.js +35 -10
- package/lib/commands/up-builder-api.js +24 -8
- package/lib/commands/up-common.js +16 -1
- package/lib/commands/up-dataplane-bootstrap.js +149 -16
- package/lib/commands/up-dataplane-credentials.js +21 -0
- package/lib/commands/up-dataplane.js +26 -34
- package/lib/commands/up-integration-server.js +112 -0
- package/lib/commands/wizard-config-normalizer.js +5 -1
- package/lib/core/admin-secrets-env-overlay.js +8 -3
- package/lib/core/environment-access-policy.js +121 -0
- package/lib/core/environment-mode-policy.js +177 -0
- package/lib/core/local-env-overrides.js +149 -0
- package/lib/core/secrets-ensure.js +4 -2
- package/lib/core/secrets-env-content.js +12 -9
- package/lib/core/secrets-env-write.js +1 -1
- package/lib/datasource/capability/capability-manifest.js +1 -0
- package/lib/deployment/installation/index.js +14 -1
- package/lib/deployment/installation/infra-catalog.js +3 -0
- package/lib/deployment/installation/local-environment-stage.js +285 -0
- package/lib/deployment/installation/local-installation.js +76 -0
- package/lib/deployment/installation/stage-input.js +5 -1
- package/lib/generator/builders.js +3 -1
- package/lib/generator/index.js +32 -9
- package/lib/generator/split-variables.js +2 -2
- package/lib/parameters/database-adoption.js +125 -0
- package/lib/parameters/physical-database-naming.js +136 -0
- package/lib/parameters/physical-secret-name.js +194 -0
- package/lib/parameters/rsa-secret-values.js +165 -0
- package/lib/programmatic/run-channel-install-artifacts.js +28 -2
- package/lib/programmatic/run-channel-package.js +3 -1
- package/lib/programmatic/workspace-context.js +37 -2
- package/lib/schema/infra.parameter.yaml +61 -27
- package/lib/schema/infrastructure-schema.json +213 -120
- package/lib/utils/compose-generate-docker-compose.js +2 -2
- package/lib/utils/compose-generator.js +4 -7
- package/lib/utils/compose-miso-env.js +2 -2
- package/lib/utils/compose-traefik-ingress-base.js +3 -4
- package/lib/utils/dataplane-dimension-abac-setting.js +120 -0
- package/lib/utils/declarative-url-ports.js +3 -0
- package/lib/utils/env-environment-file-paths.js +105 -0
- package/lib/utils/environment-scoped-resources.js +48 -14
- package/lib/utils/log-redaction.js +4 -1
- package/lib/utils/paths-system-builder-keys.js +7 -1
- package/lib/utils/paths.js +7 -2
- package/lib/utils/postgres-platform-bootstrap.js +35 -1
- package/lib/utils/redis-env-scope.js +21 -1
- package/lib/utils/registry-auth-sources.js +154 -0
- package/lib/utils/registry-credentials.js +131 -14
- package/lib/utils/secrets-generator.js +30 -21
- package/lib/utils/secrets-helpers.js +5 -7
- package/lib/utils/secrets-kv-scope.js +127 -28
- package/lib/utils/secrets-materialize-local.js +9 -0
- package/lib/utils/secrets-missing-error.js +35 -1
- package/lib/utils/token-manager.js +14 -8
- package/lib/utils/url-public-path-prefix.js +6 -10
- package/package.json +4 -2
- package/templates/README.md +6 -0
- package/templates/applications/builder-api/application.yaml +1 -1
- package/templates/applications/dataplane/application.yaml +2 -2
- package/templates/applications/dataplane/env.template +1 -1
- package/templates/applications/integration-server/application.yaml +91 -0
- package/templates/applications/integration-server/env.template +215 -0
- package/templates/applications/mirrored-templates.json +52 -0
- package/templates/applications/miso-controller/application.yaml +43 -1
- package/templates/applications/miso-controller/env.template +45 -39
- package/templates/marketplace/createUiDefinition.json +52 -4
- package/templates/marketplace/main.json +85 -7
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
# Integration Server — INTEGRATOR installation
|
|
2
|
+
# =============================================================================
|
|
3
|
+
# A single managed container giving developers SSH-only access, with no Docker
|
|
4
|
+
# execution inside it. Identity and configuration are pushed in by Miso; secrets
|
|
5
|
+
# come from the secret store, not from this container's filesystem.
|
|
6
|
+
#
|
|
7
|
+
# The enterprise installation is a different manifest with its own template —
|
|
8
|
+
# builder/builder-server/env.template. Same code, different deployment; each
|
|
9
|
+
# template lists only the variables its own installation reads.
|
|
10
|
+
#
|
|
11
|
+
# Conventions (as used across AI Fabrix apps):
|
|
12
|
+
# kv://name secret resolved from the secret store — never a literal here
|
|
13
|
+
# url://name URL derived by Builder from the manifest
|
|
14
|
+
# ${VAR} value supplied per environment
|
|
15
|
+
|
|
16
|
+
# =============================================================================
|
|
17
|
+
# CORE
|
|
18
|
+
# =============================================================================
|
|
19
|
+
|
|
20
|
+
# HTTP listen port (main.ts). Default: 3006
|
|
21
|
+
PORT=3006
|
|
22
|
+
|
|
23
|
+
# SQLite DB, audit log and managed state.
|
|
24
|
+
#
|
|
25
|
+
# A subdirectory of the mount, not its root: this is a separate installation with its
|
|
26
|
+
# own database, users and identity model. On a dev host running both side by side the
|
|
27
|
+
# bind mount is shared, and pointing both at the root would have each read the other's
|
|
28
|
+
# database — which fails closed, because the recorded server type would not match.
|
|
29
|
+
DATA_DIR=/mnt/data/integrator
|
|
30
|
+
|
|
31
|
+
# Installation type (config/server-type.ts), resolved once at startup and then
|
|
32
|
+
# persisted in builder.db. This installation IS an integrator, so it is set
|
|
33
|
+
# explicitly: unset would mean enterprise. A populated installation cannot switch
|
|
34
|
+
# type on a restart — that needs a migration.
|
|
35
|
+
BUILDER_SERVER_TYPE=integrator
|
|
36
|
+
|
|
37
|
+
# Set false or 0 to disable DATA_DIR/audit.log (audit.service.ts). Default: true
|
|
38
|
+
# AUDIT_LOG_ENABLED=true
|
|
39
|
+
|
|
40
|
+
# =============================================================================
|
|
41
|
+
# SECRETS
|
|
42
|
+
# =============================================================================
|
|
43
|
+
|
|
44
|
+
# AES-GCM key for the secrets API, as a value from the secret store — this
|
|
45
|
+
# installation has no host filesystem to mount a key file from.
|
|
46
|
+
# 64 hex characters or 32 bytes.
|
|
47
|
+
#
|
|
48
|
+
# Generate a NEW key when installing this server. Never point this at the platform's
|
|
49
|
+
# own key — Miso's, or the enterprise Builder Server's ENCRYPTION_KEY_PATH file.
|
|
50
|
+
# Installing a customer's integrator with the platform key would let every developer
|
|
51
|
+
# with a shell in that container decrypt every other installation's secrets, because
|
|
52
|
+
# the same key is handed to each of them in their config.yaml. One installation, one
|
|
53
|
+
# key: the blast radius of a leaked key is then that installation and nothing else.
|
|
54
|
+
#
|
|
55
|
+
# openssl rand -hex 32
|
|
56
|
+
#
|
|
57
|
+
# The name follows the catalog's own `{appKey}-secrets-encryptionKeyVault` convention,
|
|
58
|
+
# so it is this application's app-scoped secret and nothing else's. The earlier name said
|
|
59
|
+
# `builder-server`: that is another application's key, and reading it from here would be
|
|
60
|
+
# a cross-application read that only works by being deliberately shared — which is the
|
|
61
|
+
# hole this avoids. Register it at install:
|
|
62
|
+
#
|
|
63
|
+
# aifabrix secret set integration-server-secrets-encryptionKeyVault "$(openssl rand -hex 32)"
|
|
64
|
+
#
|
|
65
|
+
# The same key is written into every managed developer's config.yaml as
|
|
66
|
+
# `secrets-encryption`, so the CLI can decrypt what this server encrypted. Those two
|
|
67
|
+
# are one value from one source by construction — see account-ops-worker.ts, which
|
|
68
|
+
# resolves it from this variable at the moment it writes the file. Rotating it means
|
|
69
|
+
# re-running configuration sync so the homes are rewritten; values already stored in
|
|
70
|
+
# the secrets table are encrypted with the old key and do not survive a rotation.
|
|
71
|
+
ENCRYPTION_KEY=kv://integration-server-secrets-encryptionKeyVault
|
|
72
|
+
|
|
73
|
+
# =============================================================================
|
|
74
|
+
# MISO
|
|
75
|
+
# =============================================================================
|
|
76
|
+
# Where identity and configuration come from. Builder Server knows Miso only: it
|
|
77
|
+
# never configures or verifies the identity provider — miso-client owns that.
|
|
78
|
+
# /health carries a `miso` check on this installation, and fails without these.
|
|
79
|
+
|
|
80
|
+
# Server-to-controller API calls on the environment network.
|
|
81
|
+
MISO_CONTROLLER_URL=url://miso-controller-internal
|
|
82
|
+
|
|
83
|
+
# Application client credentials, used to obtain an application token and to verify
|
|
84
|
+
# the client tokens on POST /api/integrations/miso/*.
|
|
85
|
+
MISO_CLIENTID=kv://integration-server-client-idKeyVault
|
|
86
|
+
MISO_CLIENTSECRET=kv://integration-server-client-secretKeyVault
|
|
87
|
+
|
|
88
|
+
# Reachable addresses reported to Miso after the HTTP listener starts. Miso uses the
|
|
89
|
+
# internal address for user/configuration delivery and exposes the public address in
|
|
90
|
+
# application status. Keep these derived from the manifest, like Dataplane's status
|
|
91
|
+
# registration; a typed hostname or port drifts in local developer environments.
|
|
92
|
+
INTEGRATION_SERVER_WEB_SERVER_URL=url://public
|
|
93
|
+
INTEGRATION_SERVER_INTERNAL_URL=url://internal
|
|
94
|
+
|
|
95
|
+
# The `environment` value written into each developer's ~/.aifabrix/config.yaml.
|
|
96
|
+
# `controller` and `codingPlatform` are pushed by Miso; this one is the
|
|
97
|
+
# installation's own deployment configuration and is never taken from the request.
|
|
98
|
+
MANAGED_CONFIG_ENVIRONMENT=dev
|
|
99
|
+
MISO_ENVIRONMENT=dev
|
|
100
|
+
|
|
101
|
+
# =============================================================================
|
|
102
|
+
# DEVELOPER ACCESS — SSH
|
|
103
|
+
# =============================================================================
|
|
104
|
+
|
|
105
|
+
# SSH endpoint returned to enrolling developers by POST /api/bootstrap/ssh.
|
|
106
|
+
#
|
|
107
|
+
# This is what the client is told to connect to, so it must be the address that is
|
|
108
|
+
# actually reachable, not the container's own view. Both are derived rather than typed:
|
|
109
|
+
# the host from the front door, and the port from the `ssh` entry in the manifest's
|
|
110
|
+
# `additionalPorts` (Builder publishes it as SSH_PUBLIC_PORT = hostPort + developerId *
|
|
111
|
+
# 100). Typing either by hand drifts from the manifest and advertises an endpoint
|
|
112
|
+
# nothing is listening on.
|
|
113
|
+
INTEGRATOR_SSH_HOST=url://public
|
|
114
|
+
INTEGRATOR_SSH_PORT=${SSH_PUBLIC_PORT}
|
|
115
|
+
|
|
116
|
+
# Where sshd reads each developer's authorized keys from.
|
|
117
|
+
#
|
|
118
|
+
# State, not configuration: it records which key each developer enrolled. Kept in the
|
|
119
|
+
# image it was destroyed by every redeploy, and although reconciliation rebuilds it from
|
|
120
|
+
# the database at startup, a developer reconnecting before that finishes is refused.
|
|
121
|
+
# On the volume the file is already correct when sshd starts.
|
|
122
|
+
#
|
|
123
|
+
# Root-owned, inside a directory developers cannot write to.
|
|
124
|
+
# Default: ${DATA_DIR}/authorized_keys.d
|
|
125
|
+
# AUTHORIZED_KEYS_DIR=/mnt/data/integrator/authorized_keys.d
|
|
126
|
+
|
|
127
|
+
# Root of the managed developer home directories.
|
|
128
|
+
#
|
|
129
|
+
# Must be on the persistent volume. A managed home is not scratch space: it holds the
|
|
130
|
+
# developer's cloned repositories and build trees, the credentials their agent uses, the
|
|
131
|
+
# managed config.yaml and the client certificate this server issued. An agent run takes
|
|
132
|
+
# hours and outlives any single connection, so the work has to survive a redeploy.
|
|
133
|
+
#
|
|
134
|
+
# Inside the image it did not. Every redeploy destroyed the homes, which silently revoked
|
|
135
|
+
# access as well as losing the work: the Linux accounts and their authorized_keys went
|
|
136
|
+
# with them, while the projection in builder.db still said the developer was entitled.
|
|
137
|
+
#
|
|
138
|
+
# The entrypoint creates this root 755 and grants traverse on the path leading to it;
|
|
139
|
+
# each developer's own home inside is 700 and owned by them.
|
|
140
|
+
# Default: ${DATA_DIR}/home
|
|
141
|
+
# DEVELOPER_HOME_ROOT=/mnt/data/integrator/home
|
|
142
|
+
|
|
143
|
+
# Restores keys even though the projection has gone backwards.
|
|
144
|
+
#
|
|
145
|
+
# This installation records the highest Miso revision it has ever seen, outside the
|
|
146
|
+
# database, so restoring builder.db from before a revocation is detectable: the mark is
|
|
147
|
+
# higher than anything the projection now holds. When that happens the installation fails
|
|
148
|
+
# closed — every developer is reconciled to no keys until Miso re-synchronises a revision
|
|
149
|
+
# at least as high as the one already seen. Homes are retained and the server still boots,
|
|
150
|
+
# so it can be repaired.
|
|
151
|
+
#
|
|
152
|
+
# A database rebuilt deliberately looks identical to one restored from before a
|
|
153
|
+
# revocation, so this is the way past it. Set it only when you know the rebuild was
|
|
154
|
+
# intended; it is logged when used. Only the exact value `true` is accepted.
|
|
155
|
+
# ONBOARDING_ALLOW_BACKWARDS_PROJECTION=true
|
|
156
|
+
|
|
157
|
+
# =============================================================================
|
|
158
|
+
# CLIENT DOWNLOAD
|
|
159
|
+
# =============================================================================
|
|
160
|
+
# What GET /client/<target>.zip writes into the aifabrix.json beside the executable.
|
|
161
|
+
#
|
|
162
|
+
# The executable itself is identical for every installation — SmartScreen builds
|
|
163
|
+
# reputation against a file hash, so a per-customer binary would never accumulate any.
|
|
164
|
+
# The customer scope travels in this small file instead.
|
|
165
|
+
|
|
166
|
+
# Platform origin the client posts its SSH key to. Only the origin is used; the client
|
|
167
|
+
# appends its own path, so a value carrying /server would double it.
|
|
168
|
+
CLIENT_PLATFORM_URL=url://public
|
|
169
|
+
|
|
170
|
+
# Controller the client signs in against, as a browser reaches it.
|
|
171
|
+
CLIENT_CONTROLLER_URL=url://miso-controller-public
|
|
172
|
+
|
|
173
|
+
# Shown while onboarding, e.g. "Acme Corp". Optional.
|
|
174
|
+
# CLIENT_DISPLAY_NAME=
|
|
175
|
+
|
|
176
|
+
# How this installation is reached: `ssh` or `wss`. Exactly one is served.
|
|
177
|
+
#
|
|
178
|
+
# ssh Developers reach sshd over a private network overlay, which is the only route to
|
|
179
|
+
# the port — this installation must not publish 22 publicly. Deactivating a person
|
|
180
|
+
# in the overlay removes their route, and that is what ends access, including for
|
|
181
|
+
# sessions already open. The tunnel endpoint does not exist.
|
|
182
|
+
#
|
|
183
|
+
# wss There is no overlay: the session travels inside a WebSocket on the published
|
|
184
|
+
# HTTPS port, and sshd listens on loopback only, so the tunnel is the only way in.
|
|
185
|
+
# This server does the authorizing — a short-lived token at the open, refreshed
|
|
186
|
+
# over the tunnel while the developer works, and dropped on idle or revocation.
|
|
187
|
+
#
|
|
188
|
+
# Not both. Publishing 22 alongside the tunnel would make the overlay stop being a
|
|
189
|
+
# control, and running both against one sshd means "does this person still have access?"
|
|
190
|
+
# depends on which door they used — with the door that fails open deciding.
|
|
191
|
+
#
|
|
192
|
+
# Stated by the server because the server knows how it is published; the client reads it
|
|
193
|
+
# from aifabrix.json and never has to guess. Guessing wrong fails as a timeout, which is
|
|
194
|
+
# the least diagnosable failure there is.
|
|
195
|
+
CLIENT_TRANSPORT=wss
|
|
196
|
+
|
|
197
|
+
# =============================================================================
|
|
198
|
+
# FRONT DOOR
|
|
199
|
+
# =============================================================================
|
|
200
|
+
# The application is published whole at its virtual directory, the same way
|
|
201
|
+
# miso-controller serves /miso and dataplane serves /data. What is reachable from
|
|
202
|
+
# outside is decided by `frontDoorRouting` in application.yaml and by the ingress.
|
|
203
|
+
|
|
204
|
+
# Injected by Builder from `frontDoorRouting.pattern` in application.yaml; listed
|
|
205
|
+
# because the app reads it. Routes are served unprefixed — the ingress strips the
|
|
206
|
+
# prefix before the request arrives — so this only builds URLs handed to clients.
|
|
207
|
+
# BASE_PATH=/integrator
|
|
208
|
+
# X_FORWARDED_PREFIX=/integrator
|
|
209
|
+
|
|
210
|
+
# =============================================================================
|
|
211
|
+
# TESTING
|
|
212
|
+
# =============================================================================
|
|
213
|
+
|
|
214
|
+
# Enables POST /api/dev/e2e-reset. Never set in production.
|
|
215
|
+
# E2E_RESET_SECRET=
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"mirrors": [
|
|
3
|
+
{
|
|
4
|
+
"path": "templates/applications/miso-controller/env.template",
|
|
5
|
+
"master": {
|
|
6
|
+
"repo": "aifabrix-miso",
|
|
7
|
+
"path": "builder/miso-controller/env.template"
|
|
8
|
+
},
|
|
9
|
+
"sha256": "9b74b94d5d6e7457d22cfe213f2ec9c3dceb75c540df3953aba3b51c35df05c7"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"path": "templates/applications/builder-api/env.template",
|
|
13
|
+
"master": {
|
|
14
|
+
"repo": "aifabrix-miso",
|
|
15
|
+
"path": "builder/builder-api/env.template"
|
|
16
|
+
},
|
|
17
|
+
"sha256": "1a6b013aa0a34b72c5a0be3dd714bd718a8adb981bcbb9de3d5370e2dd33786c"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"path": "templates/applications/keycloak/env.template",
|
|
21
|
+
"master": {
|
|
22
|
+
"repo": "aifabrix-miso",
|
|
23
|
+
"path": "builder/keycloak/env.template"
|
|
24
|
+
},
|
|
25
|
+
"sha256": "72de7dc5345b842bc20566f8d5105b6541e8185dff157e9af2d37db9590b01fb"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"path": "templates/applications/dataplane/env.template",
|
|
29
|
+
"master": {
|
|
30
|
+
"repo": "aifabrix-dataplane",
|
|
31
|
+
"path": "builder/dataplane/env.template"
|
|
32
|
+
},
|
|
33
|
+
"sha256": "d35eda839464a65ebd018064ea7c2d292654891b41d1f56862b052720a78a3cc"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"path": "templates/applications/integration-server/env.template",
|
|
37
|
+
"master": {
|
|
38
|
+
"repo": "aifabrix-setup",
|
|
39
|
+
"path": "builder/integration-server/env.template"
|
|
40
|
+
},
|
|
41
|
+
"sha256": "a80584bcf6880937c43557a71ca1c423104ab4e15badf4bb14a0a9a9dcaa01d6"
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"path": "templates/applications/integration-server/application.yaml",
|
|
45
|
+
"master": {
|
|
46
|
+
"repo": "aifabrix-setup",
|
|
47
|
+
"path": "builder/integration-server/application.yaml"
|
|
48
|
+
},
|
|
49
|
+
"sha256": "04cbfdb9a012393a5e97a1dda1b8f304e97c9532339f045e5912cd250a26a179"
|
|
50
|
+
}
|
|
51
|
+
]
|
|
52
|
+
}
|
|
@@ -4,7 +4,7 @@ app:
|
|
|
4
4
|
displayName: 'Miso Controller'
|
|
5
5
|
description: 'Miso is the AI Fabrix in-tenant controller and portal layer for securely operating enterprise AI apps inside a customer’s Azure tenant. It provides Entra ID SSO, RBAC, audit logs, environment/app configuration via schemas, and safe secret handling via Key Vault references—ensuring governance, traceability, and predictable UX across portal, SDK, and CLI.'
|
|
6
6
|
type: webapp
|
|
7
|
-
version: '2.0.
|
|
7
|
+
version: '2.0.91'
|
|
8
8
|
# Image Configuration
|
|
9
9
|
# Set tag to match your build (e.g. aifabrix build miso-controller -t 1.0.0 then tag: 1.0.0)
|
|
10
10
|
# Registry is required so the controller can pull the image (avoids "docker: not found" on the controller host).
|
|
@@ -92,6 +92,8 @@ platforms:
|
|
|
92
92
|
configuration:
|
|
93
93
|
- name: LOCATION
|
|
94
94
|
value: 'swedencentral'
|
|
95
|
+
- name: CLOUD_VENDOR
|
|
96
|
+
value: 'azure'
|
|
95
97
|
- name: MORI_BASE_URL
|
|
96
98
|
value: 'https://platform.aifabrix.dev/mori'
|
|
97
99
|
|
|
@@ -133,6 +135,42 @@ platforms:
|
|
|
133
135
|
value: 'swedencentral'
|
|
134
136
|
- name: MORI_BASE_URL
|
|
135
137
|
value: 'url://mori-controller-internal'
|
|
138
|
+
# Declared, because the fallback infers it and infers it wrongly here:
|
|
139
|
+
# `resolveCloudVendor` returns 'azure' whenever AZURE_SUBSCRIPTION_ID is set, and every
|
|
140
|
+
# developer box has it from the shared development service principal. A local stack was
|
|
141
|
+
# therefore treated as an Azure installation, `verifyInstallationBinding` took
|
|
142
|
+
# `verifyAzureBinding`, and that demands a directory tenant AND a resource group. A
|
|
143
|
+
# developer box has no resource group, so the binding refused with
|
|
144
|
+
# "Azure licence binding requires the tenant ID and resource group." The licence guard
|
|
145
|
+
# turns that into HTTP 403 on every guarded operation — including
|
|
146
|
+
# `app rotate-secret`, which is the one command that recovers a re-minted credential.
|
|
147
|
+
#
|
|
148
|
+
# Measured on dev01 2026-10-04: the dataplane could not start (its client secret had been
|
|
149
|
+
# re-minted, plan 264.0) and the recovery command was itself refused. The two defects
|
|
150
|
+
# compose into a dead end that no developer can get out of.
|
|
151
|
+
- name: CLOUD_VENDOR
|
|
152
|
+
value: 'docker'
|
|
153
|
+
# The one installation name the licence binds to (plan 266.0). Sent to Mori as
|
|
154
|
+
# `resourceGroupName`; docker requires exactly `local-dev{NN}` or registration refuses.
|
|
155
|
+
# Only on this entry — the Azure platforms below must not be `local-*` and keep SERVICE_NAME.
|
|
156
|
+
- name: MISO_INSTALLATION_NAME
|
|
157
|
+
value: 'local-${DEV_USERNAME}'
|
|
158
|
+
# Plan 262.0: a local stack onboards as SaaS, so the full activation cycle is reachable here.
|
|
159
|
+
#
|
|
160
|
+
# `requiresTenantActivationWizard()` is `isAzureDeployment() || isAzureMockDeployment() ||
|
|
161
|
+
# isSaasDirectOnboarding()`. With `customerOwned` all three are false on a dev box, so the
|
|
162
|
+
# wizard never ran — and the wizard is what collects the customer directory tenant and
|
|
163
|
+
# records consent. Without it the tenant is only ever read from the environment, which is the
|
|
164
|
+
# in-tenant assumption and wrong for SaaS.
|
|
165
|
+
#
|
|
166
|
+
# This is the ONBOARDING axis, deliberately not the DEPLOYMENT axis. `DEPLOYMENT` stays
|
|
167
|
+
# `database` (DB-only; no Docker, no Azure), which `isLocalOnboardingDeployment()` already
|
|
168
|
+
# treats as local. `DEPLOYMENT=local` means Miso orchestrating Docker itself, which is not
|
|
169
|
+
# ready and is not what a dev box runs — keying this on it would never fire.
|
|
170
|
+
#
|
|
171
|
+
# Only on this entry: the Azure platforms below stay `customerOwned`.
|
|
172
|
+
- name: ONBOARDING_MODE
|
|
173
|
+
value: 'saasDirect'
|
|
136
174
|
|
|
137
175
|
- resource: aifabrix-int-{platform}
|
|
138
176
|
name: aifabrix
|
|
@@ -140,6 +178,8 @@ platforms:
|
|
|
140
178
|
configuration:
|
|
141
179
|
- name: LOCATION
|
|
142
180
|
value: 'swedencentral'
|
|
181
|
+
- name: CLOUD_VENDOR
|
|
182
|
+
value: 'azure'
|
|
143
183
|
# Mori the installation talks to. Previously `kv://mori-controller-web-server-url`, which
|
|
144
184
|
# resolved to whatever the *application record* held — on aifabrix-ce-16 that was
|
|
145
185
|
# `http://localhost:3004`, because an Azure deployment never overwrites a stale URL, so every
|
|
@@ -154,6 +194,8 @@ platforms:
|
|
|
154
194
|
configuration:
|
|
155
195
|
- name: LOCATION
|
|
156
196
|
value: 'swedencentral'
|
|
197
|
+
- name: CLOUD_VENDOR
|
|
198
|
+
value: 'azure'
|
|
157
199
|
# The App Service hostname, not the Front Door one, because only this is derivable.
|
|
158
200
|
#
|
|
159
201
|
# The Front Door endpoint carries an Azure-generated suffix (`fzahchbachcfbgaj`) that no
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
# [saasDirect] eSystems-hosted SaaS activation (four-step wizard)
|
|
12
12
|
# [customerOwned] Azure-in-tenant activation (existing TA wizard)
|
|
13
13
|
#
|
|
14
|
-
# ONBOARDING_MODE selects the onboarding step list.
|
|
14
|
+
# ONBOARDING_MODE selects the onboarding step list. saasDirect also enables the local wizard.
|
|
15
|
+
# It is orthogonal to DEPLOYMENT,
|
|
15
16
|
# which selects Azure SDK vs local execution. There is NO fifth DEPLOYMENT value:
|
|
16
17
|
# eSystems-hosted SaaS still runs DEPLOYMENT=azure. See the DEPLOYMENT TYPE and
|
|
17
18
|
# ONBOARDING MODE sections below.
|
|
@@ -40,14 +41,28 @@
|
|
|
40
41
|
# Optional custom controller key for onboarding (default: miso-controller)
|
|
41
42
|
ONBOARDING_CONTROLLER_KEY=miso-controller
|
|
42
43
|
|
|
43
|
-
# Infrastructure/service name used by controller/onboarding flows
|
|
44
|
-
INFRASTRUCTURE_NAME=${DEV_USERNAME}.${DEV_ROOT_DOMAIN}
|
|
45
|
-
|
|
46
44
|
# Azure region (required for first-time onboarding: controller record / provisioning metadata).
|
|
47
45
|
# Use the region you deploy to (e.g. westeurope, eastus, northeurope). Single env name: LOCATION.
|
|
48
46
|
# Resolved from the platform match; see `platforms` in application.yaml.
|
|
49
47
|
LOCATION={LOCATION}
|
|
50
48
|
|
|
49
|
+
# Which kind of installation this is, for licence binding. Declared, never inferred.
|
|
50
|
+
#
|
|
51
|
+
# `resolveCloudVendor` falls back to "AZURE_SUBSCRIPTION_ID is set, therefore Azure", and every
|
|
52
|
+
# developer box has that variable from the shared development service principal. A local stack was
|
|
53
|
+
# then bound as an Azure installation, which requires a directory tenant AND a resource group; a
|
|
54
|
+
# developer box has no resource group, so licence binding refused and the licence guard returned
|
|
55
|
+
# HTTP 403 for every guarded operation — `app rotate-secret` included, which is precisely the
|
|
56
|
+
# command needed to recover a credential the controller had re-minted.
|
|
57
|
+
#
|
|
58
|
+
# Resolved from the platform match: `docker` locally, `azure` for every real resource group.
|
|
59
|
+
CLOUD_VENDOR={CLOUD_VENDOR}
|
|
60
|
+
|
|
61
|
+
# The one installation name the licence binds to (plan 266.0). Not a label: Miso sends it to Mori
|
|
62
|
+
# as `resourceGroupName`, and docker requires exactly `local-dev{NN}` or registration refuses.
|
|
63
|
+
# `${DEV_USERNAME}` yields `local-dev03`. It is the only name a local install declares.
|
|
64
|
+
MISO_INSTALLATION_NAME=local-${DEV_USERNAME}
|
|
65
|
+
|
|
51
66
|
# Required for admin user creation during onboarding
|
|
52
67
|
# Password for the initial administrator user (username: admin)
|
|
53
68
|
ONBOARDING_ADMIN_PASSWORD=kv://miso-controller-admin-passwordKeyVault
|
|
@@ -151,7 +166,6 @@ KEYCLOAK_CLIENT_ID=kv://keycloak-client-idKeyVault
|
|
|
151
166
|
KEYCLOAK_CLIENT_SECRET=kv://keycloak-client-secretKeyVault
|
|
152
167
|
KEYCLOAK_ADMIN_USERNAME=admin
|
|
153
168
|
KEYCLOAK_ADMIN_PASSWORD=kv://keycloak-admin-passwordKeyVault
|
|
154
|
-
KEYCLOAK_PUBLIC_KEY=
|
|
155
169
|
KEYCLOAK_VERIFY_AUDIENCE=false
|
|
156
170
|
KEYCLOAK_TOKEN_TIMEOUT=5000
|
|
157
171
|
KEYCLOAK_DEFAULT_PASSWORD=kv://keycloak-default-passwordKeyVault
|
|
@@ -291,12 +305,12 @@ DEPLOYMENT=database
|
|
|
291
305
|
#
|
|
292
306
|
# customerOwned = Azure-in-tenant wizard
|
|
293
307
|
# consent → identity → size → access-model → endpoint → validation
|
|
294
|
-
# saasDirect =
|
|
295
|
-
# consent →
|
|
308
|
+
# saasDirect = hosted or local Docker guided activation
|
|
309
|
+
# consent → endpoint (local browser URL or optional domain) → identity → review
|
|
296
310
|
# Omits size, access-model, customer deploy and platform-apps.
|
|
297
311
|
#
|
|
298
312
|
# Unset defaults to customerOwned. An unknown value fails closed with HTTP 400.
|
|
299
|
-
ONBOARDING_MODE=
|
|
313
|
+
ONBOARDING_MODE=saasDirect
|
|
300
314
|
|
|
301
315
|
# Dev/test: show TA Entra "Completing setup" countdown (120s cap). Default false.
|
|
302
316
|
# Exposed to UI via GET /api/v1/tenant/status → showTaCountdown (restart controller after change).
|
|
@@ -311,9 +325,10 @@ SHOW_TA_COUNT_DOWN=false
|
|
|
311
325
|
ENCRYPTION_KEY=kv://miso-controller-secrets-encryptionKeyVault
|
|
312
326
|
|
|
313
327
|
# Comma-separated application keys allowed for POST .../applications/bootstrap (install-time only).
|
|
314
|
-
# Environment placement is a separate controller rule: workloads (dataplane,
|
|
315
|
-
# dev/tst/pro but never `miso`; miso-controller
|
|
316
|
-
|
|
328
|
+
# Environment placement is a separate controller rule: workloads (dataplane, customer apps) install
|
|
329
|
+
# into dev/tst/pro but never `miso`; miso-controller, keycloak, builder-api, and integration-server
|
|
330
|
+
# only into `miso`; mori-controller anywhere.
|
|
331
|
+
BOOTSTRAP_APPKEYS=dataplane,builder-api,integration-server,mori-controller
|
|
317
332
|
|
|
318
333
|
# Key Vault Integration (for security parameter encryption)
|
|
319
334
|
# Set to true to enable Azure Key Vault for storing security parameters
|
|
@@ -384,6 +399,25 @@ ENTRA_GRAPH_DELEGATED_REDIRECT_URI=url://mori-controller-public/entra-graph-dele
|
|
|
384
399
|
# MORI_API_KEY is per-installation and is minted by POST /marketplace/bootstrap; it is written
|
|
385
400
|
# here by the controller after registration, not provisioned ahead of it.
|
|
386
401
|
MORI_BASE_URL={MORI_BASE_URL}
|
|
402
|
+
|
|
403
|
+
# Mori's policy signing ANCHOR, public half. Verifies the signed enforcement bundle.
|
|
404
|
+
#
|
|
405
|
+
# Mori holds the private half and signs with it; this is the key the bundle is checked against, so
|
|
406
|
+
# an installation that could write it could trust a bundle it signed itself. It is supplied, never
|
|
407
|
+
# generated: on Azure the deployment template writes `mori-policy-public-keyKeyVault` into the
|
|
408
|
+
# installation vault. Locally it is per developer, because each box's Mori holds its own pair.
|
|
409
|
+
#
|
|
410
|
+
# `pnpm license:sign:local` reads it from here so the command needs no arguments. Export the public
|
|
411
|
+
# half from the local Mori once:
|
|
412
|
+
#
|
|
413
|
+
# docker exec aifabrix-dev${NN}-mori-controller node -e "const {createPublicKey}=require('crypto');\
|
|
414
|
+
# process.stdout.write(createPublicKey(process.env.MORI_POLICY_PRIVATE_KEY.replace(/\\n/g,'\n'))\
|
|
415
|
+
# .export({type:'spki',format:'pem'}).toString())"
|
|
416
|
+
#
|
|
417
|
+
# Empty is valid: an installation with no anchor cannot verify a bundle and says so, rather than
|
|
418
|
+
# accepting one signed by any key.
|
|
419
|
+
MORI_POLICY_PUBLIC_KEY=
|
|
420
|
+
MORI_POLICY_KEY_ID={POLICY_KEY_ID}
|
|
387
421
|
MORI_API_KEY=kv://mori-controller-api-keyKeyVault
|
|
388
422
|
# Ask Mori for a container-registry credential when the installation was given none.
|
|
389
423
|
# Off by default: the call has a 30s timeout inside credential resolution, so enabling it on an
|
|
@@ -417,37 +451,9 @@ APPLICATIONINSIGHTS_CONNECTION_STRING=kv://appinsights-connectionStringKeyVault
|
|
|
417
451
|
# OpenTelemetry service name (optional, defaults to miso-controller)
|
|
418
452
|
OTEL_SERVICE_NAME=miso-controller
|
|
419
453
|
|
|
420
|
-
# OpenTelemetry service version (optional, defaults to npm_package_version or 1.0.0)
|
|
421
|
-
OTEL_SERVICE_VERSION=
|
|
422
|
-
|
|
423
454
|
# =============================================================================
|
|
424
455
|
# STORAGE CONFIGURATION
|
|
425
456
|
# =============================================================================
|
|
426
457
|
|
|
427
458
|
# Mount Volume Configuration
|
|
428
459
|
MOUNT_VOLUME=/mnt/data/
|
|
429
|
-
|
|
430
|
-
# Image registry this installation deploys from.
|
|
431
|
-
#
|
|
432
|
-
# `pipeline-validation-flow.helper.ts` resolves the deploy registry as ACR_LOGIN_SERVER first, then
|
|
433
|
-
# the controller record's `configuration.imageRegistry`. Azure installations get the record from the
|
|
434
|
-
# installer, so this stays EMPTY there and behaviour is unchanged. A local installation matches no
|
|
435
|
-
# platform (`allowLocalPlatform` is false for the controller) and has no record, so without this key
|
|
436
|
-
# every `aifabrix deploy` fails with "Image registry is not configured on the controller
|
|
437
|
-
# installation".
|
|
438
|
-
#
|
|
439
|
-
# EMPTY on purpose. The controller record is read AFTER this and overwrites it, so an Azure
|
|
440
|
-
# installation ignores whatever stands here; only a local installation consumes it.
|
|
441
|
-
#
|
|
442
|
-
# This briefly shipped as `aifabrixce16.azurecr.io` — one test installation's registry, committed
|
|
443
|
-
# into the product, so every developer who did not override it would have pushed and pulled
|
|
444
|
-
# against ce-16 without being told. A `{ACR_LOGIN_SERVER}` token was tried instead and is worse
|
|
445
|
-
# still: nothing resolves it without a resource group, so it breaks `aifabrix build` outright
|
|
446
|
-
# ("no platform or environment configuration resolves it for environment dev").
|
|
447
|
-
#
|
|
448
|
-
# Empty is the honest third answer. An Azure installation is unaffected, and a local
|
|
449
|
-
# `aifabrix deploy` fails with "Image registry is not configured on the controller installation",
|
|
450
|
-
# which names the missing thing. Its proper home is the Builder run tooling, which already knows
|
|
451
|
-
# the answer (`aifabrix dev print-registry`) and should inject it per developer rather than have
|
|
452
|
-
# Miso ship one installation's registry to everybody.
|
|
453
|
-
ACR_LOGIN_SERVER=
|
|
@@ -5,19 +5,44 @@
|
|
|
5
5
|
"parameters": {
|
|
6
6
|
"config": {
|
|
7
7
|
"basics": {
|
|
8
|
-
"description": "Choose subscription, resource group, and region above. Under **Application resources Resource group name**, enter your install id (for example `aifabrixpoc02`). That managed resource group name is the naming prefix: resources become `{rg}-miso-asp`, `{rg}-miso-miso`, and so on. Set the Azure **Application name** to the same id when the portal asks. Enter the initial platform administrator password and choose the license below. Container images are pulled with the web apps' managed identities; no registry credential is needed."
|
|
8
|
+
"description": "Choose subscription, resource group, and region above. Under **Application resources Resource group name**, enter your install id (for example `aifabrixpoc02`), using at most 16 lowercase characters. That managed resource group name is the naming prefix: resources become `{rg}-miso-asp`, `{rg}-miso-miso`, and so on. Set the Azure **Application name** to the same id when the portal asks. Enter the initial platform administrator password and choose the license below. Container images are pulled with the web apps' managed identities; no registry credential is needed.",
|
|
9
|
+
"resourceGroup": {
|
|
10
|
+
"constraints": {
|
|
11
|
+
"validations": [
|
|
12
|
+
{
|
|
13
|
+
"regex": "^[a-z][a-z0-9]*(-[a-z0-9]+)*$",
|
|
14
|
+
"message": "Use lowercase letters, digits and single hyphens, starting with a letter. The name becomes the prefix for every resource, and the Key Vault created as {name}-miso-kv must start with a letter and cannot contain consecutive or trailing hyphens."
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"regex": "^.{1,16}$",
|
|
18
|
+
"message": "Use at most 16 characters. The name becomes the prefix for every resource: the Key Vault is created as {name}-miso-kv, and Azure limits a Key Vault name to 24 characters."
|
|
19
|
+
}
|
|
20
|
+
]
|
|
21
|
+
}
|
|
22
|
+
}
|
|
9
23
|
}
|
|
10
24
|
},
|
|
11
25
|
"basics": [
|
|
26
|
+
{
|
|
27
|
+
"name": "adminEmail",
|
|
28
|
+
"type": "Microsoft.Common.TextBox",
|
|
29
|
+
"label": "Administrator email",
|
|
30
|
+
"toolTip": "Email for the initial platform administrator and installation welcome.",
|
|
31
|
+
"constraints": {
|
|
32
|
+
"required": true,
|
|
33
|
+
"regex": "^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$",
|
|
34
|
+
"validationMessage": "Enter a valid email address."
|
|
35
|
+
}
|
|
36
|
+
},
|
|
12
37
|
{
|
|
13
38
|
"name": "adminPassword",
|
|
14
39
|
"type": "Microsoft.Common.PasswordBox",
|
|
15
40
|
"label": "Administrator password",
|
|
16
|
-
"toolTip": "Password for the initial AI Fabrix platform administrator (user admin). PostgreSQL, Keycloak, and encryption secrets are generated automatically and stored in Key Vault
|
|
41
|
+
"toolTip": "Password for the initial AI Fabrix platform administrator (user admin). PostgreSQL, Keycloak, and encryption secrets are generated automatically and stored in Key Vault \u2014 do not reuse this password for infrastructure.",
|
|
17
42
|
"constraints": {
|
|
18
43
|
"required": true,
|
|
19
44
|
"regex": "^.{8,128}$",
|
|
20
|
-
"validationMessage": "Password must be 8
|
|
45
|
+
"validationMessage": "Password must be 8\u2013128 characters."
|
|
21
46
|
},
|
|
22
47
|
"options": {
|
|
23
48
|
"hideConfirmation": false
|
|
@@ -63,6 +88,27 @@
|
|
|
63
88
|
"regex": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$",
|
|
64
89
|
"validationMessage": "Enter the Marketplace subscription ID as a GUID (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)."
|
|
65
90
|
}
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"name": "installationResourceModel",
|
|
94
|
+
"type": "Microsoft.Common.DropDown",
|
|
95
|
+
"label": "Initial infrastructure",
|
|
96
|
+
"toolTip": "Choose Shared for Community or Standard. Choose Dedicated DEV for Enterprise. This provisions infrastructure before licence issuance; it does not grant licence entitlements.",
|
|
97
|
+
"defaultValue": "Shared (Community / Standard)",
|
|
98
|
+
"constraints": {
|
|
99
|
+
"required": "[equals(basics('licenseChoice'), 'SUBSCRIPTION_ID')]",
|
|
100
|
+
"allowedValues": [
|
|
101
|
+
{
|
|
102
|
+
"label": "Shared (Community / Standard)",
|
|
103
|
+
"value": "shared"
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
"label": "Dedicated DEV (Enterprise)",
|
|
107
|
+
"value": "dedicated"
|
|
108
|
+
}
|
|
109
|
+
]
|
|
110
|
+
},
|
|
111
|
+
"visible": "[equals(basics('licenseChoice'), 'SUBSCRIPTION_ID')]"
|
|
66
112
|
}
|
|
67
113
|
],
|
|
68
114
|
"steps": [],
|
|
@@ -71,7 +117,9 @@
|
|
|
71
117
|
"deploymentEnvironment": "evaluation",
|
|
72
118
|
"adminPassword": "[basics('adminPassword')]",
|
|
73
119
|
"licenseEdition": "[if(equals(basics('licenseChoice'), 'SUBSCRIPTION_ID'), basics('licenseSubscriptionKey'), basics('licenseChoice'))]",
|
|
74
|
-
"acrResourceId": "__MARKETPLACE_ACR_RESOURCE_ID__"
|
|
120
|
+
"acrResourceId": "__MARKETPLACE_ACR_RESOURCE_ID__",
|
|
121
|
+
"installationResourceModel": "[if(equals(basics('licenseChoice'), 'SUBSCRIPTION_ID'), basics('installationResourceModel'), if(equals(basics('licenseChoice'), 'ENTERPRISE'), 'dedicated', 'shared'))]",
|
|
122
|
+
"adminEmail": "[basics('adminEmail')]"
|
|
75
123
|
}
|
|
76
124
|
}
|
|
77
125
|
}
|