Repository: [helm-charts]
Description: [This is a repository for storing Helm charts to support deployment of an IA Node.]
SPDX-License-Identifier: Apache-2.0 AND OGL-UK-3.0
The following repository aims to collate a number of Helm charts to support and ease a first time deployment of the IA Node (Integration Architecture Node). The IA Node is an open-source digital component developed as part of the National Digital Twin Programme (NDTP), to support managing and sharing information across organisations.
This is aimed at being an easy set up for local/dev and should NOT be used as is in production.
Important
Secrets management is outside of the scope of the deployment, however we have provided a few possible examples on how you might override the default values, or provide your own where supported.
git clone https://github.com/National-Node-Net/helm-charts.git cd helm-charts
The following technologies will need to be installed and configured prior to getting started.
Note: Versions highlighted are based on what configurations have been used throughout the testing of the Helm charts. The commands are all in sh/bash so use WSL2 if in windows.
-
Supported Kubernetes Versions:
Kubernetes 1.23+: a Kubernetes cluster i.e. AKS or local development cluster
-
Required Tooling:
kubectl 1.28.9+: prior knowledge, usage and experience withkubectlHelm 3.8.0+: prior knowledge, usage and experience in Helmjq 1.6+: for querying and formating json
-
Optional Tooling:
K9s 0.32.5+: for Kubernetes cluster overview and visualisation of deployments
-
Application Installation Requirements:
Istio Helm chart, Gateway, Base and Istiod 1.25.0+: service mesh that layers onto existing application, providing uniform and more efficient ways to secure, connect, and monitor servicesOpenID Connect (OIDC) Identity Provider:the application requires that authentication is performed by the service mesh using an OIDC authentication flow and that all paths exposed on the domain should be authenticated, this install was tested withKeycloakusingBitnami Keycloak Helm chart 24.4.13which, also installsPostgreSQL.OAuth2Proxy: a reverse proxy that should be deployed and integrated with Istio service mesh to provide authentication using a target OpenID Connect (OIDC) Identity Provider, this install usedBitnami OAuth2 Proxy Helm chart 6.2.10, which also installsRedisa session storage option that can be used with OAuth2ProxyMongoDB: for application data storage, this install was tested with theMongoDB Community Operator Helm chart 0.12.0+Apache Kafka: for application data streaming, this install was tested with theKafka Strimzi Operator 0.45.0+
-
System Requirements:
The Helm charts included in this repository were tested against a Kubernetes Cluster with the following specification:
- Nodes: 3
- CPU: 8 vCores
- Memory: 32 GB
- Storage: 64 GB
but can be also ran on smaller setups.
Follow these steps to get started with the charts in this repository.
Note
In all steps replace references to localhost with your desired installation domain.
Tip
There are some example install/uninstall script under the scripts folder that you can also use to help get started. in some cases we've added --set commands to reduce cpu and memory request for smaller environments
Follow any official documentation to deploy a desired install i.e. Azure AKS, Amazon EKS, or to run a quick install or testing setup, configure a local development cluster i.e. minikube, k3s, microk8s.
Helm is a tool that can be used to package a set of pre-configured Kubernetes resources. To install, refer to the Helm install guide and quick start.
Any Istio examples throughout this documentation are provided largely as information, to help support integrators to plan their own deployment.
Important
The installation assumes that Istio has already been installed Istio, following the Istio Helm Install guide, and assumes a default principal of cluster.local/ns/istio-system/sa/ingressgateway, and default gateway of istio-system/istio-gateway. You will require a gateway and a domain, listening on HTTPS port (443), presenting a valid TLS certificate. Istio should be integrated with an OIDC conformant Identity Provider (IdP) e.g. Keycloak, Cognito etc. It is required that the IdP be configured with users, clients and groups. The application requires the authentication to be performed by the service mesh using an OIDC authentication flow, which can be done by configuring the Istio options: global mesh config, or an Envoy Filter. All paths exposed on the domain should be authenticated. That said, for local environments a gateway is optional.
Note
Istio authorization policies are implemented to restrict communications between components. These principals are based on the namespace a service is deployed to and the service account it runs as. In particular, the principal that the Istio ingress is assigned, is environment specific and may differ from the one specified in the default deployment. These can all be overridden using the Helm values.
Tip
for local clusters Istio requests a lot of resources you can do the below to reduce them
kubectl -n istio-system set resources deployment/istiod --requests=cpu=100m,memory=128MiMost of the deployment assumes you have a namespace configured as follows:
kubectl create namespace org-b
kubectl label namespace org-b istio-injection=enabled
kubectl create namespace org-a
kubectl label namespace org-a istio-injection=enabledNote
This section assumes Istio is installed, and configured with a gateway and mesh config or envoy filter to handle the redirection of OAuth2 Proxy.
The following creates a namespace called Keycloak, labels it to ensure Istio envoys are deployed with Keycloak, and deploys Keycloak via its Helm chart.
Note the below --sets for resources are optional and configured for small environments
kubectl create namespace keycloak
kubectl label namespace keycloak istio-injection=enabled
helm install keycloak oci://registry-1.docker.io/bitnamicharts/keycloak -n keycloak \
--set image.repository=bitnamilegacy/keycloak \
--set postgresql.image.repository=bitnamilegacy/postgresql \
--set global.security.allowInsecureImages=true \
--set resources.requests.cpu=50m \
--set resources.requests.memory=256Mi \
--set postgresql.primary.resources.requests.cpu=50m \
--set postgresql.primary.resources.requests.memory=128MiYou will need to create a virtual service and configure a realm.
Note
Note: The example realms realm-ianode.json & realm-management-node.json, can be imported as a reference/starting point. This example includes example clients and a group client scope that maps both group membership and realm roles mappers, however only one of these options are required. (NS. to tidy up and fix)
Tip
some notes to help install the realms
decode the new keycloak secret keycloak its in the name space keycloak
kubectl port-forward -n keycloak svc/keycloak 8080:80
login user: user pass:from secret above
-->Manage realms --> Create realm -> browse : import both realm-management-node.json and realm-ianode.json, realm files
for each select new realm -> clients .choose ia-node or management node from list -> credentials -> client secret -> regenerate and then copy (and note)
make a secret for each for future ref
kubectl -n keycloak create secret generic keycloak-ianode-secret --from-literal=client-secret=*********(from keycloak, that you got above)
kubectl -n keycloak create secret generic keycloak-management-node-client --from-literal=client-secret=*********
Note
This section assumes Istio is installed, and configured with a gateway and mesh config or envoy filter to handle the redirection of OAuth2 Proxy. In addition it assumes, Keycloak has been deployed on the cluster ie. http://keycloak.keycloak.svc.cluster.local with a realm, test users, client and some groups configured. The Keycloak values are configurable if you are using an external install.
Deploy the ia-node-oidc helper chart to help with setting up an OIDC conformant Identity Provider (IdP) to work with the IA Node setup.
helm install ia-node-oidc oci://ghcr.io/national-node-net/helm/ia-node-oidc -n org-a --set oidcProvider.configMap.redirect_url="http://localhost/oauth2/callback" --set istio.authorizationPolicy.enabled=falseNote
if this fails with errors, like
Error: INSTALLATION FAILED: AuthorizationPolicy.security.istio.io "oauth2-proxy-to-apps" is invalid: [spe...then uninstall helm -n org-a uninstall ia-node-oidc
and try again.
A config map and optional secret output is generated by the package, that can then be used to override the OAuth2 Proxy installation.
Deploy OAuth2Proxy (with Redis) with Helm: This is used to ensure all endpoints are authenticated using keycloak, can be skipped for local/dev deployments, but you will need a redis
helm install oauth2-proxy oci://registry-1.docker.io/bitnamicharts/oauth2-proxy -n org-a --set configuration.existingSecret="oauth2-proxy-default" --set configuration.existingConfigmap="oauth2-proxy-default" --set istio.virtualService.hosts[0]="*" --set image.repository=bitnamilegacy/oauth2-proxy --set redis.image.repository=bitnamilegacy/redis --set global.security.allowInsecureImages=trueNote
This section assumes Istio is installed, and configured with a gateway and mesh config or envoy filter to handle the redirection of OAuth2 Proxy.
MongoDB is used by the access-api to store user security attributes for Attribute-Based Access Control (ABAC) in the graph-server. Multiple graph-server instances can share the same access-api and MongoDB instance.
Deploy the MongoDB community operator with Helm:
Note: The below --set commands are optional and configured for small environments
helm repo add mongodb https://mongodb.github.io/helm-charts
helm install community-operator mongodb/community-operator --namespace mongodb-operator --create-namespace \
--set operator.watchNamespace="*" \
--set operator.resources.requests.cpu=50m \
--set operator.resources.requests.memory=128Mi \
--set operator.resources.limits.cpu=200m \
--set operator.resources.limits.memory=256MiDeploy the ia-node-mongodb helper chart to deploy a MongoDB instance:
Note: The below --set commands are optional and configured for small environments
helm install ia-node-mongodb oci://ghcr.io/national-node-net/helm/ia-node-mongodb -n org-a \
--set mongodb.spec.members=1 For small enviroments you can change the requests like so
kubectl patch mongodbcommunity mongodb -n org-a --type=merge -p '
spec:
statefulSet:
spec:
template:
spec:
containers:
- name: mongod
resources:
requests:
cpu: 50m
memory: 128Mi
limits:
cpu: 50m
memory: 256Mi
- name: mongodb-agent
resources:
requests:
cpu: 50m
memory: 128Mi
limits:
cpu: 50m
memory: 256Mi
'Note
This section assumes Istio is installed, and configured, with a gateway and mesh config or envoy filter to handle the redirection of OAuth2 Proxy.
Deploy the Kafka operator with Helm:
Note: The below --set commands are optional and configured for small environments. Strimzi 0.48.0+ is required for Kafka 4.x with KRaft mode.
helm install my-strimzi-cluster-operator oci://quay.io/strimzi-helm/strimzi-kafka-operator --namespace kafka-operator --create-namespace \
--set watchAnyNamespace="true" \
--set resources.requests.cpu=50m \
--set resources.requests.memory=128Mi \
--set resources.limits.cpu=500m \
--set resources.limits.memory=256MiDeploy the ia-node-kafka helper chart for use specifically with the integration architecture node (IA Node secure graph component).
Note: The chart defaults to Kafka 4.1.0 with KRaft mode (no ZooKeeper) and single broker/controller for small environments. Use the local chart path if you have made local modifications:
# For published chart:
helm install ia-node-kafka oci://ghcr.io/national-node-net/helm/ia-node-kafka -n org-a # For local chart with Kafka 4.1.0 support:
helm install ia-node-kafka ./charts/ia-node-kafka -n org-aNote
If using Helm 4.0+, you may encounter an error: metadata.managedFields must be nil. This is due to Helm 4.0's default server-side apply mode. Use the --server-side=false flag to resolve:
helm install ia-node-kafka ./charts/ia-node-kafka -n org-a --server-side=falseAfter Kafka is deployed, you will have:
- Secret
kafka-ia-node-user(Strimzi KafkaUser secret) containingsasl.jaas.config(used by federator client/server examples) - Secret
kafka-auth-configcontainingkafka-config.propertieswithsecurity.protocol=SASL_PLAINTEXTfor local/dev (used by test-message-pod and other tooling)
Note
The ia-node-kafka chart defaults to SASL_PLAINTEXT on port :9092 for local/dev simplicity. For production with TLS, override:
helm install ia-node-kafka ./charts/ia-node-kafka -n org-a \
--set kafkaCluster.secret.securityProtocol=SASL_SSLThen tools connecting to :9093 will need truststore configuration.
For federation, prefer using kafka-ia-node-user directly (no need to delete/recreate secrets).
# Configure all Kafka ACLs in a single patch operation
kubectl patch kafkauser kafka-ia-node-user -n org-a --type='json' -p='[
{
"op": "replace",
"path": "/spec/authorization/acls/0/operations",
"value": ["Create", "Describe", "DescribeConfigs", "Read", "Write"]
},
{
"op": "replace",
"path": "/spec/authorization/acls/1/operations",
"value": ["Create", "Describe", "DescribeConfigs", "Read", "Write"]
},
{
"op": "add",
"path": "/spec/authorization/acls/-",
"value": {
"operations": ["Read"],
"resource": {
"type": "group",
"name": "JenaFusekiKafka-",
"patternType": "prefix"
}
}
},
{
"op": "add",
"path": "/spec/authorization/acls/-",
"value": {
"operations": ["Read"],
"resource": {
"type": "group",
"name": "federator-server",
"patternType": "literal"
}
}
},
{
"op": "add",
"path": "/spec/authorization/acls/-",
"value": {
"operations": ["Read"],
"resource": {
"type": "group",
"name": "management-node",
"patternType": "literal"
}
}
},
{
"op": "add",
"path": "/spec/authorization/acls/-",
"value": {
"operations": ["Create", "Describe", "Write", "Read"],
"resource": {
"type": "topic",
"name": "DEMOPRODUCERIANODE-",
"patternType": "prefix"
}
}
},
{
"op": "add",
"path": "/spec/authorization/acls/-",
"value": {
"operations": ["Describe", "DescribeConfigs"],
"resource": {
"type": "cluster",
"patternType": "literal"
}
}
},
{
"op": "add",
"path": "/spec/authorization/acls/-",
"value": {
"operations": ["Describe", "DescribeConfigs"],
"resource": {
"type": "topic",
"name": "DEMOPRODUCERIANODE-",
"patternType": "prefix"
}
}
}
]'Verify the ACLs were added:
kubectl get kafkauser kafka-ia-node-user -n org-a -o jsonpath='{.spec.authorization.acls}' | jq '.'Note
The ACLs configured above provide:
- Consumer group access: Allows graph-server (JenaFusekiKafka-*), federator-server, and management-node polling to read from Kafka
- Federated topic access: Allows federator-client to create and write to DEMOPRODUCERIANODE-* topics, and graph-server to read from them
- Cluster-level operations: Allows DESCRIBE and DESCRIBE_CONFIGS operations on the cluster for metadata operations (required for AdminClient, prevents authentication failures and broker crashes)
- Topic metadata access: Allows DESCRIBE and DESCRIBE_CONFIGS on DEMOPRODUCERIANODE-* topics so graph-server can access topic metadata
- Source topic metadata: Allows DESCRIBE_CONFIGS on knowledge and ontology topics to prevent broker crashes when AdminClient operations are performed
- Federator-server: Allows federator-server to read from kafka
Critical: The DescribeConfigs operation must be granted on ALL topics (source and federated) to prevent Kafka broker crashes due to repeated authorization denials. Without this permission, the broker will fail readiness probes and restart continuously.
Note
This section assumes Istio is installed, and configured with a gateway and mesh config or envoy filter to handle the redirection of OAuth2 Proxy and that this is now integrated with an identity provider. In addition it assumes, MongoDB and Kafka have been deployed on the cluster i.e. mongodb-svc:27017 and kafka-cluster-kafka-bootstrap:9092 respectively, are using secret names of ia-node-user-password and kafka-auth-config respectively. These can all be overridden in the values as required, along with any other requirements if you are hosting these services externally.
The ia-node chart includes multiple components. By default, only the access-api is enabled. For a full IA Node deployment including the secure graph-server, enable it with --set apps.graph.enabled=true and configure Kafka to use port 9092 (SASL_PLAINTEXT):
Note: The below --set commands for resources are optional and configured for small environments. Use the local chart if deploying with federator support (fusekiConfig.kafkaTopics):
helm -n org-a install ia-node ./charts/ia-node \
--set apps.api.configMap.data.DEPLOYED_DOMAIN="http://localhost" \
--set apps.api.configMap.data.OPENID_PROVIDER_URL="http://keycloak.keycloak.svc.cluster.local/realms/ianode/" \
--set apps.graph.configMap.data.JWKS_URL="http://keycloak.keycloak.svc.cluster.local/realms/ianode/protocol/openid-connect/certs" \
--set apps.graph.configMap.data.USER_ATTRIBUTES_URL="http://access-api.org-a.svc.cluster.local:8080/users/lookup/{user}" \
--set apps.graph.configMap.data.ATTRIBUTE_HIERARCHY_URL="http://access-api.org-a.svc.cluster.local:8080/hierarchies/lookup/{name}" \
--set apps.graph.enabled=true \
--set apps.api.deployment.resources.requests.cpu=25m \
--set apps.graph.statefulSet.resources.requests.cpu=25m \
--set apps.graph.statefulSet.resources.requests.memory=128Mi \
--set kafkaCluster.bootstrapServers="kafka-cluster-kafka-bootstrap.org-a.svc.cluster.local:9092" \
--set fusekiConfig.cqrsEnabled=false \
--set fusekiConfig.kafkaTopics.knowledge="DEMOPRODUCERIANODE-knowledge" \
--set fusekiConfig.kafkaTopics.ontology="DEMOPRODUCERIANODE-ontology.car.models" \
--set istio.virtualService.hosts[0]="localhost"Note
The ia-node chart contains:
- access-api (enabled by default): User access control API for managing ABAC permissions
- graph-server (disabled by default): The secure-agent-graph component for RDF triple store with Kafka integration
- access-ui (disabled): Web UI for access management
- query-ui (disabled): Web UI for querying the graph
Enable additional components as needed with --set apps.<component>.enabled=true
Tip
If deploying MongoDB or Kafka in a different namespace, you may need to copy the authentication secrets to the org-a namespace:
# Example: Copy MongoDB password secret
kubectl apply -f - <<EOF
apiVersion: v1
kind: Secret
metadata:
name: ia-node-user-password
namespace: org-b
type: Opaque
data:
password: <base64-encoded-password-from-MongoDB-install>
EOFTo get the base64-encoded password from the MongoDB installation:
kubectl get secret <mongodb-secret-name> -n <mongodb-namespace> -o jsonpath='{.data.password}'Below are notes on setting up the federation components. This assumes you're in a dev environment and forgoes many of the security features; these can easily be enabled in the full multi-tenanted deployment.
used to store config's used by the federators
kubectl create namespace central-org
kubectl label namespace central-org istio-injection=enabled
helm repo add bitnami https://charts.bitnami.com/bitnami
helm upgrade --install management-postgres bitnami/postgresql -n central-org \
--set architecture=standalone \
--set auth.username=mn_user \
--set auth.password=<choose password> \
--set auth.database=management_node \
--set primary.persistence.enabled=trueNote
If the above fails with a -bash: choose: No such error, then you forgot to replace the <choose password> bit with an actuall password !
Note
When SSL is disabled (as in this dev setup), no certificate secret is required. For production deployments with SSL enabled, you would need to create a secret with keystore.jks and truststore.jks files.
Get the Postgress user password from the secret created by above chart and the Keycloak client secret for the managment client
POSTGRESS_PASSWORD=$(kubectl get secret management-postgres-postgresql -n central-org -o jsonpath='{.data.password}' | base64 -d)
CLIENT_SECRET=$(kubectl get secret keycloak-management-node-client -n keycloak -o jsonpath='{.data.client-secret}' | base64 -d)helm upgrade --install management-node ./charts/management-node \
-n central-org \
--set app.datasource.secret.username=mn_user \
--set app.datasource.secret.password=${POSTGRESS_PASSWORD} \
--set app.datasource.secret.create=true \
--set app.datasource.url=jdbc:postgresql://management-postgres-postgresql.central-org.svc.cluster.local:5432/management_node \
--set image.repository=ghcr.io/national-node-net/management-node/management-node \
--set image.tag=1.0.1 \
--set app.ssl.enabled=false \
--set app.oauth2.resourceserver.jwt.issuerUri=http://keycloak.keycloak.svc.cluster.local/realms/management-node \
--set app.oauth2.resourceserver.jwt.jwkSetUri=http://keycloak.keycloak.svc.cluster.local/realms/management-node/protocol/openid-connect/certs \
--set app.oauth2.resourceserver.opaquetoken.introspectionUri=http://keycloak.keycloak.svc.cluster.local/realms/management-node/protocol/openid-connect/token/introspect \
--set app.oauth2.resourceserver.opaquetoken.clientSecret=${CLIENT_SECRET} \
--set app.oauth2.resourceserver.jwt.jwsAlgorithm=RS256First, extract the PostgreSQL password from the secret:
PGPASSWORD=$(kubectl get secret management-postgres-postgresql -n central-org -o jsonpath='{.data.password}' | base64 -d)Insert demo configuration for federator testing:
PGPASSWORD=$(kubectl get secret management-postgres-postgresql -n central-org -o jsonpath='{.data.password}' | base64 -d)
# Insert consumer
kubectl exec -n central-org management-postgres-postgresql-0 -- bash -c "PGPASSWORD=$PGPASSWORD psql -U mn_user -d management_node -c \"INSERT INTO mn.consumer (idp_client_id, name, org_id) VALUES ('management-node', 'IA-NODE-CONSUMER-1', 1);\""
# Insert producer - pointing to federator-server (not management-node)
kubectl exec -n central-org management-postgres-postgresql-0 -- bash -c "PGPASSWORD=$PGPASSWORD psql -U mn_user -d management_node -c \"INSERT INTO mn.producer (idp_client_id, name, description, host, port, active, tls, org_id) VALUES ('management-node', 'DEMO-PRODUCER-IA-NODE', 'Demo producer for IA Node', 'federator-server.org-b.svc.cluster.local', 9001, true, false, 1);\""
# Add product_type column (required for federator-client)
kubectl exec -n central-org management-postgres-postgresql-0 -- bash -c "PGPASSWORD=$PGPASSWORD psql -U mn_user -d management_node -c \"ALTER TABLE mn.product ADD COLUMN IF NOT EXISTS product_type VARCHAR(50);\""
# Insert products with product_type
kubectl exec -n central-org management-postgres-postgresql-0 -- bash -c "PGPASSWORD=$PGPASSWORD psql -U mn_user -d management_node -c \"INSERT INTO mn.product (name, topic, product_type, producer_id) SELECT 'CarModelsKnowledge', 'knowledge', 'KNOWLEDGE', id FROM mn.producer WHERE idp_client_id = 'management-node';\""
kubectl exec -n central-org management-postgres-postgresql-0 -- bash -c "PGPASSWORD=$PGPASSWORD psql -U mn_user -d management_node -c \"INSERT INTO mn.product (name, topic, product_type, producer_id) SELECT 'CarModelsOntology', 'ontology.car.models', 'ONTOLOGY', id FROM mn.producer WHERE idp_client_id = 'management-node';\""
# Link the products to the built-in `topic` product type (id=1)
kubectl exec -n central-org management-postgres-postgresql-0 -- bash -c "PGPASSWORD=$PGPASSWORD psql -U mn_user -d management_node -c \"UPDATE mn.product SET product_type_id = 1 WHERE name IN ('CarModelsKnowledge', 'CarModelsOntology');\""
# Link products to consumer
kubectl exec -n central-org management-postgres-postgresql-0 -- bash -c "PGPASSWORD=$PGPASSWORD psql -U mn_user -d management_node -c \"INSERT INTO mn.product_consumer (product_id, consumer_id, granted_ts, validity) SELECT p.id, c.id, '2025-11-06 00:00:00', 365 FROM mn.product p, mn.consumer c WHERE p.name = 'CarModelsKnowledge' AND c.idp_client_id = 'management-node';\""
kubectl exec -n central-org management-postgres-postgresql-0 -- bash -c "PGPASSWORD=$PGPASSWORD psql -U mn_user -d management_node -c \"INSERT INTO mn.product_consumer (product_id, consumer_id, granted_ts, validity) SELECT p.id, c.id, '2025-11-06 00:00:00', 365 FROM mn.product p, mn.consumer c WHERE p.name = 'CarModelsOntology' AND c.idp_client_id = 'management-node';\""
# Update the polling schedule to run every 4 hours
kubectl exec -n central-org management-postgres-postgresql-0 -- bash -c "PGPASSWORD=$PGPASSWORD psql -U mn_user -d management_node -c \"UPDATE mn.product_consumer SET schedule_type = 'cron', schedule_expression = '0 */4 * * *' WHERE id IN (4, 5);\""
echo "Configuration added successfully"Then check existing consumers and producers:
kubectl exec -n central-org management-postgres-postgresql-0 -- bash -c "PGPASSWORD=$PGPASSWORD psql -U mn_user -d management_node -c \"SELECT id, idp_client_id, name FROM mn.consumer;\""
kubectl exec -n central-org management-postgres-postgresql-0 -- bash -c "PGPASSWORD=$PGPASSWORD psql -U mn_user -d management_node -c \"SELECT id, idp_client_id, name, host FROM mn.producer;\""Verify the configuration was inserted:
PGPASSWORD=$(kubectl get secret management-postgres-postgresql -n central-org -o jsonpath='{.data.password}' | base64 -d)
kubectl exec -n central-org management-postgres-postgresql-0 -- bash -c "PGPASSWORD=$PGPASSWORD psql -U mn_user -d management_node -c \"
SELECT
c.idp_client_id as consumer,
p.name as product,
p.topic,
pr.name as producer_name,
pr.host as producer_host,
pr.port as producer_port,
COUNT(pca.id) as filter_attribute_count
FROM mn.consumer c
JOIN mn.product_consumer pc ON c.id = pc.consumer_id
JOIN mn.product p ON pc.product_id = p.id
JOIN mn.producer pr ON p.producer_id = pr.id
LEFT JOIN mn.product_consumer_attribute pca ON pc.id = pca.product_consumer_id
WHERE c.idp_client_id = 'management-node'
GROUP BY c.idp_client_id, p.name, p.topic, pr.name, pr.host, pr.port;
\""Expected output:
consumer | product | topic | producer_name | producer_host | producer_port | filter_attribute_count
-----------------+--------------------+---------------------+-----------------------+----------------------------------------------+---------------+------------------------
management-node | CarModelsKnowledge | knowledge | DEMO-PRODUCER-IA-NODE | federator-server.org-b.svc.cluster.local | 9001 | 0
management-node | CarModelsOntology | ontology.car.models | DEMO-PRODUCER-IA-NODE | federator-server.org-b.svc.cluster.local | 9001 | 0
## Federator Client
[!NOTE] used org-a as the namespace here, but change this to suit.
create namespace org-a (if not done earlier)
```sh
kubectl create namespace org-a
kubectl label namespace org-a istio-injection=enabled
make a secret containing the certs and trust stores needed for mtls, we are disabling mtls here but still need a dummy secret for the federator client to start!
# Create valid empty truststore for federator-client
keytool -genkeypair -alias dummy -keystore /tmp/truststore.jks \
-storepass changeit -keypass changeit -dname "CN=dummy" -keyalg RSA
keytool -delete -alias dummy -keystore /tmp/truststore.jks -storepass changeit
# Create cert secret
kubectl -n org-a create secret generic federator-client-certs \
--from-file=truststore.jks=/tmp/truststore.jks # Copy Keycloak client secret from keycloak namespace
kubectl get secret keycloak-management-node-client -n keycloak -o yaml | sed 's/namespace: keycloak/namespace: org-a/' | kubectl apply -f -
kubectl get secret keycloak-management-node-client -n keycloak -o yaml | sed 's/namespace: keycloak/namespace: org-b/' | kubectl apply -f -Note
The kafka-ia-node-user secret is automatically created by the ia-node-kafka chart in the namespace where it was installed (org-a in this example). If your federator-client is in a different namespace, copy it across:
kubectl get secret kafka-ia-node-user -n org-a -o yaml | sed 's/namespace: org-a/namespace: <your-client-namespace>/' | kubectl apply -f -for the below you will need to supply settings for your environment. see values-client-example.yaml
Create the federator-client connection config secret (this file typically contains client credentials, so keep it in a Secret):
helm upgrade --install federator-client ./charts/federator --namespace org-a --values ./charts/federator/examples/values-client-example.yamlneeds information about connections to management node, Keycloak, redis and also the cert and truststore
create namespace (if not already created for ia-node)
kubectl create namespace org-b
kubectl label namespace org-b istio-injection=enabledCopy authentication secrets from org-a to org-b (for accessing shared Kafka and MongoDB):
# Copy Kafka user secret (JAAS) for federator-server
kubectl get secret kafka-ia-node-user -n org-a -o json \
| jq 'del(
.metadata.namespace,
.metadata.resourceVersion,
.metadata.uid,
.metadata.creationTimestamp,
.metadata.managedFields,
.metadata.ownerReferences,
.metadata.labels
)
| .metadata.namespace="org-b"' \
| kubectl apply -f -
# Copy MongoDB password secret (if graph-server will be deployed in org-a)
kubectl get secret ia-node-user-password -n org-a -o yaml | sed 's/namespace: org-a/namespace: org-b/' | kubectl apply -f -
# Copy redis password
kubectl get secret oauth2-proxy-redis -n org-a -o yaml | sed 's/namespace: org-a/namespace: org-b/' | kubectl apply -f -Important
Cross-namespace secret requirements for federator-server:
The example values reference secrets that may not exist in the deployment namespace (org-b):
kafka-ia-node-user- created in org-a by the ia-node-kafka chart (copy it to org-b as shown)keycloak-management-node-client- created in keycloak namespace
Options:
-
Copy secrets to org-b (if using same cluster):
kubectl get secret keycloak-management-node-client -n keycloak -o yaml | sed 's/namespace: keycloak/namespace: org-b/' | kubectl apply -f - kubectl get secret kafka-ia-node-user -n org-a -o json \ | jq 'del( .metadata.namespace, .metadata.resourceVersion, .metadata.uid, .metadata.creationTimestamp, .metadata.managedFields, .metadata.ownerReferences, .metadata.labels ) | .metadata.namespace="org-b"' \ | kubectl apply -f -
-
Use hardcoded values in values-server-example.yaml (as shown in example file):
- (Removed) The federator chart examples avoid embedding secrets in values.
-
Production: Use a secret management solution (recommended):
- External Secrets Operator, Vault, AWS Secrets Manager, etc.
make a secret containing the certs and trust stores needed for mtls, we are disabling mtls here but still need a dummy secret for the federator server to start!
```sh
# Create valid empty truststore for federator-server
keytool -genkeypair -alias dummy -keystore /tmp/truststore.jks \
-storepass changeit -keypass changeit -dname "CN=dummy" -keyalg RSA -keysize 2048
keytool -delete -alias dummy -keystore /tmp/truststore.jks -storepass changeit
# Create valid empty keystore for federator-server (required even when mTLS is disabled)
keytool -genkeypair -alias dummy -keystore /tmp/keystore.p12 -storetype PKCS12 \
-storepass changeit -keypass changeit -dname "CN=dummy" -keyalg RSA -keysize 2048
# Create Kubernetes secret with both truststore and keystore
kubectl -n org-b create secret generic federator-server-certs \
--from-file=truststore.jks=/tmp/truststore.jks \
--from-file=keystore.p12=/tmp/keystore.p12
Note
Before deploying, ensure all required secrets exist in the org-b namespace. See the cross-namespace secret requirements note above and the REQUIRED SECRETS section in values-server-example.yaml.
for the below you will need to supply settings for your environment. see values-server-example.yaml
helm upgrade --install federator-server ./charts/federator --namespace org-b --values ./charts/federator/examples/values-server-example.yamlNow we need to insert some test data so we can see if the data flows properly.
The graph-server uses Attribute-Based Access Control (ABAC) to filter RDF data based on user security labels. We need to create a user that matches the JWT token's email claim with appropriate security clearances.
Important
When using Keycloak client_credentials grant (as in this example), the JWT token will contain user@test.com as the email claim. The user in MongoDB must have this exact email and possess security labels that allow access to the federated data.
MONGODB_PASSWORD=$(kubectl get secret mongodb-access-ia-node-user -n org-a -o jsonpath='{.data.password}' | base64 -d)
kubectl exec -n org-a mongodb-0 -c mongod -- mongosh --quiet -u ia-node-user -p "$MONGODB_PASSWORD" --authenticationDatabase access access --eval '
db.users.deleteMany({email: "user@test.com"});
db.users.insertOne({
externalId: "service-account-ianode",
name: "Service Account IANode",
userName: "service-account-ianode",
email: "user@test.com",
labels: [
{
name: "clearance",
value: "TS",
toString: "clearance=\"TS\"",
toDataLabelString: "classification=\"TS\""
},
{
name: "nationality",
value: "GBR",
toString: "nationality=\"GBR\"",
toDataLabelString: "permitted_nationalities=\"GBR\""
},
{
name: "deployed_organisation",
value: "ExampleOrg",
toString: "deployed_organisation=\"ExampleOrg\"",
toDataLabelString: "permitted_organisations=\"ExampleOrg\""
},
{
name: "personnel_type",
value: "GOV",
toString: "personnel_type=\"GOV\"",
toDataLabelString: null
}
],
active: true,
groups: [],
userGroups: [],
schemas: ["urn:ietf:params:scim:schemas:core:2.0:User"]
});
'Verify the user was inserted:
MONGODB_PASSWORD=$(kubectl get secret mongodb-access-ia-node-user -n org-a -o jsonpath='{.data.password}' | base64 -d)
kubectl exec -n org-a mongodb-0 -c mongod -- mongosh --quiet -u ia-node-user -p "$MONGODB_PASSWORD" --authenticationDatabase access access --eval 'db.users.find({email: "user@test.com"}, {userName: 1, email: 1, labels: 1, active: 1}).pretty()'Note
The security labels assigned to the user determine what RDF data they can access:
- clearance: Controls access based on data classification levels (e.g., TS = Top Secret)
- nationality: Restricts access based on permitted nationalities
- deployed_organisation: Limits access to data from specific organizations
- personnel_type: Additional personnel classification (e.g., GOV = Government)
If SPARQL queries return empty results despite data being present, check that the user's security labels match the labels attached to the RDF data.
The test-message-pod chart provides a simple way to send test messages to Kafka for federation testing.
First, build the Docker image:
cd charts/test-message-pod
docker build -t test-message-pod:local .
# kind load docker-image test-message-pod:local --name kind # if your using kind
cd ../..Then install the chart:
helm install test-msg ./charts/test-message-pod -n org-a \
--set kafka.bootstrapServer=kafka-cluster-kafka-bootstrap.org-a.svc.cluster.local:9092 \
--set kafka.topic=knowledge \
--set kafka.securityProtocol=SASL_PLAINTEXT \
--set kafkaCredentialsSecret.name=kafka-auth-config \
--set resources.requests.cpu=5m \
--set resources.requests.memory=16MiGet the Kafka user password from the secret:
KAFKA_PASSWORD=$(kubectl get secret kafka-ia-node-user -n org-a -o jsonpath='{.data.password}' | base64 -d)Install Kafka UI:
helm repo add kafka-ui https://provectus.github.io/kafka-ui-charts
helm upgrade --install kafka-ui kafka-ui/kafka-ui \
-n org-a \
--reset-values \
--set yamlApplicationConfig.kafka.clusters[0].name=kind-cluster \
--set yamlApplicationConfig.kafka.clusters[0].bootstrapServers=kafka-cluster-kafka-bootstrap.org-a.svc.cluster.local:9092 \
--set-string yamlApplicationConfig.kafka.clusters[0].properties.security\.protocol=SASL_PLAINTEXT \
--set-string yamlApplicationConfig.kafka.clusters[0].properties.sasl\.mechanism=SCRAM-SHA-512 \
--set-string yamlApplicationConfig.kafka.clusters[0].properties.sasl\.jaas\.config="org.apache.kafka.common.security.scram.ScramLoginModule required username=\"kafka-ia-node-user\" password=\"${KAFKA_PASSWORD}\";" \
--set volumeMounts[0].name=kafka-auth-config \
--set volumeMounts[0].mountPath=/kafka-auth \
--set volumeMounts[0].readOnly=true \
--set volumes[0].name=kafka-auth-config \
--set volumes[0].secret.secretName=kafka-auth-configfollow the text onscreen after install to port forward and open the UI.
inside the federtor client there is a jobrunr which controls the jobs for fetching new data from the server. you can expose the control dashboard for this and trigger jobs manually.
kubectl -n org-a port-forward deployment/federator-client 8085:8085navigate to that page you you will see the UI.
Important
For federation to work correctly, RDF messages MUST include a Content-Type:application/trig header and be sent as complete documents in a single Kafka message. The federator-server requires this header to properly identify and parse RDF content. The built-in script handles both requirements automatically.
Note: For local/dev on kind, this guide uses Kafka's SASL_PLAINTEXT listener on port 9092. If you're targeting a TLS listener (or AWS MSK), set kafka.securityProtocol=SASL_SSL and use the appropriate TLS port.
# Send the included test data file (script automatically includes header and sends as single message)
kubectl exec -n org-a test-msg-test-message-pod -- /opt/scripts/send-kafka-message.sh /tmp/test-data.trigNote
The built-in script automatically handles two critical requirements:
- Content-Type header: Adds
Content-Type:application/trigheader so federator-server can identify and parse RDF content - Single message format: Uses
tr "\n" " "to convert newlines to spaces, ensuring the entire RDF document is sent as one Kafka message - Istio port exclusions: The install command adds
traffic.sidecar.istio.io/excludeOutboundPorts=9092(using--set-string) so the Envoy sidecar does not intercept Kafka's plaintext listener. Without this exclusion, connections terminate during authentication.
Without the Content-Type header, federator-server will fail to process messages with parsing errors and return 0 messages to federator-client. Without single-message format, kafka-console-producer treats each line as a separate message, causing errors like: Expected BNode or IRI: Got: [KEYWORD:PREFIX]
The federation flow is: Kafka (org-a) → Federator-Server (org-b) → gRPC → Federator-Client (org-a) → Kafka (org-a) → Graph-Server (org-a)
graph LR
A[Test Message Pod<br/>org-a] -->|1. Publish with<br/>Content-Type header| B[Kafka Topic: knowledge<br/>org-a]
B -->|2. Consume| C[Federator-Server<br/>org-b]
C -->|3. gRPC Stream| D[Federator-Client<br/>org-a]
D -->|4. Publish| E[Kafka Topic:<br/>DEMOPRODUCERIANODE-knowledge<br/>org-a]
E -->|5. Consume| F[Graph-Server<br/>org-a]
F -->|6. Store RDF| G[(RDF Triple Store<br/>/knowledge/upload)]
H[Management Node<br/>central-org] -.->|Configuration| D
I[Access-API<br/>org-a] -.->|User Attributes<br/>ABAC| F
J[(MongoDB<br/>org-a)] -.->|Security Labels| I
style A fill:#e1f5ff
style C fill:#ffe1e1
style D fill:#e1f5ff
style F fill:#e1f5ff
style H fill:#fff4e1
style B fill:#f0f0f0
style E fill:#f0f0f0
After sending a test message to the knowledge topic in org-a, the federator-server makes it available via gRPC. The federator-client polls the management-node for configuration, then pulls messages from the federator-server via gRPC, and writes them back to Kafka in org-a with the DEMOPRODUCERIANODE- prefix. The graph-server in org-a consumes these federated messages and stores them in the RDF triple store.
Important: The federator-client's gRPC connection is configured in the connection-configuration.json file mounted at /config/connection-configuration.json (provided via the federator-client-connection-config Secret in the example values). Ensure this points to the correct federator-server endpoint:
connection-configuration.json: |-
[
{
"credentials": {
"name": "default",
"key": "demo-api-key"
},
"server": {
"name": "FederatorServerOrgB",
"host": "federator-server.org-b.svc.cluster.local",
"port": 9001
},
"tls": {
"enabled": false
}
}
]The federator-client writes messages back to org-a's Kafka (configured via kafka.bootstrapServers in client.properties):
kafka.bootstrapServers=kafka-cluster-kafka-bootstrap.org-a.svc.cluster.local:9092The graph-server in org-a also consumes from org-a's Kafka:
kafkaCluster.bootstrapServers=kafka-cluster-kafka-bootstrap.org-a.svc.cluster.local:9092Polling Interval Configuration:
The federator-client polling interval is controlled by the schedule_type and schedule_expression columns in the management-node database (mn.product_consumer table).
To set polling to every minute, update the database:
-- Update schedule to every minute (cron format: * * * * *)
UPDATE mn.product_consumer
SET schedule_type = 'cron', schedule_expression = '* * * * *'
WHERE consumer_id IN (SELECT id FROM mn.consumer WHERE idp_client_id = 'management-node');
-- Or use interval format: PT1M
UPDATE mn.product_consumer
SET schedule_type = 'interval', schedule_expression = 'PT1M'
WHERE consumer_id IN (SELECT id FROM mn.consumer WHERE idp_client_id = 'management-node');Note: This requires management-node v1.1.0+ with Flyway migration V20251013135880__add_schedule_expression.sql applied. If using an older version, the federator-client defaults to PT10M (10 minute polling).
Check the federator-client logs to verify it's processing messages:
# Check if federator-client fetched configuration from management-node
kubectl logs -n org-a -l app.kubernetes.io/name=federator-client --tail=100 | grep "Config fetched"
# Check the registered job interval
kubectl logs -n org-a -l app.kubernetes.io/name=federator-client --tail=100 | grep "Registered recurring job"
# Wait for the next scheduled job run and check for message processing
kubectl logs -n org-a -l app.kubernetes.io/name=federator-client --followCheck the federator-server logs to see if it's consuming from Kafka:
kubectl logs -n org-b -l app.kubernetes.io/name=federator-server --tail=100Query the graph-server to verify that federated data has been successfully ingested:
CLIENT_SECRET=$(kubectl get secret keycloak-ianode-secret -n keycloak -o jsonpath='{.data.client-secret}' | base64 -d)
kubectl exec -n org-a graph-server-0 -- bash -c "
TOKEN=\$(curl -s -X POST 'http://keycloak.keycloak.svc.cluster.local/realms/ianode/protocol/openid-connect/token' \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'grant_type=client_credentials' \
-d 'client_id=ianode' \
-d 'client_secret=${CLIENT_SECRET}' \
| sed -n 's/.*\"access_token\":\"\([^\"]*\)\".*/\1/p')
curl -s -X POST 'http://localhost:3030/knowledge/query' \
-H \"Authorization: Bearer \$TOKEN\" \
-H 'Content-Type: application/sparql-query' \
-H 'Accept: application/sparql-results+json' \
--data-binary 'SELECT * WHERE { ?s ?p ?o } LIMIT 10'
"Expected output: JSON with SPARQL results containing RDF triples.
If no data is returned:
-
Check if data was ingested by graph-server:
kubectl logs -n org-a graph-server-0 --tail=100 | grep "DEMOPRODUCERIANODE-knowledge"
Look for:
Batch: Finished [X, Y]indicating successful message processing. -
Check for authorization errors:
kubectl logs -n org-a graph-server-0 --tail=50 | grep -E "403|Rejected|No request attributes"
If you see "No request attributes for user", the MongoDB user is missing or lacks required security labels.
-
Verify user exists with correct email:
MONGODB_PASSWORD=$(kubectl get secret mongodb-access-ia-node-user -n org-a -o jsonpath='{.data.password}' | base64 -d) kubectl exec -n org-a mongodb-0 -c mongod -- mongosh --quiet -u ia-node-user -p "$MONGODB_PASSWORD" --authenticationDatabase access access --eval 'db.users.findOne({email: "user@test.com"})'
-
Check federation flow status:
# Verify messages in DEMOPRODUCERIANODE-knowledge topic kubectl logs -n org-a graph-server-0 --since=30m | grep "Batch:" | tail -5
Note
The graph-server endpoint is /knowledge/query not /knowledge/sparql. Data is ingested via the /knowledge/upload endpoint (used by Kafka consumer) and queried via /knowledge/query.
If federator-client logs show Received response from Server: 0 despite messages existing in the source topic:
Problem: The federator-server requires a Content-Type:application/trig header to identify and parse RDF messages.
Symptoms:
- Federator-server logs show:
Kafka Header: ''(empty header) - Federator-server processes messages but encounters parsing errors
- Federator-client receives 0 messages from the server
- Messages exist in source topic but aren't federated
Solution: Ensure all messages include the Content-Type header:
# ✓ CORRECT - Include Content-Type header
PAYLOAD=$(cat /tmp/test-data.trig | tr "\n" " ")
echo -e "Content-Type:application/trig\t$PAYLOAD" | \
kafka-console-producer.sh \
--bootstrap-server kafka-cluster-kafka-bootstrap.org-a.svc.cluster.local:9092 \
--topic knowledge \
--producer.config /opt/kafka/config/kafka-config.properties \
--property parse.headers=true \
--property headers.key.separator=: \
--property headers.delimiter=$'\t'Verification: Check federator-server logs for the Content-Type header:
kubectl logs -n org-b -l app.kubernetes.io/name=federator-server --tail=100 | grep "Kafka Header"You should see:
Kafka Header: 'Content-Type: application/trig'
Processed message. Offset: 'X'
If you see Kafka Header: '', the messages are missing the required header.
If you see TopicAuthorizationException: Not authorized to access topics: [DEMOPRODUCERIANODE-knowledge] in the federator-client logs:
-
Verify ACL exists in KafkaUser spec:
kubectl get kafkauser kafka-ia-node-user -n org-a -o jsonpath='{.spec.authorization.acls}' | jq '.[] | select(.resource.name | startswith("DEMO"))'
-
Check KafkaUser reconciliation status:
kubectl get kafkauser kafka-ia-node-user -n org-a -o jsonpath='{.status}' | jq
Ensure
status.conditions[].typeisReadyandstatusisTrue. -
Verify topic exists or can be auto-created:
kubectl exec -n org-a test-msg-test-message-pod -- /tmp/kafka/bin/kafka-topics.sh \ --bootstrap-server kafka-cluster-kafka-bootstrap.org-a.svc.cluster.local:9092 \ --list | grep DEMO
-
If prefix pattern ACLs don't work, try creating explicit topic and literal ACL:
# Create topic explicitly kubectl apply -f - <<EOF apiVersion: kafka.strimzi.io/v1beta2 kind: KafkaTopic metadata: name: demoproducerianode-knowledge namespace: org-a labels: strimzi.io/cluster: kafka-cluster spec: partitions: 1 replicas: 1 config: retention.ms: 604800000 EOF # Add literal ACL kubectl patch kafkauser kafka-ia-node-user -n org-a --type='json' -p='[ { "op": "add", "path": "/spec/authorization/acls/-", "value": { "operations": ["Create", "Describe", "Read", "Write"], "resource": { "type": "topic", "name": "DEMOPRODUCERIANODE-knowledge", "patternType": "literal" } } } ]'
-
After making ACL changes, always restart the federator-client:
kubectl rollout restart deployment federator-client -n org-a
If you see errors like ERROR org.apache.jena.riot - [line: 1, col: 1 ] Expected BNode or IRI: Got: [KEYWORD:PREFIX] in the federator-server logs:
Problem: The federator-server is receiving line-by-line messages instead of complete RDF documents. Each line of the RDF file was sent as a separate Kafka message.
Root Cause: Using kafka-console-producer with default settings treats each line as a separate message:
# ❌ WRONG - Sends line-by-line
cat /tmp/test-data.trig | kafka-console-producer.sh ...
# ❌ WRONG - Also sends line-by-line
kafka-console-producer.sh ... < /tmp/test-data.trigSolution: Convert newlines to spaces AND include Content-Type header before sending, so the entire RDF document becomes a single Kafka message with proper content identification:
# ✓ CORRECT - Sends as single message with Content-Type header
PAYLOAD=$(cat /tmp/test-data.trig | tr "\n" " ")
echo -e "Content-Type:application/trig\t$PAYLOAD" | \
kafka-console-producer.sh \
--property parse.headers=true \
--property headers.key.separator=: \
--property headers.delimiter=$'\t' ...Why this matters:
- The federator-server requires the
Content-Type:application/trigheader to identify and parse RDF messages correctly - Without this header, federator-server will log
Kafka Header: ''and fail with parsing errors, returning 0 messages - Each Kafka message must contain a complete RDF document (TriG/Turtle format)
- Line-by-line messages cause the parser to fail: it tries to parse
PREFIX rdf: <...>as a complete RDF graph - The federation flow requires:
Complete RDF Document with Header→ Kafka → Federator-Server → gRPC → Federator-Client → Kafka → Graph-Server
Verification: Check if you're receiving complete documents:
# View a single message from the topic
kubectl exec -n org-a test-msg-test-message-pod -- bash -c '
/tmp/kafka/bin/kafka-console-consumer.sh \
--bootstrap-server kafka-cluster-kafka-bootstrap.org-a.svc.cluster.local:9092 \
--consumer.config /opt/kafka/config/kafka-config.properties \
--topic knowledge \
--max-messages 1 \
--from-beginning \
--timeout-ms 3000 2>/dev/null
' | head -c 500If you see only a single line like PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>, the data was sent incorrectly. The message should contain the entire RDF document on one line (with spaces instead of newlines).
The full configuration and installation can be found in each individual chart in the corresponding README.md files.
Uninstall in reverse order, i.e.
helm uninstall federator-server -n org-b
kubectl delete ns org-bhelm uninstall federator-client -n org-a
kubectl delete ns org-bhelm uninstall management-node -n central-org helm uninstall management-postgres -n central-org
kubectl delete ns central-orghelm uninstall ia-node -n org-b helm uninstall ia-node-kafka -n org-b
helm uninstall my-strimzi-cluster-operator -n kafka-operatorhelm uninstall ia-node-mongodb -n org-b
helm uninstall community-operator -n mongodb-operator
helm repo remove mongodbhelm uninstall oauth2-proxy -n org-b
helm uninstall ia-node-oidc -n org-bhelm uninstall keycloak -n keycloakUninstall namespaces:
kubectl delete ns org-b kafka-operator keycloak mongodb-operatorThis repository contains several Helm charts to support deployment of an IA Node.
-
Core functionality
ia-node: Helm chart to deploy the IA Node application components.
-
Supportive functionality
ia-node-oidc: Helm chart intended to ease deployment and configuration of an OIDC conformant Identity Provider (IdP) Keycloak integrated with Istio, OAuth2 Proxy and Redis, which are prerequisites required to deploy an IA Node.ia-node-mongodb: Helm chart intended to ease deployment and configuration of MongoDB which is a prerequisite required to deploy an IA Node.ia-node-kafka: Helm chart intended to ease deployment and configuration of Apache Kafka which is a prerequisite required to deploy an IA Node.
This repository has been developed with public funding as part of the National Digital Twin Programme (NDTP), a UK Government initiative. NDTP, alongside its partners, has invested in this work to advance open, secure, and reusable digital twin technologies for any organisation, whether from the public or private sector, irrespective of size.
This repository contains both source code and documentation, which are covered by different licenses:
- Code: Developed and maintained by National Digital Twin Programme. Licensed under the Apache License 2.0.
- Documentation: Licensed under the Open Government Licence v3.0.
SeeLICENSE.md,OGL_LICENSE.md, andNOTICE.mdfor details.
We take security seriously. If you believe you have found a security vulnerability in this repository, please follow our responsible disclosure process outlined in SECURITY.md.
We welcome contributions that align with the Programme’s objectives. Please read our CONTRIBUTING.md guidelines before submitting pull requests.
This repository has benefited from collaboration with various organisations. For a list of acknowledgments, see ACKNOWLEDGEMENTS.md.
For questions or support, check our Issues or contact the NDTP team on ndtp@businessandtrade.gov.uk.
Maintained by the National Digital Twin Programme (NDTP).
© Crown Copyright 2025. This work has been developed by the National Digital Twin Programme and is legally attributed to the Department for Business and Trade (UK) as the governing entity.