ignition-stack 0.1.0__py3-none-any.whl

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 (100) hide show
  1. ignition_stack/__init__.py +1 -0
  2. ignition_stack/catalog/__init__.py +10 -0
  3. ignition_stack/catalog/download.py +145 -0
  4. ignition_stack/catalog/loader.py +65 -0
  5. ignition_stack/catalog/schema.py +158 -0
  6. ignition_stack/catalog/verify.py +72 -0
  7. ignition_stack/cli.py +354 -0
  8. ignition_stack/commands/__init__.py +0 -0
  9. ignition_stack/commands/modules.py +178 -0
  10. ignition_stack/completion.py +46 -0
  11. ignition_stack/compose/__init__.py +4 -0
  12. ignition_stack/compose/engine.py +397 -0
  13. ignition_stack/compose/templates/footer.yaml.j2 +12 -0
  14. ignition_stack/compose/templates/header.yaml.j2 +14 -0
  15. ignition_stack/compose/templates/services/bootstrap.yaml.j2 +19 -0
  16. ignition_stack/compose/templates/services/ignition.yaml.j2 +35 -0
  17. ignition_stack/compose/writer.py +428 -0
  18. ignition_stack/config/__init__.py +8 -0
  19. ignition_stack/config/schema.py +311 -0
  20. ignition_stack/lifecycle/__init__.py +31 -0
  21. ignition_stack/lifecycle/cleanup.py +71 -0
  22. ignition_stack/lifecycle/record.py +67 -0
  23. ignition_stack/lifecycle/regenerate.py +62 -0
  24. ignition_stack/modules.yaml +83 -0
  25. ignition_stack/postsetup/__init__.py +3 -0
  26. ignition_stack/postsetup/generator.py +187 -0
  27. ignition_stack/profiles/__init__.py +27 -0
  28. ignition_stack/profiles/advisory.py +132 -0
  29. ignition_stack/profiles/base.py +108 -0
  30. ignition_stack/profiles/hub_and_spoke.py +87 -0
  31. ignition_stack/profiles/mcp_n8n.py +55 -0
  32. ignition_stack/profiles/scaleout.py +65 -0
  33. ignition_stack/profiles/standalone.py +44 -0
  34. ignition_stack/services/__init__.py +25 -0
  35. ignition_stack/services/loader.py +69 -0
  36. ignition_stack/services/manifest.py +106 -0
  37. ignition_stack/services/resolver.py +133 -0
  38. ignition_stack/templates/__init__.py +0 -0
  39. ignition_stack/templates/post-setup/_default.md.j2 +12 -0
  40. ignition_stack/templates/post-setup/device-connection.md.j2 +11 -0
  41. ignition_stack/templates/post-setup/gateway-network-link.md.j2 +18 -0
  42. ignition_stack/templates/post-setup/identity-provider.md.j2 +13 -0
  43. ignition_stack/templates/post-setup/kafka-connector.md.j2 +11 -0
  44. ignition_stack/templates/post-setup/mcp-module.md.j2 +11 -0
  45. ignition_stack/templates/post-setup/mqtt-engine-connection.md.j2 +11 -0
  46. ignition_stack/templates/post-setup/opc-ua-connection.md.j2 +11 -0
  47. ignition_stack/templates/post-setup/reverse-proxy.md.j2 +8 -0
  48. ignition_stack/templates/services/chariot/compose.yaml.j2 +17 -0
  49. ignition_stack/templates/services/chariot/manifest.yaml +22 -0
  50. ignition_stack/templates/services/chariot/seed/service/USAGE.md +14 -0
  51. ignition_stack/templates/services/emqx/compose.yaml.j2 +16 -0
  52. ignition_stack/templates/services/emqx/manifest.yaml +21 -0
  53. ignition_stack/templates/services/emqx/seed/service/USAGE.md +11 -0
  54. ignition_stack/templates/services/hivemq/compose.yaml.j2 +12 -0
  55. ignition_stack/templates/services/hivemq/manifest.yaml +19 -0
  56. ignition_stack/templates/services/hivemq/seed/service/USAGE.md +16 -0
  57. ignition_stack/templates/services/kafka/compose.yaml.j2 +27 -0
  58. ignition_stack/templates/services/kafka/manifest.yaml +20 -0
  59. ignition_stack/templates/services/kafka/seed/service/USAGE.md +17 -0
  60. ignition_stack/templates/services/keycloak/compose.yaml.j2 +31 -0
  61. ignition_stack/templates/services/keycloak/manifest.yaml +25 -0
  62. ignition_stack/templates/services/keycloak/seed/service/import/ignition-realm.json +31 -0
  63. ignition_stack/templates/services/mariadb/compose.yaml.j2 +26 -0
  64. ignition_stack/templates/services/mariadb/manifest.yaml +15 -0
  65. ignition_stack/templates/services/mariadb/seed/service/initdb/00-create-extra-databases.sh +19 -0
  66. ignition_stack/templates/services/modbus-sim/compose.yaml.j2 +12 -0
  67. ignition_stack/templates/services/modbus-sim/manifest.yaml +19 -0
  68. ignition_stack/templates/services/modbus-sim/seed/service/USAGE.md +10 -0
  69. ignition_stack/templates/services/mongo/compose.yaml.j2 +21 -0
  70. ignition_stack/templates/services/mongo/manifest.yaml +14 -0
  71. ignition_stack/templates/services/mongo/seed/service/initdb/01-demo-collection.js +10 -0
  72. ignition_stack/templates/services/mysql/compose.yaml.j2 +26 -0
  73. ignition_stack/templates/services/mysql/manifest.yaml +15 -0
  74. ignition_stack/templates/services/mysql/seed/service/initdb/00-create-extra-databases.sh +19 -0
  75. ignition_stack/templates/services/n8n/compose.yaml.j2 +16 -0
  76. ignition_stack/templates/services/n8n/manifest.yaml +16 -0
  77. ignition_stack/templates/services/n8n/seed/service/USAGE.md +11 -0
  78. ignition_stack/templates/services/opcua-sim/compose.yaml.j2 +13 -0
  79. ignition_stack/templates/services/opcua-sim/manifest.yaml +21 -0
  80. ignition_stack/templates/services/opcua-sim/seed/service/USAGE.md +11 -0
  81. ignition_stack/templates/services/postgres/compose.yaml.j2 +26 -0
  82. ignition_stack/templates/services/postgres/manifest.yaml +21 -0
  83. ignition_stack/templates/services/postgres/seed/gateway-resources/config/resources/core/ignition/database-connection/db/config.json +32 -0
  84. ignition_stack/templates/services/postgres/seed/gateway-resources/config/resources/core/ignition/database-connection/db/resource.json +19 -0
  85. ignition_stack/templates/services/postgres/seed/gateway-resources/config/resources/core/ignition/secret-provider/internal-secret-provider/config.json +19 -0
  86. ignition_stack/templates/services/postgres/seed/gateway-resources/config/resources/core/ignition/secret-provider/internal-secret-provider/resource.json +19 -0
  87. ignition_stack/templates/services/postgres/seed/service/initdb/00-create-extra-databases.sh +17 -0
  88. ignition_stack/templates/services/rabbitmq/compose.yaml.j2 +19 -0
  89. ignition_stack/templates/services/rabbitmq/manifest.yaml +23 -0
  90. ignition_stack/templates/services/rabbitmq/seed/service/enabled_plugins +1 -0
  91. ignition_stack/templates/standalone-postgres/docker-compose.yaml +62 -0
  92. ignition_stack/templates/standalone-postgres/scripts/docker-bootstrap.sh +78 -0
  93. ignition_stack/templates/standalone-postgres/services/ignition/config/resources/core/config-mode.json +7 -0
  94. ignition_stack/templates/standalone-postgres/services/ignition/config/resources/dev/config-mode.json +7 -0
  95. ignition_stack/templates/standalone-postgres/services/ignition/projects/.gitkeep +0 -0
  96. ignition_stack/wizard.py +362 -0
  97. ignition_stack-0.1.0.dist-info/METADATA +97 -0
  98. ignition_stack-0.1.0.dist-info/RECORD +100 -0
  99. ignition_stack-0.1.0.dist-info/WHEEL +4 -0
  100. ignition_stack-0.1.0.dist-info/entry_points.txt +2 -0
@@ -0,0 +1,27 @@
1
+ {{ name }}:
2
+ image: {{ image_ref }}
3
+ hostname: {{ name }}
4
+ container_name: {{ container_name_ref }}
5
+ environment:
6
+ TZ: ${TZ}
7
+ CLUSTER_ID: ${KAFKA_CLUSTER_ID}
8
+ KAFKA_NODE_ID: "1"
9
+ KAFKA_PROCESS_ROLES: broker,controller
10
+ KAFKA_CONTROLLER_QUORUM_VOTERS: 1@{{ name }}:29093
11
+ KAFKA_LISTENERS: PLAINTEXT://0.0.0.0:9092,CONTROLLER://0.0.0.0:29093,PLAINTEXT_HOST://0.0.0.0:9094
12
+ KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://{{ name }}:9092,PLAINTEXT_HOST://localhost:${KAFKA_PORT}
13
+ KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,CONTROLLER:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT
14
+ KAFKA_CONTROLLER_LISTENER_NAMES: CONTROLLER
15
+ KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT
16
+ KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: "1"
17
+ KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: "1"
18
+ KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: "1"
19
+ KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: "0"
20
+ ports:
21
+ - "${KAFKA_PORT}:9094"
22
+ {%- if networks %}
23
+ networks:
24
+ {%- for net in networks %}
25
+ - {{ net }}
26
+ {%- endfor %}
27
+ {%- endif %}
@@ -0,0 +1,20 @@
1
+ # Kafka - single-node KRaft broker. Ignition talks to it via the built-in
2
+ # Kafka connector / Event Streams modules (no third-party .modl needed).
3
+ name: kafka
4
+ kind: streaming
5
+ summary: Single-node Kafka broker (KRaft mode) for Event Streams demos.
6
+ image: confluentinc/cp-kafka:8.2.1
7
+ image_env: KAFKA_IMAGE
8
+ network: backend
9
+ provides:
10
+ - kafka-broker
11
+ requires: []
12
+ env:
13
+ KAFKA_PORT: "9094"
14
+ KAFKA_CLUSTER_ID: 4L6g3nShT-eMCtK--X86sw
15
+ seeds_gateway_resources: false
16
+ post_setup:
17
+ - connection: kafka-connector
18
+ reason: >-
19
+ Configure Ignition's built-in Kafka connector to point at
20
+ kafka:9092 in the gateway once the broker is up.
@@ -0,0 +1,17 @@
1
+ # Kafka (single-node KRaft)
2
+
3
+ A single-node Kafka broker. No ZooKeeper - it runs as its own KRaft controller.
4
+
5
+ - In-stack bootstrap server (for the gateway and other containers):
6
+ `kafka:9092`.
7
+ - Host bootstrap server (for local tools): `localhost:${KAFKA_PORT}`.
8
+
9
+ Create a topic and produce from the host:
10
+
11
+ ```bash
12
+ docker compose exec kafka kafka-topics --bootstrap-server kafka:9092 \
13
+ --create --topic demo --partitions 1 --replication-factor 1
14
+ ```
15
+
16
+ Ignition connects via the built-in Kafka connector (Event Streams) - point it
17
+ at `kafka:9092`. No third-party module is required.
@@ -0,0 +1,31 @@
1
+ {{ name }}:
2
+ image: {{ image_ref }}
3
+ hostname: {{ name }}
4
+ container_name: {{ container_name_ref }}
5
+ {%- if depends_on %}
6
+ depends_on:
7
+ {%- for dep in depends_on %}
8
+ {{ dep }}:
9
+ condition: service_healthy
10
+ {%- endfor %}
11
+ {%- endif %}
12
+ command: ["start-dev", "--import-realm"]
13
+ environment:
14
+ TZ: ${TZ}
15
+ KC_BOOTSTRAP_ADMIN_USERNAME: ${KEYCLOAK_ADMIN_USER}
16
+ KC_BOOTSTRAP_ADMIN_PASSWORD: ${KEYCLOAK_ADMIN_PASSWORD}
17
+ KC_DB: {{ db_kind }}
18
+ KC_DB_URL_HOST: ${DB_HOST}
19
+ KC_DB_URL_DATABASE: keycloak
20
+ KC_DB_USERNAME: ${DB_USER}
21
+ KC_DB_PASSWORD: ${DB_PASSWORD}
22
+ ports:
23
+ - "${KEYCLOAK_HTTP_PORT}:8080"
24
+ volumes:
25
+ - ./services/{{ name }}/import:/opt/keycloak/data/import:ro
26
+ {%- if networks %}
27
+ networks:
28
+ {%- for net in networks %}
29
+ - {{ net }}
30
+ {%- endfor %}
31
+ {%- endif %}
@@ -0,0 +1,25 @@
1
+ # Keycloak - OIDC/SAML identity provider for gateway single sign-on.
2
+ # Requires a SQL database; the resolver auto-adds Postgres (and a dedicated
3
+ # "keycloak" logical database) when none is selected.
4
+ name: keycloak
5
+ kind: idp
6
+ summary: Keycloak identity provider for gateway OIDC/SAML single sign-on.
7
+ image: quay.io/keycloak/keycloak:26.6.2
8
+ image_env: KEYCLOAK_IMAGE
9
+ network: backend
10
+ provides:
11
+ - oidc-idp
12
+ requires:
13
+ - sql-database
14
+ env:
15
+ KEYCLOAK_ADMIN_USER: admin
16
+ KEYCLOAK_ADMIN_PASSWORD: admin
17
+ KEYCLOAK_HTTP_PORT: "8081"
18
+ seeds_gateway_resources: false
19
+ post_setup:
20
+ - connection: identity-provider
21
+ reason: >-
22
+ Keycloak generates the OIDC client secret at runtime, so it cannot be
23
+ file-seeded into the gateway's identity-provider config. Create (or open)
24
+ the ignition-gateway client in Keycloak, copy its secret, and paste it
25
+ into the gateway's IdP configuration.
@@ -0,0 +1,31 @@
1
+ {
2
+ "realm": "ignition",
3
+ "enabled": true,
4
+ "displayName": "Ignition",
5
+ "clients": [
6
+ {
7
+ "clientId": "ignition-gateway",
8
+ "name": "Ignition Gateway",
9
+ "description": "OIDC client for Ignition gateway single sign-on. Copy this client's secret into the gateway IdP config (see POST-SETUP.md).",
10
+ "enabled": true,
11
+ "protocol": "openid-connect",
12
+ "publicClient": false,
13
+ "standardFlowEnabled": true,
14
+ "directAccessGrantsEnabled": true,
15
+ "redirectUris": [
16
+ "*"
17
+ ],
18
+ "webOrigins": [
19
+ "*"
20
+ ]
21
+ }
22
+ ],
23
+ "roles": {
24
+ "realm": [
25
+ {
26
+ "name": "ignition-admin",
27
+ "description": "Maps to the Administrator role in the Ignition gateway."
28
+ }
29
+ ]
30
+ }
31
+ }
@@ -0,0 +1,26 @@
1
+ {{ name }}:
2
+ image: ${MARIADB_IMAGE}
3
+ hostname: {{ name }}
4
+ container_name: {{ container_name_ref }}
5
+ environment:
6
+ TZ: ${TZ}
7
+ MARIADB_ROOT_PASSWORD: ${DB_PASSWORD}
8
+ MARIADB_DATABASE: ${DB_USER}
9
+ MARIADB_USER: ${DB_USER}
10
+ MARIADB_PASSWORD: ${DB_PASSWORD}
11
+ {%- if extra_databases %}
12
+ EXTRA_DATABASES: ${EXTRA_DATABASES}
13
+ volumes:
14
+ - ./services/{{ name }}/initdb:/docker-entrypoint-initdb.d:ro
15
+ {%- endif %}
16
+ healthcheck:
17
+ test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
18
+ interval: 5s
19
+ timeout: 10s
20
+ retries: 10
21
+ {%- if networks %}
22
+ networks:
23
+ {%- for net in networks %}
24
+ - {{ net }}
25
+ {%- endfor %}
26
+ {%- endif %}
@@ -0,0 +1,15 @@
1
+ # MariaDB - SQL database, MySQL-compatible. Ignition ships a MariaDB driver
2
+ # config built in, so no external JDBC driver is attached.
3
+ name: mariadb
4
+ kind: database
5
+ summary: MariaDB database (MySQL-compatible), driver built into Ignition.
6
+ image: mariadb:11
7
+ image_env: MARIADB_IMAGE
8
+ network: backend
9
+ provides:
10
+ - sql-database
11
+ - mysql-compatible
12
+ requires: []
13
+ env: {}
14
+ seeds_gateway_resources: false
15
+ post_setup: []
@@ -0,0 +1,19 @@
1
+ #!/bin/bash
2
+ # MariaDB runs every *.sh in /docker-entrypoint-initdb.d once, on first init.
3
+ # Creates each database named in EXTRA_DATABASES (comma-separated) and grants
4
+ # the application user access. The resolver sets EXTRA_DATABASES to "keycloak"
5
+ # when Keycloak is selected against this database.
6
+ set -e
7
+
8
+ if [ -z "${EXTRA_DATABASES:-}" ]; then
9
+ exit 0
10
+ fi
11
+
12
+ for db in $(echo "${EXTRA_DATABASES}" | tr ',' ' '); do
13
+ echo "Ensuring database '${db}' exists..."
14
+ mariadb -u root -p"${MARIADB_ROOT_PASSWORD}" <<-SQL
15
+ CREATE DATABASE IF NOT EXISTS \`${db}\`;
16
+ GRANT ALL PRIVILEGES ON \`${db}\`.* TO '${MARIADB_USER}'@'%';
17
+ FLUSH PRIVILEGES;
18
+ SQL
19
+ done
@@ -0,0 +1,12 @@
1
+ {{ name }}:
2
+ image: {{ image_ref }}
3
+ hostname: {{ name }}
4
+ container_name: {{ container_name_ref }}
5
+ ports:
6
+ - "${MODBUS_SIM_PORT}:5020"
7
+ {%- if networks %}
8
+ networks:
9
+ {%- for net in networks %}
10
+ - {{ net }}
11
+ {%- endfor %}
12
+ {%- endif %}
@@ -0,0 +1,19 @@
1
+ # Modbus TCP simulation server (oitc/modbus-server). Serves simulated
2
+ # holding/input registers over Modbus TCP for driver demos.
3
+ name: modbus-sim
4
+ kind: simulator
5
+ summary: Modbus TCP simulation server for the gateway Modbus driver.
6
+ image: oitc/modbus-server:2.2.0
7
+ image_env: MODBUS_SIM_IMAGE
8
+ network: backend
9
+ provides:
10
+ - modbus-server
11
+ requires: []
12
+ env:
13
+ MODBUS_SIM_PORT: "5020"
14
+ seeds_gateway_resources: false
15
+ post_setup:
16
+ - connection: device-connection
17
+ reason: >-
18
+ The gateway Modbus device connection (unit ID, register mappings) is set
19
+ up in the gateway UI once the simulator is reachable.
@@ -0,0 +1,10 @@
1
+ # Modbus TCP Simulation Server
2
+
3
+ Serves simulated Modbus registers over TCP on `5020` (host port
4
+ `${MODBUS_SIM_PORT}`).
5
+
6
+ Endpoint from another container: `modbus-sim:5020`. From the host:
7
+ `localhost:${MODBUS_SIM_PORT}`.
8
+
9
+ In the Ignition gateway, add a Modbus TCP device pointing at `modbus-sim:5020`
10
+ (unit ID 1) and map the holding/input registers you want to poll.
@@ -0,0 +1,21 @@
1
+ {{ name }}:
2
+ image: ${MONGO_IMAGE}
3
+ hostname: {{ name }}
4
+ container_name: {{ container_name_ref }}
5
+ environment:
6
+ TZ: ${TZ}
7
+ MONGO_INITDB_ROOT_USERNAME: ${DB_USER}
8
+ MONGO_INITDB_ROOT_PASSWORD: ${DB_PASSWORD}
9
+ volumes:
10
+ - ./services/{{ name }}/initdb:/docker-entrypoint-initdb.d:ro
11
+ healthcheck:
12
+ test: ["CMD", "mongosh", "--quiet", "--eval", "db.adminCommand('ping')"]
13
+ interval: 5s
14
+ timeout: 10s
15
+ retries: 10
16
+ {%- if networks %}
17
+ networks:
18
+ {%- for net in networks %}
19
+ - {{ net }}
20
+ {%- endfor %}
21
+ {%- endif %}
@@ -0,0 +1,14 @@
1
+ # MongoDB - document store. Used by automation/streaming demos that want a
2
+ # NoSQL backend. Not a SQL database, so it does not satisfy Keycloak.
3
+ name: mongo
4
+ kind: database
5
+ summary: MongoDB document store for NoSQL demos.
6
+ image: mongo:7
7
+ image_env: MONGO_IMAGE
8
+ network: backend
9
+ provides:
10
+ - document-store
11
+ requires: []
12
+ env: {}
13
+ seeds_gateway_resources: false
14
+ post_setup: []
@@ -0,0 +1,10 @@
1
+ // MongoDB runs every *.js in /docker-entrypoint-initdb.d once, on first init.
2
+ // Seeds a small "ignition" database with one demo collection so the store is
3
+ // non-empty when you first connect (replace with your own schema).
4
+ db = db.getSiblingDB("ignition");
5
+ db.createCollection("readings");
6
+ db.readings.insertOne({
7
+ source: "ignition-stack-seed",
8
+ note: "Demo document. Safe to delete.",
9
+ createdAt: new Date(),
10
+ });
@@ -0,0 +1,26 @@
1
+ {{ name }}:
2
+ image: ${MYSQL_IMAGE}
3
+ hostname: {{ name }}
4
+ container_name: {{ container_name_ref }}
5
+ environment:
6
+ TZ: ${TZ}
7
+ MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
8
+ MYSQL_DATABASE: ${DB_USER}
9
+ MYSQL_USER: ${DB_USER}
10
+ MYSQL_PASSWORD: ${DB_PASSWORD}
11
+ {%- if extra_databases %}
12
+ EXTRA_DATABASES: ${EXTRA_DATABASES}
13
+ volumes:
14
+ - ./services/{{ name }}/initdb:/docker-entrypoint-initdb.d:ro
15
+ {%- endif %}
16
+ healthcheck:
17
+ test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
18
+ interval: 5s
19
+ timeout: 10s
20
+ retries: 10
21
+ {%- if networks %}
22
+ networks:
23
+ {%- for net in networks %}
24
+ - {{ net }}
25
+ {%- endfor %}
26
+ {%- endif %}
@@ -0,0 +1,15 @@
1
+ # MySQL - SQL database. Selecting MySQL makes the resolver attach the
2
+ # mysql-jdbc driver to every gateway (Ignition ships no MySQL driver built-in).
3
+ name: mysql
4
+ kind: database
5
+ summary: MySQL database; pulls in the MySQL JDBC driver for the gateway.
6
+ image: mysql:9
7
+ image_env: MYSQL_IMAGE
8
+ network: backend
9
+ provides:
10
+ - sql-database
11
+ - mysql-compatible
12
+ requires: []
13
+ env: {}
14
+ seeds_gateway_resources: false
15
+ post_setup: []
@@ -0,0 +1,19 @@
1
+ #!/bin/bash
2
+ # MySQL runs every *.sh in /docker-entrypoint-initdb.d once, on first init.
3
+ # Creates each database named in EXTRA_DATABASES (comma-separated) and grants
4
+ # the application user access. The resolver sets EXTRA_DATABASES to "keycloak"
5
+ # when Keycloak is selected against this database.
6
+ set -e
7
+
8
+ if [ -z "${EXTRA_DATABASES:-}" ]; then
9
+ exit 0
10
+ fi
11
+
12
+ for db in $(echo "${EXTRA_DATABASES}" | tr ',' ' '); do
13
+ echo "Ensuring database '${db}' exists..."
14
+ mysql -u root -p"${MYSQL_ROOT_PASSWORD}" <<-SQL
15
+ CREATE DATABASE IF NOT EXISTS \`${db}\`;
16
+ GRANT ALL PRIVILEGES ON \`${db}\`.* TO '${MYSQL_USER}'@'%';
17
+ FLUSH PRIVILEGES;
18
+ SQL
19
+ done
@@ -0,0 +1,16 @@
1
+ {{ name }}:
2
+ image: {{ image_ref }}
3
+ hostname: {{ name }}
4
+ container_name: {{ container_name_ref }}
5
+ environment:
6
+ TZ: ${TZ}
7
+ N8N_SECURE_COOKIE: "false"
8
+ N8N_RUNNERS_ENABLED: "true"
9
+ ports:
10
+ - "${N8N_PORT}:5678"
11
+ {%- if networks %}
12
+ networks:
13
+ {%- for net in networks %}
14
+ - {{ net }}
15
+ {%- endfor %}
16
+ {%- endif %}
@@ -0,0 +1,16 @@
1
+ # n8n - workflow automation. The user-facing service in the catalog, so it
2
+ # lands on the frontend network when the network split is on. Pairs with the
3
+ # Ignition MCP module in the mcp-n8n profile (Phase 6).
4
+ name: n8n
5
+ kind: automation
6
+ summary: n8n workflow automation engine.
7
+ image: n8nio/n8n:2.23.0
8
+ image_env: N8N_IMAGE
9
+ network: frontend
10
+ provides:
11
+ - automation
12
+ requires: []
13
+ env:
14
+ N8N_PORT: "5678"
15
+ seeds_gateway_resources: false
16
+ post_setup: []
@@ -0,0 +1,11 @@
1
+ # n8n
2
+
3
+ Workflow automation engine with a web editor on `5678`
4
+ (host port `${N8N_PORT}`): http://localhost:${N8N_PORT}.
5
+
6
+ On first visit, n8n walks you through creating the owner account. From a
7
+ workflow you can call the Ignition gateway's HTTP/WebDev endpoints at
8
+ `http://gateway:8088` to read tags or trigger logic.
9
+
10
+ In the mcp-n8n profile (Phase 6) this pairs with the Ignition MCP module so an
11
+ assistant can drive the gateway through n8n.
@@ -0,0 +1,13 @@
1
+ {{ name }}:
2
+ image: {{ image_ref }}
3
+ hostname: {{ name }}
4
+ container_name: {{ container_name_ref }}
5
+ command: ["--pn=50000", "--autoaccept", "--unsecuretransport"]
6
+ ports:
7
+ - "${OPCUA_SIM_PORT}:50000"
8
+ {%- if networks %}
9
+ networks:
10
+ {%- for net in networks %}
11
+ - {{ net }}
12
+ {%- endfor %}
13
+ {%- endif %}
@@ -0,0 +1,21 @@
1
+ # OPC-UA simulation server - Microsoft OPC-PLC, the mainstream containerized
2
+ # OPC-UA test server (Prosys ships no Docker image). Anonymous, unsecured
3
+ # endpoint suitable for demos.
4
+ name: opcua-sim
5
+ kind: simulator
6
+ summary: Microsoft OPC-PLC OPC-UA simulation server.
7
+ image: mcr.microsoft.com/iotedge/opc-plc:2.9.9
8
+ image_env: OPCUA_SIM_IMAGE
9
+ network: backend
10
+ provides:
11
+ - opcua-server
12
+ requires: []
13
+ env:
14
+ OPCUA_SIM_PORT: "50000"
15
+ seeds_gateway_resources: false
16
+ post_setup:
17
+ - connection: opc-ua-connection
18
+ reason: >-
19
+ The gateway's outbound OPC-UA connection config is file-seedable, but is
20
+ left to post-setup here so the endpoint URL and security mode can be
21
+ confirmed against the running simulator.
@@ -0,0 +1,11 @@
1
+ # OPC-UA Simulation Server (Microsoft OPC-PLC)
2
+
3
+ An anonymous, unsecured OPC-UA server with simulated nodes, listening on `50000`
4
+ (host port `${OPCUA_SIM_PORT}`).
5
+
6
+ Endpoint from another container: `opc.tcp://opcua-sim:50000`. From the host:
7
+ `opc.tcp://localhost:${OPCUA_SIM_PORT}`.
8
+
9
+ In the Ignition gateway, add an OPC-UA connection to that endpoint with security
10
+ policy `None` and anonymous authentication. The server auto-accepts client
11
+ certificates (`--autoaccept`) so no certificate exchange is needed.
@@ -0,0 +1,26 @@
1
+ {{ name }}:
2
+ image: ${POSTGRES_IMAGE}
3
+ hostname: {{ name }}
4
+ container_name: {{ container_name_ref }}
5
+ environment:
6
+ TZ: ${TZ}
7
+ POSTGRES_USER: ${DB_USER}
8
+ POSTGRES_PASSWORD: ${DB_PASSWORD}
9
+ {%- if extra_databases %}
10
+ EXTRA_DATABASES: ${EXTRA_DATABASES}
11
+ {%- endif %}
12
+ {%- if extra_databases %}
13
+ volumes:
14
+ - ./services/{{ name }}/initdb:/docker-entrypoint-initdb.d:ro
15
+ {%- endif %}
16
+ healthcheck:
17
+ test: ["CMD", "pg_isready", "-h", "localhost", "-U", "${DB_USER}"]
18
+ interval: 5s
19
+ timeout: 10s
20
+ retries: 10
21
+ {%- if networks %}
22
+ networks:
23
+ {%- for net in networks %}
24
+ - {{ net }}
25
+ {%- endfor %}
26
+ {%- endif %}
@@ -0,0 +1,21 @@
1
+ # PostgreSQL - the default Ignition SQL store / historian backend.
2
+ # Provides the sql-database capability Keycloak depends on.
3
+ name: postgres
4
+ kind: database
5
+ summary: PostgreSQL database, the default Ignition historian and SQL store.
6
+ image: postgres:18.1
7
+ image_env: POSTGRES_IMAGE
8
+ network: backend
9
+ provides:
10
+ - sql-database
11
+ - postgres-compatible
12
+ requires: []
13
+ # DB credentials (DB_USER / DB_PASSWORD / DB_HOST) are emitted by the writer's
14
+ # shared database .env section, not here, so every database kind shares one
15
+ # credential vocabulary.
16
+ env: {}
17
+ # Ships a pre-seeded gateway database-connection (config.json + resource.json)
18
+ # plus the internal-secret-provider that holds its password, per the Phase-1
19
+ # matrix db-connection row (file-seedable-config: yes, file-seedable-secret: yes).
20
+ seeds_gateway_resources: true
21
+ post_setup: []
@@ -0,0 +1,32 @@
1
+ {
2
+ "connectURL": "jdbc:postgresql://db:5432/ignition",
3
+ "connectionProps": "",
4
+ "connectionResetParams": "",
5
+ "defaultTransactionLevel": "DEFAULT",
6
+ "driver": "PostgreSQL",
7
+ "evictionRate": -1,
8
+ "evictionTests": 3,
9
+ "evictionTime": 1800000,
10
+ "failoverMode": "STANDARD",
11
+ "includeSchemaInTableName": false,
12
+ "password": {
13
+ "data": {
14
+ "providerName": "internal-secret-provider",
15
+ "secretName": "db-password"
16
+ },
17
+ "type": "Referenced"
18
+ },
19
+ "poolInitSize": 0,
20
+ "poolMaxActive": 8,
21
+ "poolMaxIdle": 8,
22
+ "poolMaxWait": 5000,
23
+ "poolMinIdle": 0,
24
+ "slowQueryLogThreshold": 60000,
25
+ "testOnBorrow": true,
26
+ "testOnReturn": false,
27
+ "testWhileIdle": false,
28
+ "translator": "POSTGRES",
29
+ "username": "ignition",
30
+ "validationQuery": "SELECT 1",
31
+ "validationSleepTime": 10000
32
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "scope": "A",
3
+ "description": "Pre-seeded connection to the bundled Postgres service.",
4
+ "version": 1,
5
+ "restricted": false,
6
+ "overridable": true,
7
+ "files": [
8
+ "config.json"
9
+ ],
10
+ "attributes": {
11
+ "lastModification": {
12
+ "actor": "admin",
13
+ "timestamp": "2025-10-08T20:07:51Z"
14
+ },
15
+ "uuid": "9727b3a7-bff4-4249-93f5-25f9010886a3",
16
+ "lastModificationSignature": "e09b7a5df3f22e15d47311335de7420e2024239ecf71af85946b2798dba069b3",
17
+ "enabled": true
18
+ }
19
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "profile": {
3
+ "type": "internal"
4
+ },
5
+ "settings": {
6
+ "secrets": {
7
+ "db-password": {
8
+ "ciphertext": {
9
+ "ciphertext": "QZshnhoavcjVfg",
10
+ "encrypted_key": "MmT2XEQuLfmKj0dHURyIT0BuTJJMxgE-7W6kZTf5vL4jo9BnWkwxiQ",
11
+ "iv": "7wMQIa5Vdy1QZ0cj",
12
+ "protected": "eyJhbGciOiJBMjU2S1ciLCJlbmMiOiJBMjU2R0NNIiwiaWF0IjoxNzU0NDI5NTI2LCJ6aXAiOiJERUYifQ",
13
+ "tag": "ckxlkktV8u9b_43pjuXCBA"
14
+ },
15
+ "description": "Password for the bundled Postgres connection."
16
+ }
17
+ }
18
+ }
19
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "scope": "A",
3
+ "description": "",
4
+ "version": 1,
5
+ "restricted": false,
6
+ "overridable": true,
7
+ "files": [
8
+ "config.json"
9
+ ],
10
+ "attributes": {
11
+ "lastModification": {
12
+ "actor": "admin",
13
+ "timestamp": "2025-08-05T21:32:06Z"
14
+ },
15
+ "uuid": "cadfe4ea-4f3a-448a-a4b5-c8836a701c41",
16
+ "lastModificationSignature": "74930ea8c842fffac05dc1ae3446dbb60db71c74e88f5bbb45da275b880486e6",
17
+ "enabled": true
18
+ }
19
+ }
@@ -0,0 +1,17 @@
1
+ #!/bin/bash
2
+ # Postgres runs every *.sh in /docker-entrypoint-initdb.d once, on first init.
3
+ # Creates each database named in POSTGRES_EXTRA_DATABASES (comma-separated)
4
+ # beyond the default one Postgres creates for POSTGRES_USER. The resolver sets
5
+ # this to "keycloak" when Keycloak is selected against this database.
6
+ set -e
7
+
8
+ if [ -z "${EXTRA_DATABASES:-}" ]; then
9
+ exit 0
10
+ fi
11
+
12
+ for db in $(echo "${EXTRA_DATABASES}" | tr ',' ' '); do
13
+ echo "Ensuring database '${db}' exists..."
14
+ if ! psql -tAc "SELECT 1 FROM pg_database WHERE datname = '${db}'" --username "${POSTGRES_USER}" | grep -q 1; then
15
+ psql --username "${POSTGRES_USER}" -c "CREATE DATABASE \"${db}\""
16
+ fi
17
+ done
@@ -0,0 +1,19 @@
1
+ {{ name }}:
2
+ image: {{ image_ref }}
3
+ hostname: {{ name }}
4
+ container_name: {{ container_name_ref }}
5
+ environment:
6
+ TZ: ${TZ}
7
+ RABBITMQ_DEFAULT_USER: ${RABBITMQ_USER}
8
+ RABBITMQ_DEFAULT_PASS: ${RABBITMQ_PASSWORD}
9
+ ports:
10
+ - "${RABBITMQ_MQTT_PORT}:1883"
11
+ - "${RABBITMQ_MGMT_PORT}:15672"
12
+ volumes:
13
+ - ./services/{{ name }}/enabled_plugins:/etc/rabbitmq/enabled_plugins:ro
14
+ {%- if networks %}
15
+ networks:
16
+ {%- for net in networks %}
17
+ - {{ net }}
18
+ {%- endfor %}
19
+ {%- endif %}