@aifabrix/builder 2.59.0 → 2.61.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. package/README.md +14 -11
  2. package/docs/README.md +80 -0
  3. package/docs/builder-help/evidence-patterns.json +155 -0
  4. package/docs/builder-help/golden-examples/crm-company.json +30 -0
  5. package/docs/builder-help/golden-examples/crm-deal.json +29 -0
  6. package/docs/builder-help/golden-examples/document-storage-keyed-get.json +85 -0
  7. package/docs/builder-help/golden-examples/document-storage.json +30 -0
  8. package/docs/builder-help/golden-examples/meeting-transcript.json +29 -0
  9. package/docs/builder-help/golden-examples/repository-template.json +29 -0
  10. package/docs/builder-help/golden-examples/service-ticket.json +29 -0
  11. package/docs/builder-help/platform-roles.json +98 -0
  12. package/docs/builder-help/resource-type-catalog.json +402 -0
  13. package/lib/agent-kit/git-identity.js +180 -0
  14. package/lib/agent-kit/setup.js +3 -0
  15. package/lib/agent-kit/start.js +45 -1
  16. package/lib/api/configuration.api.js +131 -0
  17. package/lib/api/role-assistant-test-job.api.js +60 -0
  18. package/lib/api/system-secrets.api.js +72 -0
  19. package/lib/api/work-search.api.js +13 -6
  20. package/lib/app/deploy.js +8 -1
  21. package/lib/app/run-docker-fallback.js +6 -1
  22. package/lib/app/run-helpers.js +2 -1
  23. package/lib/app/run-parameter-sync.js +142 -0
  24. package/lib/app/show-display.js +1 -0
  25. package/lib/app/show-online.js +15 -0
  26. package/lib/build/docker-build-args.js +5 -2
  27. package/lib/build/index.js +3 -2
  28. package/lib/build/standard-docker-build.js +6 -3
  29. package/lib/cli/setup-app.js +19 -1
  30. package/lib/cli/setup-environment.js +156 -0
  31. package/lib/cli/setup-utility.js +24 -1
  32. package/lib/commands/datasource-capability-upsert-cli.js +112 -0
  33. package/lib/commands/datasource-capability.js +4 -2
  34. package/lib/commands/env-secret-context.js +113 -0
  35. package/lib/commands/env-secret-list.js +200 -0
  36. package/lib/commands/env-secret-push-confirm.js +54 -0
  37. package/lib/commands/env-secret-push-run.js +227 -0
  38. package/lib/commands/env-secret-push.js +168 -0
  39. package/lib/commands/repair-datasource-apply.js +2 -0
  40. package/lib/commands/repair-datasource-keyed-document.js +122 -0
  41. package/lib/commands/repair-datasource-run.js +1 -0
  42. package/lib/commands/role-assistant.js +7 -0
  43. package/lib/commands/setup-modes.js +1 -8
  44. package/lib/commands/setup-prompts.js +2 -180
  45. package/lib/commands/verify-operations-skip-e2e.js +25 -1
  46. package/lib/commands/verify-operations-steps.js +13 -1
  47. package/lib/commands/wizard-config-normalizer.js +7 -4
  48. package/lib/commands/wizard-core.js +5 -157
  49. package/lib/commands/wizard-file-saving.js +163 -0
  50. package/lib/core/env-platform-expand.js +97 -0
  51. package/lib/core/secrets-env-content.js +42 -4
  52. package/lib/core/secrets-env-write.js +10 -3
  53. package/lib/core/secrets-load.js +4 -2
  54. package/lib/datasource/binary-documents-validator.js +190 -0
  55. package/lib/datasource/capability/run-capability-upsert.js +202 -0
  56. package/lib/datasource/capability/upsert-ingredients.js +291 -0
  57. package/lib/datasource/capability/upsert-operations.js +138 -0
  58. package/lib/datasource/capability/upsert-test-scaffold.js +189 -0
  59. package/lib/datasource/validate.js +12 -5
  60. package/lib/deployment/installation/azure-infra-stage.js +3 -1
  61. package/lib/deployment/installation/infra-catalog.js +2 -5
  62. package/lib/generator/builders.js +17 -0
  63. package/lib/generator/helpers.js +23 -2
  64. package/lib/generator/index.js +13 -6
  65. package/lib/lifecycle/product-model.js +4 -3
  66. package/lib/lifecycle/report-display.js +3 -2
  67. package/lib/parameters/infra-parameter-catalog.js +1 -1
  68. package/lib/programmatic/builder-help-enterprise-sync-fabrix.js +1 -1
  69. package/lib/programmatic/builder-help-governance.js +1 -1
  70. package/lib/programmatic/builder-help.js +1 -1
  71. package/lib/role-assistant/test-cases-search.js +3 -0
  72. package/lib/role-assistant/test-job-runner.js +125 -0
  73. package/lib/role-assistant/test-runner-search.js +44 -2
  74. package/lib/role-assistant/test-runner-workhub-answers.js +4 -1
  75. package/lib/role-assistant/test-runner-workhub-missing-fields.js +42 -0
  76. package/lib/role-assistant/test-runner-workhub-wait-stop.js +94 -0
  77. package/lib/role-assistant/test-runner-workhub.js +28 -19
  78. package/lib/schema/application-schema.json +205 -2
  79. package/lib/schema/external-datasource.schema.json +23 -3
  80. package/lib/schema/infra-parameter.schema.json +139 -33
  81. package/lib/schema/infra.parameter.yaml +416 -66
  82. package/lib/schema/infrastructure-schema.json +10 -35
  83. package/lib/utils/compose-generate-docker-compose.js +16 -9
  84. package/lib/utils/datasource-binary-evidence.js +92 -0
  85. package/lib/utils/datasource-test-run-capability-scope.js +44 -1
  86. package/lib/utils/datasource-test-run-debug-display.js +2 -0
  87. package/lib/utils/datasource-test-run-display.js +8 -2
  88. package/lib/utils/datasource-test-run-issue-guidance.js +176 -0
  89. package/lib/utils/datasource-test-run-tty-log.js +2 -0
  90. package/lib/utils/docker-build.js +29 -8
  91. package/lib/utils/docker-manifest-public-port.js +36 -0
  92. package/lib/utils/env-copy.js +11 -10
  93. package/lib/utils/external-system-system-test-tty.js +3 -2
  94. package/lib/utils/image-tags.js +2 -2
  95. package/lib/utils/platform-kv-ref.js +1 -1
  96. package/lib/utils/platform-resolution.js +226 -0
  97. package/lib/utils/prepare-local-data-mount.js +58 -0
  98. package/lib/utils/resolve-docker-image-ref.js +8 -3
  99. package/lib/utils/secrets-helpers.js +0 -1
  100. package/lib/utils/system-secret-mapping.js +125 -0
  101. package/lib/utils/test-log-writer.js +2 -1
  102. package/lib/validation/external-manifest-validator.js +5 -0
  103. package/lib/validation/openapi-contract-surface-validator.js +3 -1
  104. package/lib/validation/validate-external-file.js +5 -1
  105. package/package.json +5 -4
  106. package/templates/README.md +2 -1
  107. package/templates/agent-kit/agent-kit.yaml +4 -1
  108. package/templates/agent-kit/instructions/AGENTKIT.md +1 -1
  109. package/templates/agent-kit/instructions/root.AGENTS.md +2 -0
  110. package/templates/agent-kit/skills/aifabrix-connected-system/SKILL.md +35 -2
  111. package/templates/agent-kit/skills/aifabrix-connected-system/references/delivery-gates.md +15 -0
  112. package/templates/agent-kit/skills/aifabrix-connected-system/scripts/delivery-verdict.js +176 -0
  113. package/templates/agent-kit/skills/aifabrix-plan/SKILL.md +6 -2
  114. package/templates/agent-kit/skills/aifabrix-prove/SKILL.md +28 -2
  115. package/templates/agent-kit/skills/aifabrix-prove/references/evidence-lifecycle.md +22 -0
  116. package/templates/agent-kit/skills/aifabrix-role-assistant/SKILL.md +28 -1
  117. package/templates/agent-kit/skills/aifabrix-role-assistant/references/testing-playbook.md +117 -0
  118. package/templates/agent-kit/skills/shared/feedback.md +31 -0
  119. package/templates/agent-kit/skills/shared/hosts.md +13 -3
  120. package/templates/agent-kit/skills/shared/interaction.md +83 -0
  121. package/templates/agent-kit/skills/shared/status.md +76 -0
  122. package/templates/agent-kit/workspace/BUILDER_IMPROVEMENT_FINDINGS.md +27 -0
  123. package/templates/applications/builder-api/application.yaml +1 -1
  124. package/templates/applications/builder-api/env.template +5 -1
  125. package/templates/applications/dataplane/application.yaml +30 -2
  126. package/templates/applications/dataplane/env.template +36 -5
  127. package/templates/applications/keycloak/application.yaml +6 -1
  128. package/templates/applications/miso-controller/application.yaml +96 -1
  129. package/templates/applications/miso-controller/env.template +63 -37
  130. package/templates/applications/miso-controller/rbac.yaml +17 -0
  131. package/templates/external-system/external-datasource.yaml.hbs +7 -1
  132. package/templates/marketplace/main.json +73 -225
  133. package/templates/python/Dockerfile.hbs +2 -0
  134. package/templates/python/docker-compose.hbs +1 -1
  135. package/templates/typescript/Dockerfile.hbs +2 -0
  136. package/templates/typescript/docker-compose.hbs +12 -0
  137. package/templates/agent-kit/skills/aifabrix-plan/references/interaction.md +0 -34
  138. /package/{lib/programmatic/help-content → docs/builder-help/content}/channel-onboarding.md +0 -0
  139. /package/{lib/programmatic/help-content → docs/builder-help/content}/cip-overview.md +0 -0
  140. /package/{lib/programmatic/help-content → docs/builder-help/content}/connected-system-ui.md +0 -0
  141. /package/{lib/programmatic/help-content → docs/builder-help/content}/dimensions-guide.md +0 -0
  142. /package/{lib/programmatic/help-content → docs/builder-help/content}/enterprise-sync-fabrix.md +0 -0
  143. /package/{lib/programmatic/help-content → docs/builder-help/content}/overview.md +0 -0
  144. /package/{lib/programmatic/help-content → docs/builder-help/content}/subscription-guide.md +0 -0
  145. /package/{lib/programmatic/help-content → docs/builder-help/content}/workflow.md +0 -0
@@ -29,7 +29,6 @@ standardUpInfraEnsureKeys:
29
29
  - databases-dataplane-3-urlKeyVault
30
30
  - databases-dataplane-3-passwordKeyVault
31
31
  # Miso-controller Entra onboarding (vendor app); required for aifabrix resolve miso-controller
32
- - entra-callback-state-secret
33
32
  - entra-callback-uri
34
33
  - entra-client-idKeyVault
35
34
  - entra-client-secretKeyVault
@@ -106,6 +105,9 @@ parameters:
106
105
  value: 'redis://${REDIS_HOST}:${REDIS_PORT}'
107
106
  ensureOn: [upInfra, resolveApp]
108
107
  azure:
108
+ vaultSecretName: redis-url
109
+ legacyVaultSecretNames:
110
+ - redis-urlKeyVault
109
111
  notes: >-
110
112
  Local up-infra resolves the REDIS_HOST/REDIS_PORT placeholders. Azure application deploy
111
113
  must leave this key absent so the Azure SDK derives the rediss URL and credential.
@@ -349,20 +351,6 @@ parameters:
349
351
 
350
352
  # Miso-controller Entra ID onboarding (vendor multi-tenant app). Empty client/callback values
351
353
  # allow local resolve and start-dev; set real values before Entra admin-consent flows.
352
- - key: entra-callback-state-secret
353
- category: Microsoft Entra ID
354
- sensitive: true
355
- valueSource: generated
356
- requiredOnSystemOnboarding: true
357
- scope: system
358
- generator:
359
- type: randomBytes32
360
- ensureOn: [upInfra, resolveApp]
361
- azure:
362
- notes: >-
363
- Shared HMAC secret (min 16 chars) for OAuth state between controller and Mori callback;
364
- maps to ENTRA_CALLBACK_STATE_SECRET in miso-controller env.template.
365
-
366
354
  - key: entra-callback-uri
367
355
  category: Microsoft Entra ID
368
356
  sensitive: false
@@ -441,8 +429,9 @@ parameters:
441
429
  # side is decrypted by the other. The read was already happening in the shipped template
442
430
  # and the policy denied it. Declared rather than silently permitted, and kept to the one
443
431
  # application that needs it instead of moving the key to a scope that grants everyone.
444
- sharedWithAppKeys:
432
+ visibility:
445
433
  - dataplane
434
+ - mori-controller
446
435
  scope: app
447
436
  generator:
448
437
  type: randomBytes32
@@ -454,6 +443,32 @@ parameters:
454
443
  notes: >-
455
444
  32-byte secret as base64; generated on first ensure. Rotate in production.
456
445
 
446
+ - key: integration-server-secrets-encryptionKeyVault
447
+ ownerAppKey: integration-server
448
+ # Deliberately NOT shared with any other app, which is the point of the row.
449
+ # An integrator installation hands this key to every developer it provisions: it is
450
+ # written into each managed ~/.aifabrix/config.yaml as `secrets-encryption`, so the
451
+ # CLI can decrypt what that installation's own secrets API encrypted. A key shared
452
+ # with another application would therefore be readable by everyone holding a shell
453
+ # in that container, and one leak would reach every installation using it.
454
+ # One installation, one key, so the blast radius stays inside it.
455
+ #
456
+ # Before this row existed, integration-server's env.template read
457
+ # `kv://builder-server-secrets-encryptionKeyVault` — another application's key.
458
+ # That resolves only by being granted, and granting it grants exactly the access
459
+ # above; without a grant it silently generated an unrelated value instead, so the
460
+ # two server types drifted apart with nothing reporting it.
461
+ scope: app
462
+ generator:
463
+ type: randomBytes32
464
+ ensureOn: [upInfra, resolveApp]
465
+ azure:
466
+ vaultSecretName: integration-server-secrets-encryptionKeyVault
467
+ notes: >-
468
+ 32-byte secret as base64; generated on first ensure, per installation. Rotating it
469
+ requires re-running configuration sync so every managed config.yaml is rewritten;
470
+ secrets already stored by that installation do not survive the rotation.
471
+
457
472
  # Local kv:// name matches miso-controller env.template (JWT_SECRET); not the generic {appKey}-secrets-jwtKeyVault Azure name.
458
473
  - key: miso-controller-jwt-secretKeyVault
459
474
  ownerAppKey: miso-controller
@@ -582,6 +597,7 @@ parameters:
582
597
  - key: miso-controller-client-idKeyVault
583
598
  ownerAppKey: miso-controller
584
599
  scope: app
600
+ sensitive: false
585
601
  generator:
586
602
  type: literal
587
603
  value: 'miso-controller-miso-miso-controller'
@@ -603,10 +619,35 @@ parameters:
603
619
  Per-app OAuth client secret used by miso-controller for service-to-service auth (MISO_CLIENTSECRET).
604
620
  Generated on first ensure for local bootstrap.
605
621
 
622
+ # Builder API ↔ controller OAuth. These are explicit declarations rather than catch-all
623
+ # matches so the backup catalog records the owner and canonical names unambiguously.
624
+ - key: builder-api-client-idKeyVault
625
+ ownerAppKey: builder-api
626
+ scope: app
627
+ sensitive: false
628
+ generator:
629
+ type: emptyString
630
+ ensureOn: [resolveApp]
631
+ azure:
632
+ vaultSecretName: builder-api-client-idKeyVault
633
+ notes: Per-app OAuth client id issued when builder-api is registered with Miso.
634
+
635
+ - key: builder-api-client-secretKeyVault
636
+ ownerAppKey: builder-api
637
+ scope: app
638
+ sensitive: true
639
+ generator:
640
+ type: emptyString
641
+ ensureOn: [resolveApp]
642
+ azure:
643
+ vaultSecretName: builder-api-client-secretKeyVault
644
+ notes: Per-app OAuth client secret issued when builder-api is registered with Miso.
645
+
606
646
  # Dataplane ↔ controller OAuth (builder/dataplane env.template MISO_CLIENTID / MISO_CLIENTSECRET).
607
647
  - key: dataplane-client-idKeyVault
608
648
  ownerAppKey: dataplane
609
649
  scope: app
650
+ sensitive: false
610
651
  generator:
611
652
  type: literal
612
653
  value: 'miso-controller-dev-dataplane'
@@ -640,18 +681,39 @@ parameters:
640
681
  notes: Empty locally; set when calling Mori with API key auth.
641
682
 
642
683
  # Public, but a security-critical trust anchor. Never derive it from a pasted JWT.
684
+ #
685
+ # `sensitive: true` on a public key is deliberate -- 227.1 made Miso verify licences against a
686
+ # PINNED key precisely because a key travelling inside the token it verifies proves nothing, so
687
+ # this value must be protected from tampering even though disclosing it costs nothing.
688
+ #
689
+ # It is owned by `miso-controller` rather than left system-scoped because a system-scoped
690
+ # sensitive secret is readable by NO application (`forbidden-system-secret`). Measured on
691
+ # aifabrix-ce-16 2026-09-27: miso-controller declares MORI_LICENSE_PUBLIC_KEY in its manifest, yet
692
+ # the key was absent from its 70 registry rows and from its snapshot, so the installation could
693
+ # never have verified a licence. Ownership is what makes a trust anchor readable by the service
694
+ # whose trust it anchors -- the same correction already applied to secrets-policyPrivateKeyVault.
643
695
  - key: mori-license-public-keyKeyVault
644
696
  category: Mori
697
+ ownerAppKey: miso-controller
645
698
  sensitive: true
646
699
  valueSource: optionalInput
647
700
  requiredOnSystemOnboarding: false
648
- scope: system
701
+ scope: app
702
+ # The public half of the Mori licence pair above, so the key Miso verifies with cannot drift
703
+ # from the key Mori signs with. Was `emptyString`: the two halves came from unrelated places
704
+ # and an installation could hold a perfectly valid public key belonging to a different private
705
+ # key, which verifies nothing and reports no error.
706
+ #
707
+ # Rotating an installation that already holds a value here means clearing BOTH halves
708
+ # together — the generator is get-or-create, so replacing one alone leaves a mismatched pair.
649
709
  generator:
650
- type: emptyString
710
+ type: rsaKeyPair
711
+ pairId: mori-license-signing
712
+ part: public
651
713
  ensureOn: [upInfra, resolveApp]
652
714
  azure:
653
715
  vaultSecretName: mori-license-public-keyKeyVault
654
- notes: Trusted Mori SPKI PEM public key, provisioned by the installer or issuer release identity.
716
+ notes: SPKI PEM public half of the Mori licence signing pair; linked to secrets-licensePrivateKeyVault.
655
717
 
656
718
  - key: appinsights-connectionStringKeyVault
657
719
  category: Observability
@@ -693,7 +755,7 @@ parameters:
693
755
  vaultSecretName: loganalytics-workspace-id
694
756
  notes: Azure SDK-derived Log Analytics workspace customer identifier.
695
757
 
696
- - key: azureopenai-endpoint
758
+ - key: azure-openai/endpoint
697
759
  category: Azure OpenAI
698
760
  sensitive: false
699
761
  valueSource: optionalInput
@@ -703,10 +765,10 @@ parameters:
703
765
  type: emptyString
704
766
  ensureOn: [resolveApp]
705
767
  azure:
706
- vaultSecretName: azureopenai-endpoint
707
- notes: Azure OpenAI endpoint produced by the environment infrastructure deployment.
768
+ vaultSecretName: dev-dataplane-azure-openai-endpoint
769
+ notes: Azure OpenAI endpoint produced by infrastructure at the standard dev Dataplane address.
708
770
 
709
- - key: azureopenai-apikey
771
+ - key: azure-openai/apiKey
710
772
  category: Azure OpenAI
711
773
  sensitive: true
712
774
  valueSource: optionalInput
@@ -716,21 +778,8 @@ parameters:
716
778
  type: emptyString
717
779
  ensureOn: [resolveApp]
718
780
  azure:
719
- vaultSecretName: azureopenai-apikey
720
- notes: Azure OpenAI API key produced by the environment infrastructure deployment.
721
-
722
- # Dataplane builder/dataplane env.template (AI/LLM); optional until OpenAI or Azure OpenAI is configured.
723
- - key: secrets-openaiApiKeyVault
724
- category: OpenAI
725
- sensitive: true
726
- valueSource: optionalInput
727
- requiredOnSystemOnboarding: false
728
- scope: system
729
- generator:
730
- type: emptyString
731
- ensureOn: [resolveApp]
732
- azure:
733
- notes: Optional shared OpenAI API key; empty until supplied and never auto-generated.
781
+ vaultSecretName: dev-dataplane-azure-openai-api-key
782
+ notes: Azure OpenAI API key produced by infrastructure at the standard dev Dataplane address.
734
783
 
735
784
  - key: azure-openaiapi-urlKeyVault
736
785
  scope: app
@@ -740,17 +789,13 @@ parameters:
740
789
  azure:
741
790
  notes: Empty until set; Azure OpenAI endpoint URL.
742
791
 
743
- - key: secrets-azureOpenaiApiKeyVault
744
- scope: app
745
- generator:
746
- type: emptyString
747
- ensureOn: [upInfra, resolveApp]
748
- azure:
749
- notes: Empty until set; user-supplied Azure OpenAI API key (not auto-generated).
750
-
751
792
  # Dataplane RS256 signing identity. The two rows are generated as one linked pair.
752
793
  - key: secrets-certificatePrivateKeyVault
753
794
  ownerAppKey: dataplane
795
+ # Deliberately NOT shared with mori-controller. `readability-real-catalog.test.ts` asserts
796
+ # "denies mori signing licences with the dataplane signing key" — Mori's manifest pointing
797
+ # MORI_LICENSE_PRIVATE_KEY at this key is the misconfiguration, not the denial. The fix is a
798
+ # mori-controller-owned licence key and a manifest change, never widening this entry.
754
799
  scope: app
755
800
  generator:
756
801
  type: rsaKeyPair
@@ -761,6 +806,66 @@ parameters:
761
806
  vaultSecretName: secrets-certificatePrivateKeyVault
762
807
  notes: PKCS#8 PEM private key generated with the matching public key.
763
808
 
809
+ # Mori's policy signing key. Present in every installation's vault but absent from this catalog,
810
+ # so it fell through to the `^[a-zA-Z0-9_-]+KeyVault$` catch-all — `scope: app` with no owner,
811
+ # which `resolveOwnershipDecision` cannot classify and the policy therefore denies to everyone.
812
+ #
813
+ # Measured on aifabrix-ce-16, 2026-09-26: the secret exists, Mori reads it today through an App
814
+ # Service Key Vault reference, and bootstrap omitted it. That is the same key whose absence
815
+ # previously failed Mori's signing health check.
816
+ - key: secrets-policyPrivateKeyVault
817
+ ownerAppKey: mori-controller
818
+ scope: app
819
+ sensitive: true
820
+ generator:
821
+ type: rsaKeyPair
822
+ pairId: mori-policy-signing
823
+ part: private
824
+ ensureOn: [resolveApp]
825
+ azure:
826
+ vaultSecretName: secrets-policyPrivateKeyVault
827
+ notes: PKCS#8 PEM private key for Mori policy signing; MORI_POLICY_KEY_ID names the pair.
828
+
829
+ # Mori's LICENCE signing key, which is not its policy signing key and not the dataplane's.
830
+ #
831
+ # `subscription-license.operations.ts` refuses to issue at all without it:
832
+ # if (!privateKey) throw ErrorFactory.internalServerError('License signing is unavailable')
833
+ # and until this entry existed there was nothing for it to read. Mori's manifest pointed
834
+ # MORI_LICENSE_PRIVATE_KEY at `secrets-certificatePrivateKeyVault`, which the dataplane owns, so
835
+ # the read was denied -- correctly. An existing test (`denies mori signing licences with the
836
+ # dataplane signing key`) pins that denial, so the fix is a key Mori owns, never a wider grant.
837
+ #
838
+ # `emptyString` rather than `rsaKeyPair`: the operator supplies the pair, because whoever holds
839
+ # this key decides which licences an installation will believe. Generating it during onboarding
840
+ # would make the platform mint its own trust root as a side effect of deployment. Onboarding
841
+ # creates the placeholder, `aifabrix env secret push` writes the real value, and create-if-absent
842
+ # then leaves it alone. The verifying half is `mori-license-public-keyKeyVault`, pinned to
843
+ # miso-controller; a pair is only useful if both halves are readable by their own service.
844
+ - key: secrets-licensePrivateKeyVault
845
+ category: Mori
846
+ ownerAppKey: mori-controller
847
+ scope: app
848
+ sensitive: true
849
+ valueSource: optionalInput
850
+ requiredOnSystemOnboarding: false
851
+ # Generated as one linked pair with mori-license-public-keyKeyVault, the same way the
852
+ # dataplane's signing identity is. Was `emptyString`, which meant an installation that
853
+ # repointed Mori at this key got nothing and `subscription-license.operations.ts` refused
854
+ # with "License signing is unavailable" -- so the only working configuration was the wrong
855
+ # one: Mori signing with the DATAPLANE's key via secrets-certificatePrivateKeyVault.
856
+ #
857
+ # Mori and the dataplane must not share a signing identity. Sharing one means the dataplane
858
+ # holds a key that mints licences, and the two services have completely different blast
859
+ # radii. Mori is the licence server; the dataplane runs customer workloads.
860
+ generator:
861
+ type: rsaKeyPair
862
+ pairId: mori-license-signing
863
+ part: private
864
+ ensureOn: [resolveApp]
865
+ azure:
866
+ vaultSecretName: secrets-licensePrivateKeyVault
867
+ notes: PKCS#8 PEM private key for Mori licence signing; MORI_LICENSE_KEY_ID names the pair.
868
+
764
869
  - key: secrets-certificatePublicKeyVault
765
870
  ownerAppKey: dataplane
766
871
  scope: app
@@ -794,18 +899,6 @@ parameters:
794
899
  Optional local testing/development bearer bypass. A supplied local value is resolved; when absent,
795
900
  local and Azure application deployment resolve it as empty and do not fail.
796
901
 
797
- - key: mori-callback-api-keyKeyVault
798
- ownerAppKey: miso-controller
799
- scope: app
800
- generator:
801
- type: randomBytes32
802
- ensureOn: [resolveApp]
803
- azure:
804
- vaultSecretName: mori-callback-api-keyKeyVault
805
- legacyVaultSecretNames:
806
- - marketplace-mori-callback-api-key
807
- notes: Miso Controller callback credential; generated only when its resolved manifest requires it.
808
-
809
902
  # App semver when env.template / conversion yields kv://version (local secrets backfill).
810
903
  - key: version
811
904
  scope: app
@@ -825,8 +918,12 @@ parameters:
825
918
  type: databaseUrl
826
919
  ensureOn: [upInfra, resolveApp]
827
920
  azure:
828
- vaultSecretNamePattern: '{appKey}-databases-{index}-urlKeyVault'
921
+ vaultSecretNamePattern: '{environment}-{appKey}-databases-{index}-urlKeyVault'
829
922
  legacyVaultSecretNames:
923
+ # Pre-environment naming: app and index only, so two environments sharing one vault
924
+ # collided. Kept so installations written under it keep resolving without a vault write.
925
+ - '{appKey}-databases-{index}-urlKeyVault'
926
+ # The original logical key, which is what aifabrix-ce-16 actually holds.
830
927
  - '{key}'
831
928
  - '{serviceName}-databases-0-urlKeyVault'
832
929
 
@@ -837,8 +934,12 @@ parameters:
837
934
  type: databasePassword
838
935
  ensureOn: [upInfra, resolveApp]
839
936
  azure:
840
- vaultSecretNamePattern: '{appKey}-databases-{index}-passwordKeyVault'
937
+ vaultSecretNamePattern: '{environment}-{appKey}-databases-{index}-passwordKeyVault'
841
938
  legacyVaultSecretNames:
939
+ # Pre-environment naming: app and index only, so two environments sharing one vault
940
+ # collided. Kept so installations written under it keep resolving without a vault write.
941
+ - '{appKey}-databases-{index}-passwordKeyVault'
942
+ # The original logical key, which is what aifabrix-ce-16 actually holds.
842
943
  - '{key}'
843
944
 
844
945
  - key: databases-miso-controller-1-urlKeyVault
@@ -848,8 +949,12 @@ parameters:
848
949
  type: databaseUrl
849
950
  ensureOn: [upInfra, resolveApp]
850
951
  azure:
851
- vaultSecretNamePattern: '{appKey}-databases-{index}-urlKeyVault'
952
+ vaultSecretNamePattern: '{environment}-{appKey}-databases-{index}-urlKeyVault'
852
953
  legacyVaultSecretNames:
954
+ # Pre-environment naming: app and index only, so two environments sharing one vault
955
+ # collided. Kept so installations written under it keep resolving without a vault write.
956
+ - '{appKey}-databases-{index}-urlKeyVault'
957
+ # The original logical key, which is what aifabrix-ce-16 actually holds.
853
958
  - '{key}'
854
959
  - '{serviceName}-databases-1-urlKeyVault'
855
960
 
@@ -860,9 +965,120 @@ parameters:
860
965
  type: databasePassword
861
966
  ensureOn: [upInfra, resolveApp]
862
967
  azure:
863
- vaultSecretNamePattern: '{appKey}-databases-{index}-passwordKeyVault'
968
+ vaultSecretNamePattern: '{environment}-{appKey}-databases-{index}-passwordKeyVault'
969
+ legacyVaultSecretNames:
970
+ # Pre-environment naming: app and index only, so two environments sharing one vault
971
+ # collided. Kept so installations written under it keep resolving without a vault write.
972
+ - '{appKey}-databases-{index}-passwordKeyVault'
973
+ # The original logical key, which is what aifabrix-ce-16 actually holds.
974
+ - '{key}'
975
+
976
+ # Installed Dataplane stacks used semantic database names before the environment-scoped
977
+ # convention. Keep the exact inventory here so onboarding can copy each value to its canonical
978
+ # name without guessing which database slot it belongs to.
979
+ - key: databases-dataplane-0-urlKeyVault
980
+ ownerAppKey: dataplane
981
+ scope: app
982
+ generator:
983
+ type: databaseUrl
984
+ ensureOn: [upInfra, resolveApp]
985
+ azure:
986
+ vaultSecretNamePattern: '{environment}-{appKey}-databases-{index}-urlKeyVault'
987
+ legacyVaultSecretNames:
988
+ - '{appKey}-databases-{index}-urlKeyVault'
989
+ - '{key}'
990
+ - dataplane-connection-string
991
+
992
+ - key: databases-dataplane-0-passwordKeyVault
993
+ ownerAppKey: dataplane
994
+ scope: app
995
+ generator:
996
+ type: databasePassword
997
+ ensureOn: [upInfra, resolveApp]
998
+ azure:
999
+ vaultSecretNamePattern: '{environment}-{appKey}-databases-{index}-passwordKeyVault'
864
1000
  legacyVaultSecretNames:
1001
+ - '{appKey}-databases-{index}-passwordKeyVault'
865
1002
  - '{key}'
1003
+ - dataplane-password
1004
+
1005
+ - key: databases-dataplane-1-urlKeyVault
1006
+ ownerAppKey: dataplane
1007
+ scope: app
1008
+ generator:
1009
+ type: databaseUrl
1010
+ ensureOn: [upInfra, resolveApp]
1011
+ azure:
1012
+ vaultSecretNamePattern: '{environment}-{appKey}-databases-{index}-urlKeyVault'
1013
+ legacyVaultSecretNames:
1014
+ - '{appKey}-databases-{index}-urlKeyVault'
1015
+ - '{key}'
1016
+ - dataplane-vector-connection-string
1017
+
1018
+ - key: databases-dataplane-1-passwordKeyVault
1019
+ ownerAppKey: dataplane
1020
+ scope: app
1021
+ generator:
1022
+ type: databasePassword
1023
+ ensureOn: [upInfra, resolveApp]
1024
+ azure:
1025
+ vaultSecretNamePattern: '{environment}-{appKey}-databases-{index}-passwordKeyVault'
1026
+ legacyVaultSecretNames:
1027
+ - '{appKey}-databases-{index}-passwordKeyVault'
1028
+ - '{key}'
1029
+ - dataplane-vector-password
1030
+
1031
+ - key: databases-dataplane-2-urlKeyVault
1032
+ ownerAppKey: dataplane
1033
+ scope: app
1034
+ generator:
1035
+ type: databaseUrl
1036
+ ensureOn: [upInfra, resolveApp]
1037
+ azure:
1038
+ vaultSecretNamePattern: '{environment}-{appKey}-databases-{index}-urlKeyVault'
1039
+ legacyVaultSecretNames:
1040
+ - '{appKey}-databases-{index}-urlKeyVault'
1041
+ - '{key}'
1042
+ - dataplane-logs-connection-string
1043
+
1044
+ - key: databases-dataplane-2-passwordKeyVault
1045
+ ownerAppKey: dataplane
1046
+ scope: app
1047
+ generator:
1048
+ type: databasePassword
1049
+ ensureOn: [upInfra, resolveApp]
1050
+ azure:
1051
+ vaultSecretNamePattern: '{environment}-{appKey}-databases-{index}-passwordKeyVault'
1052
+ legacyVaultSecretNames:
1053
+ - '{appKey}-databases-{index}-passwordKeyVault'
1054
+ - '{key}'
1055
+ - dataplane-logs-password
1056
+
1057
+ - key: databases-dataplane-3-urlKeyVault
1058
+ ownerAppKey: dataplane
1059
+ scope: app
1060
+ generator:
1061
+ type: databaseUrl
1062
+ ensureOn: [upInfra, resolveApp]
1063
+ azure:
1064
+ vaultSecretNamePattern: '{environment}-{appKey}-databases-{index}-urlKeyVault'
1065
+ legacyVaultSecretNames:
1066
+ - '{appKey}-databases-{index}-urlKeyVault'
1067
+ - '{key}'
1068
+ - dataplane-records-connection-string
1069
+
1070
+ - key: databases-dataplane-3-passwordKeyVault
1071
+ ownerAppKey: dataplane
1072
+ scope: app
1073
+ generator:
1074
+ type: databasePassword
1075
+ ensureOn: [upInfra, resolveApp]
1076
+ azure:
1077
+ vaultSecretNamePattern: '{environment}-{appKey}-databases-{index}-passwordKeyVault'
1078
+ legacyVaultSecretNames:
1079
+ - '{appKey}-databases-{index}-passwordKeyVault'
1080
+ - '{key}'
1081
+ - dataplane-records-password
866
1082
 
867
1083
  # Per-index database secrets. Index must match requires.databases order in application.yaml.
868
1084
  - keyPattern: '^databases-[a-z0-9-]+-\d+-urlKeyVault$'
@@ -871,8 +1087,12 @@ parameters:
871
1087
  type: databaseUrl
872
1088
  ensureOn: [upInfra, resolveApp]
873
1089
  azure:
874
- vaultSecretNamePattern: '{appKey}-databases-{index}-urlKeyVault'
1090
+ vaultSecretNamePattern: '{environment}-{appKey}-databases-{index}-urlKeyVault'
875
1091
  legacyVaultSecretNames:
1092
+ # Pre-environment naming: app and index only, so two environments sharing one vault
1093
+ # collided. Kept so installations written under it keep resolving without a vault write.
1094
+ - '{appKey}-databases-{index}-urlKeyVault'
1095
+ # The original logical key, which is what aifabrix-ce-16 actually holds.
876
1096
  - '{key}'
877
1097
  notes: The manifest key stays logical; the app-prefixed name is the physical vault key.
878
1098
 
@@ -882,12 +1102,26 @@ parameters:
882
1102
  type: databasePassword
883
1103
  ensureOn: [upInfra, resolveApp]
884
1104
  azure:
885
- vaultSecretNamePattern: '{appKey}-databases-{index}-passwordKeyVault'
1105
+ vaultSecretNamePattern: '{environment}-{appKey}-databases-{index}-passwordKeyVault'
886
1106
  legacyVaultSecretNames:
1107
+ # Pre-environment naming: app and index only, so two environments sharing one vault
1108
+ # collided. Kept so installations written under it keep resolving without a vault write.
1109
+ - '{appKey}-databases-{index}-passwordKeyVault'
1110
+ # The original logical key, which is what aifabrix-ce-16 actually holds.
887
1111
  - '{key}'
888
1112
 
1113
+ # Addresses, not credentials.
1114
+ #
1115
+ # These defaulted to sensitive (`sensitive` is absent, and the service reads `entry.sensitive !== false`),
1116
+ # so the system-secrets list API withheld the VALUE of a URL, a hostname and a virtual directory. Six
1117
+ # literal entries — frontdoor-url, frontdoor-host, keycloak-web-server-url,
1118
+ # keycloak-internal-server-url, keycloak-vdir-public, miso-controller-web-server-url and friends —
1119
+ # exist only to override that one key at a time, which is the hardcoding this convention removes.
1120
+ # An address is operator-facing by nature: hiding it makes an installation harder to diagnose and
1121
+ # protects nothing.
889
1122
  # Keycloak and other *-url keys (non-database).
890
1123
  - keyPattern: '^keycloak-.+-url$'
1124
+ sensitive: false
891
1125
  scope: shared-service
892
1126
  generator:
893
1127
  type: emptyString
@@ -895,6 +1129,7 @@ parameters:
895
1129
 
896
1130
  # App-scoped URL placeholders (filled at resolve / deploy).
897
1131
  - keyPattern: '^[a-z0-9-]+-url$'
1132
+ sensitive: false
898
1133
  scope: app
899
1134
  generator:
900
1135
  type: emptyString
@@ -902,6 +1137,7 @@ parameters:
902
1137
 
903
1138
  # Deploy manifest maps frontDoorRouting.host templates to this secret (Azure / pipeline).
904
1139
  - keyPattern: '^[a-z0-9-]+-frontdoor-routing-host$'
1140
+ sensitive: false
905
1141
  scope: app
906
1142
  generator:
907
1143
  type: emptyString
@@ -910,32 +1146,146 @@ parameters:
910
1146
  notes: Hostname template for Traefik / Front Door; filled at deploy.
911
1147
 
912
1148
  - keyPattern: '^[a-z0-9-]+-vdir-public$'
1149
+ sensitive: false
913
1150
  scope: app
914
1151
  generator:
915
1152
  type: emptyString
916
1153
  ensureOn: [resolveApp]
917
1154
 
918
1155
  - keyPattern: '^[a-z0-9-]+-vdir-internal$'
1156
+ sensitive: false
919
1157
  scope: app
920
1158
  generator:
921
1159
  type: emptyString
922
1160
  ensureOn: [resolveApp]
923
1161
 
924
1162
  - keyPattern: '^[a-z0-9-]+-host-public$'
1163
+ sensitive: false
925
1164
  scope: app
926
1165
  generator:
927
1166
  type: emptyString
928
1167
  ensureOn: [resolveApp]
929
1168
 
930
1169
  - keyPattern: '^[a-z0-9-]+-host-internal$'
1170
+ sensitive: false
931
1171
  scope: app
932
1172
  generator:
933
1173
  type: emptyString
934
1174
  ensureOn: [resolveApp]
935
1175
 
936
- # Remaining *KeyVault secrets — generated tokens/passwords (not database rows above).
937
- - keyPattern: '^[a-zA-Z0-9_-]+KeyVault$'
1176
+ # Secrets that are genuinely generated here, declared explicitly because the catch-all below no
1177
+ # longer invents values. Each is a shared secret with no other issuer: nothing outside this
1178
+ # catalog mints them, so an empty value would leave the consumer with nothing to verify against.
1179
+ - key: notification-event-webhook-secretKeyVault
938
1180
  scope: app
939
1181
  generator:
940
1182
  type: randomBytes32
941
1183
  ensureOn: [resolveApp]
1184
+ azure:
1185
+ vaultSecretName: notification-event-webhook-secretKeyVault
1186
+ notes: Shared secret signing notification webhook callbacks; generated, never supplied.
1187
+
1188
+ - key: secrets-encryptionKeyVault
1189
+ scope: app
1190
+ generator:
1191
+ type: randomBytes32
1192
+ ensureOn: [resolveApp]
1193
+ azure:
1194
+ vaultSecretName: secrets-encryptionKeyVault
1195
+ notes: Application-scoped encryption key; generated, never supplied.
1196
+
1197
+ - key: miso-controller-api-key-secretKeyVault
1198
+ scope: app
1199
+ generator:
1200
+ type: randomBytes32
1201
+ ensureOn: [resolveApp]
1202
+ azure:
1203
+ vaultSecretName: miso-controller-api-key-secretKeyVault
1204
+ notes: Generated API key for test/integration apps; generated, never supplied.
1205
+
1206
+ # `-idKeyVault` and `-nameKeyVault` are NOT secrets.
1207
+ #
1208
+ # An identifier and a name are operator-facing: a client ID, a tenant ID, a subscription ID, a
1209
+ # service name. They live in the same store as the secrets because they arrive the same way, but
1210
+ # there is nothing to withhold, and withholding them makes an installation harder to read.
1211
+ #
1212
+ # These two rows replace five literal entries that existed only to set `sensitive: false` on one
1213
+ # key each — `keycloak-client-idKeyVault`, `entra-client-idKeyVault`,
1214
+ # `azure-subscription-idKeyVault`, `azure-tenant-idKeyVault`, `azure-service-nameKeyVault`. The
1215
+ # convention now carries the decision, so a customer's own `myapp-tenant-idKeyVault` is readable
1216
+ # without an edit here.
1217
+ #
1218
+ # ORDER MATTERS: `findCatalogEntry` checks exact keys, then patterns in FILE ORDER and returns the
1219
+ # first match. Both suffixes also match the generic `*KeyVault` catch-all below, so they have to
1220
+ # stay above it or they inherit `sensitive: true` and the convention silently does nothing.
1221
+ - keyPattern: '^[a-zA-Z0-9_-]+-idKeyVault$'
1222
+ scope: app
1223
+ sensitive: false
1224
+ generator:
1225
+ type: emptyString
1226
+ ensureOn: [resolveApp]
1227
+
1228
+ - keyPattern: '^[a-zA-Z0-9_-]+-nameKeyVault$'
1229
+ scope: app
1230
+ sensitive: false
1231
+ generator:
1232
+ type: emptyString
1233
+ ensureOn: [resolveApp]
1234
+
1235
+ # Remaining *KeyVault secrets — created EMPTY, then supplied.
1236
+ #
1237
+ # This generated `randomBytes32`, so any `kv://<anything>KeyVault` key the catalog did not name
1238
+ # explicitly received 32 random bytes — 44 base64 characters — and the reference resolved to a
1239
+ # value that was never a credential anybody issued.
1240
+ #
1241
+ # Measured on installation aifabrix-ce-16, 2026-09-25: `secrets-policyPrivateKeyVault` is Mori's
1242
+ # licence-policy SIGNING KEY and held exactly that, 44 characters and not a PEM. Mori's
1243
+ # `signingKeysHealthCheck` parses the key rather than checking presence, so it reported
1244
+ # `signingKeys` down, the container exited, and the web app answered HTTP 503 on every path for two
1245
+ # days while App Service reported it Running. Mori's own comment had predicted this class in
1246
+ # writing: "the deployment builder provisioned an unknown Key Vault catalog key with a random
1247
+ # 44-character secret rather than failing: present, resolved, and not a private key at all".
1248
+ #
1249
+ # Empty is the correct default for a key nobody here mints. It creates the secret — so `kv://`
1250
+ # resolves and the reference is valid — while leaving the value visibly unset, which is a state an
1251
+ # operator can see and fix. A random value is indistinguishable from a real credential from the
1252
+ # outside, which is precisely what made it expensive.
1253
+ #
1254
+ # The catch-all stays a pattern rather than becoming a list of literals on purpose: a customer
1255
+ # deploying their own application declares its own `kv://` keys, and those must work without being
1256
+ # hardcoded here. Values arrive afterwards through the write-only system-secrets API
1257
+ # (`PUT /api/v1/environments/{envKey}/system-secrets/{catalogKey}`), which is what
1258
+ # aifabrix-builder plan 204.0 builds the CLI for.
1259
+ #
1260
+ # A secret that genuinely has no issuer but must exist gets an explicit `randomBytes32` entry
1261
+ # above. Adding one is the deliberate act; generation is no longer the silent default.
1262
+ - keyPattern: '^[a-zA-Z0-9_-]+KeyVault$'
1263
+ scope: app
1264
+ sensitive: true
1265
+ generator:
1266
+ type: emptyString
1267
+ ensureOn: [resolveApp]
1268
+
1269
+ # Non-secret values in the same store — the suffix carries the sensitivity.
1270
+ #
1271
+ # `*KeyVault` above means "secret": hidden by the list API, write-only through
1272
+ # `PUT /api/v1/environments/{envKey}/system-secrets/{catalogKey}`. `*Param` means the same storage
1273
+ # with nothing to hide: the list API returns the value, because `summarize()` emits `value` only
1274
+ # when `sensitive === false`.
1275
+ #
1276
+ # Why a pattern and not more literals: the suffix used to say nothing about sensitivity, so every
1277
+ # non-secret that happened to end in `KeyVault` needed its own literal row purely to set
1278
+ # `sensitive: false` — `keycloak-client-idKeyVault`, `entra-client-idKeyVault`,
1279
+ # `azure-subscription-idKeyVault`, `azure-tenant-idKeyVault`, `azure-service-nameKeyVault`. Client
1280
+ # IDs, tenant IDs and service names are not credentials, and hardcoding each one does not scale:
1281
+ # a customer deploying their own application cannot get a visible parameter without an edit here.
1282
+ #
1283
+ # With the convention, visibility is a naming decision the application author makes and a reader
1284
+ # can see: name it `…Param` to be readable, `…KeyVault` to be secret. Changing your mind is a
1285
+ # rename, not a catalog change. Both create the entry EMPTY and are supplied afterwards.
1286
+ - keyPattern: '^[a-zA-Z0-9_-]+Param$'
1287
+ scope: app
1288
+ sensitive: false
1289
+ generator:
1290
+ type: emptyString
1291
+ ensureOn: [resolveApp]