ignition-stack 0.5.0__tar.gz → 0.6.0__tar.gz

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 (192) hide show
  1. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/PKG-INFO +7 -5
  2. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/README.md +6 -4
  3. ignition_stack-0.6.0/ignition_stack/__init__.py +1 -0
  4. ignition_stack-0.6.0/ignition_stack/architectures/__init__.py +35 -0
  5. {ignition_stack-0.5.0/ignition_stack/profiles → ignition_stack-0.6.0/ignition_stack/architectures}/advisory.py +1 -1
  6. {ignition_stack-0.5.0/ignition_stack/profiles → ignition_stack-0.6.0/ignition_stack/architectures}/base.py +68 -64
  7. ignition_stack-0.6.0/ignition_stack/architectures/basic.py +45 -0
  8. {ignition_stack-0.5.0/ignition_stack/profiles → ignition_stack-0.6.0/ignition_stack/architectures}/carry.py +27 -27
  9. {ignition_stack-0.5.0/ignition_stack/profiles → ignition_stack-0.6.0/ignition_stack/architectures}/hub_and_spoke.py +23 -23
  10. ignition_stack-0.5.0/ignition_stack/profiles/scaleout.py → ignition_stack-0.6.0/ignition_stack/architectures/scale_out.py +18 -17
  11. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/catalog/builtins.py +3 -3
  12. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/cli.py +130 -98
  13. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/completion.py +21 -20
  14. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/compose/engine.py +82 -2
  15. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/compose/templates/footer.yaml.j2 +5 -1
  16. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/compose/templates/services/ignition.yaml.j2 +12 -0
  17. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/compose/writer.py +13 -5
  18. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/config/io.py +1 -1
  19. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/config/schema.py +101 -19
  20. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/lifecycle/record.py +3 -3
  21. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/lifecycle/regenerate.py +1 -1
  22. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/postsetup/generator.py +171 -11
  23. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/services/manifest.py +79 -2
  24. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/services/resolver.py +58 -31
  25. ignition_stack-0.6.0/ignition_stack/templates/post-setup/connections.md.j2 +23 -0
  26. ignition_stack-0.6.0/ignition_stack/templates/post-setup/reverse-proxy.md.j2 +17 -0
  27. ignition_stack-0.6.0/ignition_stack/templates/services/chariot/manifest.yaml +48 -0
  28. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/emqx/manifest.yaml +10 -1
  29. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/hivemq/manifest.yaml +9 -1
  30. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/kafka/manifest.yaml +8 -1
  31. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/keycloak/manifest.yaml +10 -1
  32. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/mariadb/manifest.yaml +10 -1
  33. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/modbus-sim/manifest.yaml +6 -1
  34. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/mongo/manifest.yaml +10 -1
  35. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/mysql/manifest.yaml +10 -1
  36. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/n8n/manifest.yaml +6 -1
  37. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/opcua-sim/manifest.yaml +6 -1
  38. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/postgres/manifest.yaml +12 -1
  39. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/rabbitmq/manifest.yaml +11 -1
  40. ignition_stack-0.6.0/ignition_stack/wizard.py +1054 -0
  41. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/wizard_composer.py +246 -71
  42. ignition_stack-0.5.0/tests/test_profiles.py → ignition_stack-0.6.0/tests/test_architectures.py +325 -214
  43. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/test_completion.py +8 -8
  44. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/test_declarative_io.py +83 -38
  45. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/test_disable_builtins.py +15 -15
  46. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/test_docs_cli_reference.py +5 -6
  47. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/test_iiot_overlay.py +23 -23
  48. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/test_init_standalone.py +8 -8
  49. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/test_lifecycle.py +14 -14
  50. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/test_manifest_invariants.py +57 -13
  51. ignition_stack-0.6.0/tests/test_postsetup.py +279 -0
  52. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/test_redundancy.py +21 -21
  53. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/test_registry.py +8 -6
  54. ignition_stack-0.5.0/tests/test_switch_profile_registry.py → ignition_stack-0.6.0/tests/test_switch_arch_registry.py +52 -27
  55. ignition_stack-0.6.0/tests/test_wizard_back_nav.py +338 -0
  56. ignition_stack-0.6.0/tests/test_wizard_breadcrumb.py +188 -0
  57. ignition_stack-0.6.0/tests/test_wizard_composer.py +747 -0
  58. ignition_stack-0.5.0/ignition_stack/__init__.py +0 -1
  59. ignition_stack-0.5.0/ignition_stack/profiles/__init__.py +0 -33
  60. ignition_stack-0.5.0/ignition_stack/profiles/mcp_n8n.py +0 -55
  61. ignition_stack-0.5.0/ignition_stack/profiles/standalone.py +0 -44
  62. ignition_stack-0.5.0/ignition_stack/templates/post-setup/reverse-proxy.md.j2 +0 -8
  63. ignition_stack-0.5.0/ignition_stack/templates/services/chariot/manifest.yaml +0 -30
  64. ignition_stack-0.5.0/ignition_stack/wizard.py +0 -634
  65. ignition_stack-0.5.0/tests/golden/profiles/mcp-n8n/docker-compose.yaml +0 -72
  66. ignition_stack-0.5.0/tests/test_postsetup.py +0 -156
  67. ignition_stack-0.5.0/tests/test_wizard_composer.py +0 -448
  68. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/.gitignore +0 -0
  69. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/LICENSE +0 -0
  70. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/builtin_modules.yaml +0 -0
  71. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/catalog/__init__.py +0 -0
  72. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/catalog/download.py +0 -0
  73. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/catalog/loader.py +0 -0
  74. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/catalog/schema.py +0 -0
  75. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/catalog/verify.py +0 -0
  76. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/commands/__init__.py +0 -0
  77. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/commands/modules.py +0 -0
  78. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/compose/__init__.py +0 -0
  79. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/compose/templates/header.yaml.j2 +0 -0
  80. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/compose/templates/services/bootstrap.yaml.j2 +0 -0
  81. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/config/__init__.py +0 -0
  82. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/lifecycle/__init__.py +0 -0
  83. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/lifecycle/cleanup.py +0 -0
  84. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/postsetup/__init__.py +0 -0
  85. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/services/__init__.py +0 -0
  86. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/services/loader.py +0 -0
  87. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/__init__.py +0 -0
  88. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/iiot/gateway-resources-mqtt-engine/config/resources/core/com.cirruslink.mqtt.engine.gateway/default-namespace/Sparkplug B/config.json +0 -0
  89. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/iiot/gateway-resources-mqtt-engine/config/resources/core/com.cirruslink.mqtt.engine.gateway/default-namespace/Sparkplug B/resource.json +0 -0
  90. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/iiot/gateway-resources-mqtt-engine/config/resources/core/com.cirruslink.mqtt.engine.gateway/general/config.json +0 -0
  91. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/iiot/gateway-resources-mqtt-engine/config/resources/core/com.cirruslink.mqtt.engine.gateway/general/resource.json +0 -0
  92. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/iiot/gateway-resources-mqtt-engine/config/resources/core/com.cirruslink.mqtt.engine.gateway/namespace-server-set/Sparkplug B-Default Set/config.json +0 -0
  93. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/iiot/gateway-resources-mqtt-engine/config/resources/core/com.cirruslink.mqtt.engine.gateway/namespace-server-set/Sparkplug B-Default Set/resource.json +0 -0
  94. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/iiot/gateway-resources-mqtt-engine/config/resources/core/com.cirruslink.mqtt.engine.gateway/server/Chariot SCADA/config.json.j2 +0 -0
  95. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/iiot/gateway-resources-mqtt-engine/config/resources/core/com.cirruslink.mqtt.engine.gateway/server/Chariot SCADA/resource.json +0 -0
  96. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/iiot/gateway-resources-mqtt-engine/config/resources/core/com.cirruslink.mqtt.engine.gateway/server-set/Default Set/config.json +0 -0
  97. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/iiot/gateway-resources-mqtt-engine/config/resources/core/com.cirruslink.mqtt.engine.gateway/server-set/Default Set/resource.json +0 -0
  98. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/iiot/gateway-resources-mqtt-transmission/config/resources/core/com.cirruslink.mqtt.transmission.gateway/general/config.json +0 -0
  99. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/iiot/gateway-resources-mqtt-transmission/config/resources/core/com.cirruslink.mqtt.transmission.gateway/general/resource.json +0 -0
  100. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/iiot/gateway-resources-mqtt-transmission/config/resources/core/com.cirruslink.mqtt.transmission.gateway/history-store/Default In-Memory Store/config.json +0 -0
  101. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/iiot/gateway-resources-mqtt-transmission/config/resources/core/com.cirruslink.mqtt.transmission.gateway/history-store/Default In-Memory Store/resource.json +0 -0
  102. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/iiot/gateway-resources-mqtt-transmission/config/resources/core/com.cirruslink.mqtt.transmission.gateway/server/Chariot SCADA/config.json.j2 +0 -0
  103. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/iiot/gateway-resources-mqtt-transmission/config/resources/core/com.cirruslink.mqtt.transmission.gateway/server/Chariot SCADA/resource.json +0 -0
  104. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/iiot/gateway-resources-mqtt-transmission/config/resources/core/com.cirruslink.mqtt.transmission.gateway/server-set/Default/config.json +0 -0
  105. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/iiot/gateway-resources-mqtt-transmission/config/resources/core/com.cirruslink.mqtt.transmission.gateway/server-set/Default/resource.json +0 -0
  106. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/iiot/gateway-resources-mqtt-transmission/config/resources/core/com.cirruslink.mqtt.transmission.gateway/transmitter/{{gateway}}/config.json.j2 +0 -0
  107. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/iiot/gateway-resources-mqtt-transmission/config/resources/core/com.cirruslink.mqtt.transmission.gateway/transmitter/{{gateway}}/resource.json +0 -0
  108. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/post-setup/_default.md.j2 +0 -0
  109. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/post-setup/device-connection.md.j2 +0 -0
  110. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/post-setup/gateway-network-link.md.j2 +0 -0
  111. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/post-setup/identity-provider.md.j2 +0 -0
  112. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/post-setup/kafka-connector.md.j2 +0 -0
  113. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/post-setup/mcp-module.md.j2 +0 -0
  114. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/post-setup/mqtt-engine-connection.md.j2 +0 -0
  115. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/post-setup/opc-ua-connection.md.j2 +0 -0
  116. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/post-setup/redundancy-pairing.md.j2 +0 -0
  117. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/redundancy/redundancy.xml.j2 +0 -0
  118. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/chariot/compose.yaml.j2 +0 -0
  119. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/chariot/seed/service/USAGE.md +0 -0
  120. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/chariot/seed/service/chariot-trial.sh +0 -0
  121. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/emqx/compose.yaml.j2 +0 -0
  122. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/emqx/seed/service/USAGE.md +0 -0
  123. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/hivemq/compose.yaml.j2 +0 -0
  124. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/hivemq/seed/service/USAGE.md +0 -0
  125. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/kafka/compose.yaml.j2 +0 -0
  126. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/kafka/seed/service/USAGE.md +0 -0
  127. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/keycloak/compose.yaml.j2 +0 -0
  128. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/keycloak/seed/gateway-resources/config/resources/core/ignition/identity-provider/keycloak/config.json +0 -0
  129. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/keycloak/seed/gateway-resources/config/resources/core/ignition/identity-provider/keycloak/resource.json +0 -0
  130. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/keycloak/seed/service/import/ignition-realm.json +0 -0
  131. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/mariadb/compose.yaml.j2 +0 -0
  132. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/mariadb/seed/service/initdb/00-create-extra-databases.sh +0 -0
  133. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/modbus-sim/compose.yaml.j2 +0 -0
  134. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/modbus-sim/seed/service/USAGE.md +0 -0
  135. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/mongo/compose.yaml.j2 +0 -0
  136. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/mongo/seed/service/initdb/01-demo-collection.js +0 -0
  137. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/mysql/compose.yaml.j2 +0 -0
  138. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/mysql/seed/service/initdb/00-create-extra-databases.sh +0 -0
  139. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/n8n/compose.yaml.j2 +0 -0
  140. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/n8n/seed/service/USAGE.md +0 -0
  141. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/opcua-sim/compose.yaml.j2 +0 -0
  142. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/opcua-sim/seed/service/USAGE.md +0 -0
  143. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/postgres/compose.yaml.j2 +0 -0
  144. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/postgres/seed/gateway-resources/config/resources/core/ignition/database-connection/db/config.json +0 -0
  145. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/postgres/seed/gateway-resources/config/resources/core/ignition/database-connection/db/resource.json +0 -0
  146. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/postgres/seed/gateway-resources/config/resources/core/ignition/secret-provider/internal-secret-provider/config.json +0 -0
  147. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/postgres/seed/gateway-resources/config/resources/core/ignition/secret-provider/internal-secret-provider/resource.json +0 -0
  148. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/postgres/seed/service/initdb/00-create-extra-databases.sh +0 -0
  149. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/rabbitmq/compose.yaml.j2 +0 -0
  150. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/rabbitmq/seed/service/enabled_plugins +0 -0
  151. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/standalone-postgres/docker-compose.yaml +0 -0
  152. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/standalone-postgres/scripts/docker-bootstrap.sh +0 -0
  153. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/standalone-postgres/services/ignition/config/resources/core/config-mode.json +0 -0
  154. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/standalone-postgres/services/ignition/config/resources/dev/config-mode.json +0 -0
  155. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/standalone-postgres/services/ignition/projects/.gitkeep +0 -0
  156. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/update_check.py +0 -0
  157. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/modules.yaml +0 -0
  158. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/pyproject.toml +0 -0
  159. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/__init__.py +0 -0
  160. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/conftest.py +0 -0
  161. {ignition_stack-0.5.0/tests/golden/profiles/standalone → ignition_stack-0.6.0/tests/golden/architectures/basic}/docker-compose.yaml +0 -0
  162. {ignition_stack-0.5.0/tests/golden/profiles → ignition_stack-0.6.0/tests/golden/architectures}/hub-and-spoke/docker-compose.yaml +0 -0
  163. {ignition_stack-0.5.0/tests/golden/profiles/scaleout → ignition_stack-0.6.0/tests/golden/architectures/scale-out}/docker-compose.yaml +0 -0
  164. {ignition_stack-0.5.0/tests/golden/profiles/scaleout-redundant → ignition_stack-0.6.0/tests/golden/architectures/scale-out-redundant}/docker-compose.yaml +0 -0
  165. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/golden/combos/hub-and-spoke-iiot-chariot/docker-compose.yaml +0 -0
  166. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/golden/combos/network-split/docker-compose.yaml +0 -0
  167. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/golden/combos/smoke-stack/docker-compose.yaml +0 -0
  168. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/golden/scaleout-skeleton/docker-compose.yaml +0 -0
  169. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/golden/services/chariot/docker-compose.yaml +0 -0
  170. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/golden/services/db-mariadb/docker-compose.yaml +0 -0
  171. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/golden/services/db-mongo/docker-compose.yaml +0 -0
  172. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/golden/services/db-mysql/docker-compose.yaml +0 -0
  173. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/golden/services/db-postgres/docker-compose.yaml +0 -0
  174. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/golden/services/emqx/docker-compose.yaml +0 -0
  175. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/golden/services/hivemq/docker-compose.yaml +0 -0
  176. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/golden/services/kafka/docker-compose.yaml +0 -0
  177. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/golden/services/keycloak/docker-compose.yaml +0 -0
  178. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/golden/services/modbus-sim/docker-compose.yaml +0 -0
  179. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/golden/services/n8n/docker-compose.yaml +0 -0
  180. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/golden/services/opcua-sim/docker-compose.yaml +0 -0
  181. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/golden/services/rabbitmq/docker-compose.yaml +0 -0
  182. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/golden/standalone-postgres/docker-compose.yaml +0 -0
  183. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/test_builtin_catalog_smoke.py +0 -0
  184. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/test_compose_engine.py +0 -0
  185. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/test_modules_catalog.py +0 -0
  186. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/test_modules_cli.py +0 -0
  187. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/test_service_catalog.py +0 -0
  188. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/test_service_catalog_smoke.py +0 -0
  189. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/test_update_check.py +0 -0
  190. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/verification/iiot-spike/README.md +0 -0
  191. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/verification/redundancy-spike/README.md +0 -0
  192. {ignition_stack-0.5.0 → ignition_stack-0.6.0}/verification/smoke/README.md +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ignition-stack
3
- Version: 0.5.0
3
+ Version: 0.6.0
4
4
  Summary: CLI that generates ready-to-run Docker Compose stacks for Ignition 8.3 SCADA demos and SE engagements
5
5
  Author-email: Eric Knorr <etknorr@gmail.com>
6
6
  License: MIT
@@ -33,7 +33,7 @@ Description-Content-Type: text/markdown
33
33
  [![Docs](https://github.com/ia-eknorr/ignition-stack/actions/workflows/docs.yml/badge.svg)](https://github.com/ia-eknorr/ignition-stack/actions/workflows/docs.yml)
34
34
  [![Documentation](https://img.shields.io/badge/docs-ia--eknorr.github.io-blue)](https://ia-eknorr.github.io/ignition-stack/)
35
35
 
36
- CLI that generates ready-to-run Docker Compose stacks for Ignition 8.3 SCADA demos and SE engagements. Picks an architecture profile, asks a few questions, writes a self-contained project with a hand-readable compose file, env, file-config seed resources, and a `POST-SETUP.md` listing only what could not be pre-seeded.
36
+ CLI that generates ready-to-run Docker Compose stacks for Ignition 8.3 SCADA demos and SE engagements. Pick a system architecture (`basic`, `scale-out`, or `hub-and-spoke`), layer on services, and it writes a self-contained project: a hand-readable compose file, `.env`, file-config seed resources, and a `POST-SETUP.md` listing only what could not be pre-seeded.
37
37
 
38
38
  See [`docs/docs/reference/seeding-matrix.md`](docs/docs/reference/seeding-matrix.md) for which Ignition 8.3 connection types can be provisioned from the filesystem and env on a live 8.3.6 gateway. Full documentation lives in the [`docs/`](docs/) Docusaurus site.
39
39
 
@@ -55,23 +55,25 @@ pipx install git+https://github.com/ia-eknorr/ignition-stack.git@<branch>
55
55
  Generate a project and bring it up:
56
56
 
57
57
  ```sh
58
- ignition-stack init demo
58
+ ignition-stack init demo --arch basic
59
59
  cd demo
60
60
  docker compose up -d
61
61
  ```
62
62
 
63
63
  The gateway reaches RUNNING with no UI prompts. The admin user is `admin / password` and the gateway is at `http://localhost:9088`. The default Postgres credentials are `ignition / ignition` on the `db` service.
64
64
 
65
+ Run `init` without `--arch` to walk the interactive wizard instead: it opens architecture-first, then layers database, edition, IIoT, services, and exposure on top, with a summary you can preview, tweak, or generate.
66
+
65
67
  Everything that ships in the generated project is hand-readable: `docker-compose.yaml`, `.env`, `scripts/docker-bootstrap.sh`, and a `services/ignition/` resources tree the gateway reads on first boot.
66
68
 
67
69
  ## Commands
68
70
 
69
71
  | Command | What it does |
70
72
  | --- | --- |
71
- | `init <name>` | Generate a project at `./<name>/` from a profile and a few prompts. |
73
+ | `init <name>` | Generate a project at `./<name>/` from an architecture and a few prompts. |
72
74
  | `modules` | Download, verify, and manage the `.modl` / JDBC catalog. |
73
75
  | `reset` | Re-run generation from an SE-demo project's recorded config. |
74
- | `switch-profile` | Reshape an SE-demo project under a different profile. |
76
+ | `switch-arch` | Reshape an SE-demo project under a different architecture. |
75
77
  | `wipe` | Remove this project's containers and volumes only. |
76
78
 
77
79
  See the [CLI reference](docs/docs/reference/cli.md) for every command, argument, and option.
@@ -4,7 +4,7 @@
4
4
  [![Docs](https://github.com/ia-eknorr/ignition-stack/actions/workflows/docs.yml/badge.svg)](https://github.com/ia-eknorr/ignition-stack/actions/workflows/docs.yml)
5
5
  [![Documentation](https://img.shields.io/badge/docs-ia--eknorr.github.io-blue)](https://ia-eknorr.github.io/ignition-stack/)
6
6
 
7
- CLI that generates ready-to-run Docker Compose stacks for Ignition 8.3 SCADA demos and SE engagements. Picks an architecture profile, asks a few questions, writes a self-contained project with a hand-readable compose file, env, file-config seed resources, and a `POST-SETUP.md` listing only what could not be pre-seeded.
7
+ CLI that generates ready-to-run Docker Compose stacks for Ignition 8.3 SCADA demos and SE engagements. Pick a system architecture (`basic`, `scale-out`, or `hub-and-spoke`), layer on services, and it writes a self-contained project: a hand-readable compose file, `.env`, file-config seed resources, and a `POST-SETUP.md` listing only what could not be pre-seeded.
8
8
 
9
9
  See [`docs/docs/reference/seeding-matrix.md`](docs/docs/reference/seeding-matrix.md) for which Ignition 8.3 connection types can be provisioned from the filesystem and env on a live 8.3.6 gateway. Full documentation lives in the [`docs/`](docs/) Docusaurus site.
10
10
 
@@ -26,23 +26,25 @@ pipx install git+https://github.com/ia-eknorr/ignition-stack.git@<branch>
26
26
  Generate a project and bring it up:
27
27
 
28
28
  ```sh
29
- ignition-stack init demo
29
+ ignition-stack init demo --arch basic
30
30
  cd demo
31
31
  docker compose up -d
32
32
  ```
33
33
 
34
34
  The gateway reaches RUNNING with no UI prompts. The admin user is `admin / password` and the gateway is at `http://localhost:9088`. The default Postgres credentials are `ignition / ignition` on the `db` service.
35
35
 
36
+ Run `init` without `--arch` to walk the interactive wizard instead: it opens architecture-first, then layers database, edition, IIoT, services, and exposure on top, with a summary you can preview, tweak, or generate.
37
+
36
38
  Everything that ships in the generated project is hand-readable: `docker-compose.yaml`, `.env`, `scripts/docker-bootstrap.sh`, and a `services/ignition/` resources tree the gateway reads on first boot.
37
39
 
38
40
  ## Commands
39
41
 
40
42
  | Command | What it does |
41
43
  | --- | --- |
42
- | `init <name>` | Generate a project at `./<name>/` from a profile and a few prompts. |
44
+ | `init <name>` | Generate a project at `./<name>/` from an architecture and a few prompts. |
43
45
  | `modules` | Download, verify, and manage the `.modl` / JDBC catalog. |
44
46
  | `reset` | Re-run generation from an SE-demo project's recorded config. |
45
- | `switch-profile` | Reshape an SE-demo project under a different profile. |
47
+ | `switch-arch` | Reshape an SE-demo project under a different architecture. |
46
48
  | `wipe` | Remove this project's containers and volumes only. |
47
49
 
48
50
  See the [CLI reference](docs/docs/reference/cli.md) for every command, argument, and option.
@@ -0,0 +1 @@
1
+ __version__ = "0.6.0"
@@ -0,0 +1,35 @@
1
+ """System architectures: pre-canned shapes that turn intent into config.
2
+
3
+ The slugs mirror Ignition's documented system architectures (Basic / Scale Out
4
+ / Hub and Spoke). Importing this package registers every built-in architecture
5
+ by side-effect so ``get_architecture("scale-out")`` works without explicit
6
+ module imports.
7
+ """
8
+
9
+ from ignition_stack.architectures import basic, hub_and_spoke, scale_out # noqa: F401
10
+ from ignition_stack.architectures.advisory import Advisory, spoke_advisory
11
+ from ignition_stack.architectures.base import (
12
+ Architecture,
13
+ ArchOptions,
14
+ apply_iiot,
15
+ build_architecture,
16
+ can_host_redundant_role,
17
+ get_architecture,
18
+ list_architectures,
19
+ mark_redundant,
20
+ )
21
+ from ignition_stack.architectures.hub_and_spoke import ArchitectureError
22
+
23
+ __all__ = [
24
+ "Advisory",
25
+ "ArchOptions",
26
+ "Architecture",
27
+ "ArchitectureError",
28
+ "apply_iiot",
29
+ "build_architecture",
30
+ "can_host_redundant_role",
31
+ "get_architecture",
32
+ "list_architectures",
33
+ "mark_redundant",
34
+ "spoke_advisory",
35
+ ]
@@ -1,6 +1,6 @@
1
1
  """Hub-and-spoke RAM advisory.
2
2
 
3
- The hub-and-spoke profile spins up one hub gateway plus *N* spokes; each
3
+ The hub-and-spoke architecture spins up one hub gateway plus *N* spokes; each
4
4
  Ignition gateway needs ~1.5 GB to run comfortably. The advisory turns that
5
5
  math into a proportional friction signal so SEs can see the cost of a
6
6
  large demo without being silently blocked when they really do want it:
@@ -1,19 +1,21 @@
1
- """Profile contract + options + registry.
1
+ """Architecture contract + options + registry.
2
2
 
3
- A *profile* is the small piece of code that turns the user's high-level
4
- intent ("scaleout", "hub-and-spoke with 3 spokes", "mcp-n8n demo") into a
5
- fully-formed :class:`ProjectConfig`. The compose engine and the dependency
6
- resolver are profile-agnostic; profiles only shape the inputs they take.
3
+ An *architecture* is the small piece of code that turns the user's high-level
4
+ intent ("scale-out", "hub-and-spoke with 3 spokes") into a fully-formed
5
+ :class:`ProjectConfig`. The slugs mirror Ignition's own documented system
6
+ architectures (Basic / Scale Out / Hub and Spoke). The compose engine and the
7
+ dependency resolver are architecture-agnostic; architectures only shape the
8
+ inputs they take.
7
9
 
8
10
  Two-stage pipeline:
9
11
 
10
- 1. Either the CLI flags or the wizard answers populate a
11
- :class:`ProfileOptions` and pick a profile slug.
12
- 2. ``build_profile(slug, name, options)`` looks up the profile and calls
13
- its ``build()`` method, returning a ``ProjectConfig`` that
12
+ 1. Either the CLI flags or the wizard answers populate an
13
+ :class:`ArchOptions` and pick an architecture slug.
14
+ 2. ``build_architecture(slug, name, options)`` looks up the architecture and
15
+ calls its ``build()`` method, returning a ``ProjectConfig`` that
14
16
  ``services.resolver.resolve()`` then expands the usual implicit deps on.
15
17
 
16
- Each profile is a small dataclass with three pieces:
18
+ Each architecture is a small dataclass with three pieces:
17
19
 
18
20
  - ``slug`` - the wizard/flag value users type.
19
21
  - ``summary`` - one-line description for the wizard menu + docs.
@@ -39,8 +41,8 @@ from ignition_stack.config import (
39
41
 
40
42
 
41
43
  @dataclass(frozen=True)
42
- class ProfileOptions:
43
- """Inputs each profile reads to shape the resolved config.
44
+ class ArchOptions:
45
+ """Inputs each architecture reads to shape the resolved config.
44
46
 
45
47
  Every field has a sensible default so callers only set what they
46
48
  actually care about. The wizard fills in many of these from prompts;
@@ -49,10 +51,10 @@ class ProfileOptions:
49
51
  """
50
52
 
51
53
  spokes: int = 3
52
- """Hub-and-spoke spoke count. Ignored by other profiles."""
54
+ """Hub-and-spoke spoke count. Ignored by other architectures."""
53
55
 
54
56
  frontends: int = 1
55
- """Scaleout frontend gateway count. Ignored by other profiles.
57
+ """Scale-out frontend gateway count. Ignored by other architectures.
56
58
 
57
59
  1 yields a single gateway named ``frontend``; N>1 yields
58
60
  ``frontend-1``..``frontend-N``. A ``backend`` gateway is always added
@@ -65,16 +67,16 @@ class ProfileOptions:
65
67
  network_split: bool | None = None
66
68
  """Tri-state override for the frontend/backend network split.
67
69
 
68
- ``None`` lets each profile apply its own default (scaleout splits,
70
+ ``None`` lets each architecture apply its own default (scale-out splits,
69
71
  hub-and-spoke does not). ``True``/``False`` force the split on or off
70
- regardless of the profile default.
72
+ regardless of the architecture default.
71
73
  """
72
74
 
73
75
  edge_role: str | None = None
74
76
  """Which gateway role (if any) runs the Edge edition.
75
77
 
76
- For scaleout this is typically 'frontend'; for hub-and-spoke it can
77
- be 'spoke' (every spoke runs Edge) or None. The profile is free to
78
+ For scale-out this is typically 'frontend'; for hub-and-spoke it can
79
+ be 'spoke' (every spoke runs Edge) or None. The architecture is free to
78
80
  apply its own default when this is None.
79
81
  """
80
82
 
@@ -85,26 +87,27 @@ class ProfileOptions:
85
87
  """SQL database for the stack. None = no database (gateway-only)."""
86
88
 
87
89
  services: tuple[str, ...] = ()
88
- """Additional service catalog slugs the user picked beyond profile defaults."""
90
+ """Additional service catalog slugs the user picked beyond architecture defaults."""
89
91
 
90
92
  redundant_role: str | None = None
91
93
  """Role (or gateway name) to make redundant, expanding it into a master +
92
94
  backup pair. ``None`` (default) builds no redundancy. Must name a singleton
93
- workhorse role (scaleout 'backend', hub-and-spoke 'hub', standalone
95
+ workhorse role (scale-out 'backend', hub-and-spoke 'hub', basic
94
96
  'gateway'); replicated tiers ('frontend', 'spoke') are rejected."""
95
97
 
96
98
  disable_builtins: tuple[str, ...] = ()
97
99
  """Built-in module slugs to turn off on every gateway in the stack.
98
100
 
99
101
  Empty (default) leaves all built-ins on. Applied uniformly by
100
- ``build_profile`` - the demo intent is "drop Vision/SFC everywhere", and
101
- per-gateway disabling stays a declarative-config-only feature. Slugs are
102
- validated against builtin_modules.yaml by ``GatewayConfig``."""
102
+ ``build_architecture`` - the demo intent is "drop Vision/SFC everywhere",
103
+ and per-gateway disabling stays a declarative-config-only feature. Slugs
104
+ are validated against builtin_modules.yaml by ``GatewayConfig``."""
103
105
 
104
106
  iiot: bool = False
105
107
  """Overlay an MQTT/Sparkplug IIoT pipeline (a broker + Cirrus Link
106
- Transmission/Engine) onto the stack. Off by default; ``build_profile`` calls
107
- :func:`apply_iiot` when this is set, defaulting the broker to ``chariot``."""
108
+ Transmission/Engine) onto the stack. Off by default; ``build_architecture``
109
+ calls :func:`apply_iiot` when this is set, defaulting the broker to
110
+ ``chariot``."""
108
111
 
109
112
  iiot_broker: str | None = None
110
113
  """MQTT broker slug the IIoT overlay wires to. ``None`` with ``iiot`` on
@@ -112,41 +115,41 @@ class ProfileOptions:
112
115
  when ``iiot`` is False. Validated against the catalog by :func:`apply_iiot`."""
113
116
 
114
117
 
115
- class Profile(Protocol):
116
- """A factory that turns ``ProfileOptions`` into a ``ProjectConfig``."""
118
+ class Architecture(Protocol):
119
+ """A factory that turns ``ArchOptions`` into a ``ProjectConfig``."""
117
120
 
118
121
  slug: str
119
122
  summary: str
120
123
 
121
- def build(self, name: str, options: ProfileOptions) -> ProjectConfig: ...
124
+ def build(self, name: str, options: ArchOptions) -> ProjectConfig: ...
122
125
 
123
126
 
124
- # Registry populated by the profile modules at import time. Keep alphabetical
125
- # insertion-order for stable wizard menus + --help listings.
126
- _REGISTRY: dict[str, Profile] = {}
127
+ # Registry populated by the architecture modules at import time. Keep
128
+ # alphabetical insertion-order for stable wizard menus + --help listings.
129
+ _REGISTRY: dict[str, Architecture] = {}
127
130
 
128
131
 
129
- def register(profile: Profile) -> Profile:
130
- """Register a profile by slug. Returns the profile so module-level uses
131
- can write ``standalone = register(StandaloneProfile())``.
132
+ def register(architecture: Architecture) -> Architecture:
133
+ """Register an architecture by slug. Returns the architecture so module-level
134
+ uses can write ``basic = register(BasicArchitecture())``.
132
135
  """
133
- if profile.slug in _REGISTRY:
134
- raise ValueError(f"profile '{profile.slug}' is already registered")
135
- _REGISTRY[profile.slug] = profile
136
- return profile
136
+ if architecture.slug in _REGISTRY:
137
+ raise ValueError(f"architecture '{architecture.slug}' is already registered")
138
+ _REGISTRY[architecture.slug] = architecture
139
+ return architecture
137
140
 
138
141
 
139
- def get_profile(slug: str) -> Profile:
140
- """Look up a registered profile by slug. Raises ``KeyError`` if unknown."""
142
+ def get_architecture(slug: str) -> Architecture:
143
+ """Look up a registered architecture by slug. Raises ``KeyError`` if unknown."""
141
144
  try:
142
145
  return _REGISTRY[slug]
143
146
  except KeyError as exc:
144
147
  known = ", ".join(sorted(_REGISTRY))
145
- raise KeyError(f"unknown profile '{slug}'; known profiles: {known}") from exc
148
+ raise KeyError(f"unknown architecture '{slug}'; known architectures: {known}") from exc
146
149
 
147
150
 
148
- def list_profiles() -> list[Profile]:
149
- """All registered profiles in stable insertion order."""
151
+ def list_architectures() -> list[Architecture]:
152
+ """All registered architectures in stable insertion order."""
150
153
  return list(_REGISTRY.values())
151
154
 
152
155
 
@@ -170,9 +173,9 @@ def _matching_gateways(config: ProjectConfig, redundant_role: str) -> list:
170
173
  def can_host_redundant_role(config: ProjectConfig, redundant_role: str) -> bool:
171
174
  """True when ``config`` has exactly one gateway that can be paired as master.
172
175
 
173
- ``switch-profile`` uses this to decide whether redundancy intent recovered
174
- from the old stack can carry to the target profile, without raising the way
175
- :func:`mark_redundant` does - a profile-specific role (e.g. standalone's
176
+ ``switch-arch`` uses this to decide whether redundancy intent recovered from
177
+ the old stack can carry to the target architecture, without raising the way
178
+ :func:`mark_redundant` does - an architecture-specific role (e.g. basic's
176
179
  ``gateway``) simply may not exist in the destination.
177
180
  """
178
181
  if redundant_role in _NON_REDUNDANT_ROLES:
@@ -186,7 +189,7 @@ def mark_redundant(config: ProjectConfig, redundant_role: str | None) -> Project
186
189
  Returns ``config`` unchanged when ``redundant_role`` is None. The expansion
187
190
  into a master+backup pair happens later in
188
191
  :func:`ignition_stack.services.resolver.resolve`; this only marks which
189
- gateway to pair, so the same logic serves every profile and the wizard.
192
+ gateway to pair, so the same logic serves every architecture and the wizard.
190
193
 
191
194
  Raises ``ValueError`` (surfaced by the CLI as a usage error, exit code 2)
192
195
  when the role is a replicated frontend/spoke tier, unknown, or ambiguous
@@ -197,7 +200,7 @@ def mark_redundant(config: ProjectConfig, redundant_role: str | None) -> Project
197
200
  if redundant_role in _NON_REDUNDANT_ROLES:
198
201
  raise ValueError(
199
202
  f"role '{redundant_role}' is horizontally replicated, not paired; "
200
- "redundancy applies to a single gateway (e.g. a scaleout 'backend' "
203
+ "redundancy applies to a single gateway (e.g. a scale-out 'backend' "
201
204
  "or a hub-and-spoke 'hub'), never to frontends or spokes"
202
205
  )
203
206
  matches = _matching_gateways(config, redundant_role)
@@ -211,15 +214,16 @@ def mark_redundant(config: ProjectConfig, redundant_role: str | None) -> Project
211
214
  return config
212
215
 
213
216
 
214
- def build_profile(slug: str, name: str, options: ProfileOptions) -> ProjectConfig:
215
- """Materialize a ``ProjectConfig`` for the named profile.
217
+ def build_architecture(slug: str, name: str, options: ArchOptions) -> ProjectConfig:
218
+ """Materialize a ``ProjectConfig`` for the named architecture.
216
219
 
217
- The profile builds the base topology; ``mark_redundant`` then stamps the
218
- redundancy master when ``options.redundant_role`` is set, leaving the
219
- resolver to expand the pair. Keeping the stamp here (not in each profile)
220
- means one eligibility rule serves every profile and the wizard alike.
220
+ The architecture builds the base topology; ``mark_redundant`` then stamps
221
+ the redundancy master when ``options.redundant_role`` is set, leaving the
222
+ resolver to expand the pair. Keeping the stamp here (not in each
223
+ architecture) means one eligibility rule serves every architecture and the
224
+ wizard alike.
221
225
  """
222
- config = get_profile(slug).build(name, options)
226
+ config = get_architecture(slug).build(name, options)
223
227
  config = mark_redundant(config, options.redundant_role)
224
228
  config = apply_disable_builtins(config, options.disable_builtins)
225
229
  broker = (options.iiot_broker or _IIOT_DEFAULT_BROKER) if options.iiot else None
@@ -230,9 +234,9 @@ def apply_disable_builtins(config: ProjectConfig, disable_builtins: tuple[str, .
230
234
  """Stamp ``disable_builtins`` onto every gateway in ``config``.
231
235
 
232
236
  Applied centrally (like :func:`mark_redundant`) so one rule serves every
233
- profile and the wizard. Uniform across gateways: the demo intent is to drop
234
- a module everywhere, and a redundant pair must agree on its module set. The
235
- resolver later copies the list onto any expanded backup node.
237
+ architecture and the wizard. Uniform across gateways: the demo intent is to
238
+ drop a module everywhere, and a redundant pair must agree on its module set.
239
+ The resolver later copies the list onto any expanded backup node.
236
240
  """
237
241
  if not disable_builtins:
238
242
  return config
@@ -254,9 +258,9 @@ def apply_disable_builtins(config: ProjectConfig, disable_builtins: tuple[str, .
254
258
  _IIOT_DEFAULT_BROKER = "chariot"
255
259
 
256
260
  # Gateway roles that run MQTT Transmission (edge-side: publish Sparkplug to the
257
- # broker) versus MQTT Engine (central: subscribe and aggregate). A
258
- # standalone/mcp-n8n shape has neither role, so its single gateway runs both for
259
- # a self-contained demo loop through the broker.
261
+ # broker) versus MQTT Engine (central: subscribe and aggregate). A basic shape
262
+ # has neither role, so its single gateway runs both for a self-contained demo
263
+ # loop through the broker.
260
264
  _TRANSMISSION_ROLES = frozenset({"spoke", "frontend"})
261
265
  _ENGINE_ROLES = frozenset({"hub", "backend"})
262
266
 
@@ -273,9 +277,9 @@ def apply_iiot(config: ProjectConfig, broker: str | None) -> ProjectConfig:
273
277
  plus the Transmission module (they publish Sparkplug to the broker);
274
278
  - ``hub`` / ``backend`` gateways get an ``mqtt-engine`` attachment plus the
275
279
  Engine module (they subscribe and aggregate);
276
- - if NO gateway carries any of those roles (standalone / mcp-n8n shapes), the
277
- single/first gateway gets BOTH attachments + both modules - a self-contained
278
- demo loop through the broker.
280
+ - if NO gateway carries any of those roles (a basic shape), the single/first
281
+ gateway gets BOTH attachments + both modules - a self-contained demo loop
282
+ through the broker.
279
283
 
280
284
  Brokers are not ``never_on_edge``, so an Edge spoke attaching with role
281
285
  ``mqtt-transmission`` is correct and expected. Idempotent: guards on
@@ -0,0 +1,45 @@
1
+ """Basic architecture: one full Ignition gateway + optional SQL DB.
2
+
3
+ This mirrors Ignition's documented Basic architecture - a single gateway
4
+ that does everything - surfaced as a named architecture so the wizard can
5
+ offer it alongside the multi-gateway architectures. The only knobs are the
6
+ database choice (defaults to Postgres) and the optional reverse-proxy
7
+ scaffold.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from dataclasses import dataclass
13
+
14
+ from ignition_stack.architectures.base import Architecture, ArchOptions, register
15
+ from ignition_stack.config import DatabaseConfig, GatewayConfig, ProjectConfig
16
+
17
+
18
+ @dataclass(frozen=True)
19
+ class BasicArchitecture:
20
+ slug: str = "basic"
21
+ summary: str = "One full Ignition 8.3 gateway + Postgres. The default starter stack."
22
+
23
+ def build(self, name: str, options: ArchOptions) -> ProjectConfig:
24
+ gateway = GatewayConfig()
25
+ if options.edge_role in {"gateway", "basic"}:
26
+ gateway = gateway.model_copy(update={"ignition_edition": "edge"})
27
+
28
+ return ProjectConfig(
29
+ name=name,
30
+ architecture=self.slug,
31
+ gateways=[gateway],
32
+ database=_database(options),
33
+ services=list(options.services),
34
+ reverse_proxy=options.reverse_proxy,
35
+ )
36
+
37
+
38
+ def _database(options: ArchOptions) -> DatabaseConfig | None:
39
+ if options.database_kind is None:
40
+ return None
41
+ return DatabaseConfig(kind=options.database_kind)
42
+
43
+
44
+ # Side-effect: registers this architecture when the module is imported.
45
+ architecture: Architecture = register(BasicArchitecture())
@@ -1,25 +1,25 @@
1
- """Carry a resolved service registry across a ``switch-profile`` reshape.
1
+ """Carry a resolved service registry across a ``switch-arch`` reshape.
2
2
 
3
- ``switch-profile`` rebuilds a stack under a new architecture profile. The
4
- profile only knows how to lay down topology + the inputs ``ProfileOptions``
5
- carries; it cannot know about the *richer* registry shapes a hand-authored or
6
- Custom-wizard config can hold:
3
+ ``switch-arch`` rebuilds a stack under a new architecture. The architecture
4
+ only knows how to lay down topology + the inputs ``ArchOptions`` carries; it
5
+ cannot know about the *richer* registry shapes a hand-authored or
6
+ composer-built config can hold:
7
7
 
8
8
  - a custom instance ``id`` that differs from its service slug;
9
9
  - per-instance ``image`` / ``env`` / ``user`` / ``password`` overrides;
10
- - a second database (distinct kind) beyond the profile's default;
10
+ - a second database (distinct kind) beyond the architecture's default;
11
11
  - partial / role-specific attachment sets (an instance attached to only some
12
12
  gateways, not fanned to all).
13
13
 
14
- Anything *not* expressible through ``ProfileOptions.services`` /
15
- ``database_kind`` rides here instead: :func:`carry_registry` re-grafts those
16
- instances onto the freshly built config and re-maps their attachments by role
17
- into the new topology. Attachments whose source gateway has no counterpart in
18
- the target topology - or that would violate a new-topology invariant - are
19
- **dropped with a printed advisory** rather than failing the reshape.
14
+ Anything *not* expressible through ``ArchOptions.services`` / ``database_kind``
15
+ rides here instead: :func:`carry_registry` re-grafts those instances onto the
16
+ freshly built config and re-maps their attachments by role into the new
17
+ topology. Attachments whose source gateway has no counterpart in the target
18
+ topology - or that would violate a new-topology invariant - are **dropped with
19
+ a printed advisory** rather than failing the reshape.
20
20
 
21
- The IIoT overlay is *not* carried here: ``switch-profile`` recovers IIoT intent
22
- into ``ProfileOptions.iiot`` / ``iiot_broker`` so ``apply_iiot`` re-wires the
21
+ The IIoT overlay is *not* carried here: ``switch-arch`` recovers IIoT intent
22
+ into ``ArchOptions.iiot`` / ``iiot_broker`` so ``apply_iiot`` re-wires the
23
23
  broker + Transmission/Engine roles natively in the new topology. The broker
24
24
  instance and its ``mqtt-transmission`` / ``mqtt-engine`` attachments are
25
25
  therefore skipped here to avoid double-wiring.
@@ -40,9 +40,9 @@ from ignition_stack.services.loader import load_all_services
40
40
  from ignition_stack.services.manifest import ServiceManifest
41
41
 
42
42
  # Roles that aggregate centrally (full gateways) versus roles that sit on the
43
- # edge side and publish/scale out. A role-less gateway (standalone / mcp-n8n)
44
- # is central - it is the one full gateway in its topology. The two-way name
45
- # preference (hub<->backend, spoke<->frontend) is tried before the class match.
43
+ # edge side and publish/scale out. A role-less gateway (basic) is central - it
44
+ # is the one full gateway in its topology. The two-way name preference
45
+ # (hub<->backend, spoke<->frontend) is tried before the class match.
46
46
  _CENTRAL_ROLES = frozenset({"hub", "backend"})
47
47
  _EDGE_SIDE_ROLES = frozenset({"spoke", "frontend"})
48
48
  _ROLE_COUNTERPART = {
@@ -52,7 +52,7 @@ _ROLE_COUNTERPART = {
52
52
  "frontend": "spoke",
53
53
  }
54
54
 
55
- # Attachment roles the IIoT overlay owns; carried via ProfileOptions.iiot, never
55
+ # Attachment roles the IIoT overlay owns; carried via ArchOptions.iiot, never
56
56
  # re-grafted here (apply_iiot re-wires them in the new topology).
57
57
  _IIOT_ROLES = frozenset({"mqtt-transmission", "mqtt-engine"})
58
58
 
@@ -66,13 +66,13 @@ def is_default_representable(
66
66
  config: ProjectConfig,
67
67
  catalog: dict[str, ServiceManifest],
68
68
  ) -> bool:
69
- """True when ``instance`` is faithfully carried by ``ProfileOptions`` alone.
69
+ """True when ``instance`` is faithfully carried by ``ArchOptions`` alone.
70
70
 
71
71
  The "today's behavior" carry: an instance whose ``id`` equals its service
72
72
  slug, carries no per-instance override, and is attached as a plain
73
73
  ``consumer`` on every *eligible* gateway (every gateway, minus the Edge
74
74
  gateways a ``never_on_edge`` service skips). Such an instance is fully
75
- reproduced by listing its slug in ``ProfileOptions.services`` and letting
75
+ reproduced by listing its slug in ``ArchOptions.services`` and letting
76
76
  the resolver re-lower it, so it does not need re-grafting.
77
77
 
78
78
  Databases are never representable through ``services`` (they ride
@@ -103,7 +103,7 @@ def is_default_representable(
103
103
 
104
104
 
105
105
  def database_carried_by_kind(config: ProjectConfig, catalog: dict[str, ServiceManifest]) -> ServiceInstance | None:
106
- """The primary database iff it rides ``ProfileOptions.database_kind`` cleanly.
106
+ """The primary database iff it rides ``ArchOptions.database_kind`` cleanly.
107
107
 
108
108
  ``database_kind`` always rebuilds a database with the canonical id ``db``,
109
109
  default credentials, the kind's default image, and a consumer attachment on
@@ -149,8 +149,8 @@ def detect_iiot_broker(config: ProjectConfig) -> str | None:
149
149
  Detection is by attachment role: a stack carrying any ``mqtt-transmission``
150
150
  or ``mqtt-engine`` attachment was built with ``apply_iiot``; the instance
151
151
  those attachments reference is the broker. Returning its *service slug*
152
- (not its id) lets ``switch-profile`` re-apply the overlay with
153
- ``ProfileOptions.iiot_broker``.
152
+ (not its id) lets ``switch-arch`` re-apply the overlay with
153
+ ``ArchOptions.iiot_broker``.
154
154
  """
155
155
  by_id = {inst.id: inst for inst in config.service_instances}
156
156
  for gw in config.gateways:
@@ -200,7 +200,7 @@ def carry_registry(
200
200
  """Re-graft ``old_config``'s richer registry onto the freshly built config.
201
201
 
202
202
  Mutates and returns ``new_config``. For every old instance that is *not*
203
- already default-representable (carried via ``ProfileOptions``) and *not* the
203
+ already default-representable (carried via ``ArchOptions``) and *not* the
204
204
  IIoT broker, the instance is added to the registry (preserving id +
205
205
  overrides) and its non-IIoT attachments are re-mapped by role into the new
206
206
  topology. Attachments are dropped - each with a printed
@@ -212,7 +212,7 @@ def carry_registry(
212
212
 
213
213
  The order matters: instances are added before attachments so an attachment's
214
214
  referenced instance always exists, and the per-gateway db cap is checked
215
- against attachments already present (including those the profile/lowering
215
+ against attachments already present (including those the architecture/lowering
216
216
  produced) so the carry never over-attaches.
217
217
  """
218
218
  catalog = load_all_services()
@@ -222,7 +222,7 @@ def carry_registry(
222
222
  dropped_instance: dict[str, str] = {}
223
223
 
224
224
  for old_inst in old_config.service_instances:
225
- # The IIoT broker rides ProfileOptions.iiot; apply_iiot re-adds it.
225
+ # The IIoT broker rides ArchOptions.iiot; apply_iiot re-adds it.
226
226
  if iiot_broker_slug is not None and old_inst.service == iiot_broker_slug:
227
227
  continue
228
228
  if is_default_representable(old_inst, old_config, catalog):
@@ -303,7 +303,7 @@ def _try_attach(
303
303
  """
304
304
  manifest = catalog[inst.service]
305
305
  if any(a.instance == inst.id and a.role == att.role for a in target.services):
306
- return # already attached (idempotent / profile already produced it)
306
+ return # already attached (idempotent / architecture already produced it)
307
307
 
308
308
  if manifest.placement.never_on_edge and target.ignition_edition == "edge":
309
309
  dropped.setdefault(inst.id, []).append(f"'{inst.service}' must not run on Edge gateway '{target.name}'")