{"configuration":{},"description":"C4 Container model with ThingsBoard, Kafka, Spark Streaming ML, TDengine, Neo4j, Ontology DB, TB Adapter and Markov risk model","documentation":{},"id":14,"lastModifiedAgent":"structurizr-onpremises/dsl-editor/b3bd314d-9359-45a9-82ea-2df589a9c783","lastModifiedDate":"2026-03-04T12:25:04Z","lastModifiedUser":"ralfs.matisons","model":{"people":[{"description":"Views dashboards and reacts to alerts.","id":"1","location":"Unspecified","name":"Operator","properties":{"structurizr.dsl.identifier":"operator"},"relationships":[{"description":"Views telemetry, historical data, alarms, Markov states and mitigation recommendations","destinationId":"6","id":"17","sourceId":"1","tags":"Relationship","technology":"HTTPS"},{"description":"Views telemetry, historical data, alarms, Markov states and mitigation recommendations","destinationId":"5","id":"18","linkedRelationshipId":"17","sourceId":"1","technology":"HTTPS"}],"tags":"Element,Person"}],"softwareSystems":[{"description":"Devices, gateways, PLC/RTU sources sending telemetry.","documentation":{},"id":"2","location":"Unspecified","name":"IoT/OT Devices","properties":{"structurizr.dsl.identifier":"iot"},"relationships":[{"description":"Sends raw telemetry directly","destinationId":"6","id":"19","sourceId":"2","tags":"Relationship","technology":"MQTT / HTTP / CoAP"},{"description":"Sends raw telemetry directly","destinationId":"5","id":"20","linkedRelationshipId":"19","sourceId":"2","technology":"MQTT / HTTP / CoAP"}],"tags":"Element,Software System"},{"description":"External social feeds, APIs, webhooks.","documentation":{},"id":"3","location":"Unspecified","name":"Social Networks","properties":{"structurizr.dsl.identifier":"social"},"relationships":[{"description":"Webhooks and API pulls","destinationId":"9","id":"21","sourceId":"3","tags":"Relationship","technology":"HTTPS"},{"description":"Webhooks and API pulls","destinationId":"5","id":"22","linkedRelationshipId":"21","sourceId":"3","technology":"HTTPS"}],"tags":"Element,Software System"},{"description":"Network devices, servers, and sensors emitting syslog.","documentation":{},"id":"4","location":"Unspecified","name":"Sensors & Infrastructure (Syslog)","properties":{"structurizr.dsl.identifier":"sys"},"relationships":[{"description":"Sends syslog messages","destinationId":"10","id":"24","sourceId":"4","tags":"Relationship","technology":"UDP/TCP 514"},{"description":"Sends syslog messages","destinationId":"5","id":"25","linkedRelationshipId":"24","sourceId":"4","technology":"UDP/TCP 514"}],"tags":"Element,Software System"},{"containers":[{"description":"Direct IoT/OT ingest, dashboards, alarms, rule engine, device/entity management.","documentation":{},"id":"6","name":"ThingsBoard","properties":{"structurizr.dsl.identifier":"rtsa.thingsboard"},"relationships":[{"description":"Stores internal platform data","destinationId":"7","id":"27","sourceId":"6","tags":"Relationship","technology":"JDBC / SQL"},{"description":"Publishes raw device telemetry/events for downstream analytics","destinationId":"8","id":"28","sourceId":"6","tags":"Relationship","technology":"Rule Engine / Kafka integration"}],"tags":"Element,Container","technology":"ThingsBoard"},{"description":"ThingsBoard internal metadata, relations, alarms and dashboard state.","documentation":{},"id":"7","name":"PostgreSQL (ThingsBoard)","properties":{"structurizr.dsl.identifier":"rtsa.pg_tb"},"tags":"Element,Container","technology":"PostgreSQL"},{"description":"Real-time event streaming backbone for raw and processed events.","documentation":{},"id":"8","name":"Apache Kafka","properties":{"structurizr.dsl.identifier":"rtsa.kafka"},"relationships":[{"description":"Consumes raw telemetry/events","destinationId":"11","id":"29","sourceId":"8","tags":"Relationship","technology":"Kafka Consumer"}],"tags":"Element,Container","technology":"Apache Kafka"},{"description":"Collects social feeds and publishes normalized events to Kafka.","documentation":{},"id":"9","name":"Social Ingest API","properties":{"structurizr.dsl.identifier":"rtsa.social_api"},"relationships":[{"description":"Publishes social.events","destinationId":"8","id":"23","sourceId":"9","tags":"Relationship","technology":"Kafka Producer"}],"tags":"Element,Container","technology":"FastAPI / Flask"},{"description":"Collects RFC3164/RFC5424 syslogs and publishes normalized events to Kafka.","documentation":{},"id":"10","name":"Syslog Ingest API","properties":{"structurizr.dsl.identifier":"rtsa.syslog_api"},"relationships":[{"description":"Publishes infra.syslog","destinationId":"8","id":"26","sourceId":"10","tags":"Relationship","technology":"Kafka Producer"}],"tags":"Element,Container","technology":"FastAPI / Flask + UDP/TCP listener"},{"description":"Consumes raw telemetry, validates data, fills missing values, detects anomalies, and publishes cleaned data.","documentation":{},"id":"11","name":"Spark Streaming ML","properties":{"structurizr.dsl.identifier":"rtsa.spark_ml"},"relationships":[{"description":"Writes cleaned timestamped time-series","destinationId":"12","id":"30","sourceId":"11","tags":"Relationship","technology":"SQL / Native client"},{"description":"Publishes cleaned/imputed data for risk-state calculation","destinationId":"16","id":"31","sourceId":"11","tags":"Relationship","technology":"Kafka / Stream / API"},{"description":"Notifies detected anomalies for alerting","destinationId":"6","id":"32","sourceId":"11","tags":"Relationship","technology":"REST API / MQTT"}],"tags":"Element,Container","technology":"Spark Structured Streaming / MLlib"},{"description":"Stores cleaned timestamped time-series for historical analysis.","documentation":{},"id":"12","name":"TDengine","properties":{"structurizr.dsl.identifier":"rtsa.tdengine"},"relationships":[{"description":"Provides historical and aggregated time-series","destinationId":"13","id":"33","sourceId":"12","tags":"Relationship","technology":"SQL / Native client"}],"tags":"Element,Container","technology":"TDengine"},{"description":"Reads historical and aggregated series from TDengine and publishes them to ThingsBoard for visualization.","documentation":{},"id":"13","name":"TB Adapter API","properties":{"structurizr.dsl.identifier":"rtsa.tb_adapter"},"relationships":[{"description":"Publishes historical/aggregated telemetry for visualization","destinationId":"6","id":"34","sourceId":"13","tags":"Relationship","technology":"REST API / MQTT"}],"tags":"Element,Container","technology":"FastAPI / Flask"},{"description":"Stores graph context of assets, dependencies, threats, vulnerabilities and state relationships.","documentation":{},"id":"14","name":"Neo4j","properties":{"structurizr.dsl.identifier":"rtsa.neo4j"},"relationships":[{"description":"Publishes Markov states, risk scores and mitigation recommendations","destinationId":"6","id":"37","sourceId":"14","tags":"Relationship","technology":"REST API / MQTT"}],"tags":"Element,Container","technology":"Neo4j"},{"description":"Stores ontology concepts, rules and mitigations used by the risk model.","documentation":{},"id":"15","name":"Ontology DB","properties":{"structurizr.dsl.identifier":"rtsa.ontology_db"},"tags":"Element,Container","technology":"RDF Store / OWL Store / Graph DB"},{"description":"Consumes cleaned data, calculates Markov states and risk scores, uses graph and ontology context, and selects mitigations.","documentation":{},"id":"16","name":"Markov Risk & Mitigation Service","properties":{"structurizr.dsl.identifier":"rtsa.markov"},"relationships":[{"description":"Reads/writes graph context and state relationships","destinationId":"14","id":"35","sourceId":"16","tags":"Relationship","technology":"Bolt / Cypher"},{"description":"Reads ontology rules and mitigation knowledge","destinationId":"15","id":"36","sourceId":"16","tags":"Relationship","technology":"SPARQL / API"}],"tags":"Element,Container","technology":"Python / Scala / Java service"}],"description":"Processes real-time data and serves dashboards through ThingsBoard.","documentation":{},"id":"5","location":"Unspecified","name":"Streaming Platform","properties":{"structurizr.dsl.identifier":"rtsa"},"tags":"Element,Software System"}]},"name":"Realtime smart cities","properties":{"structurizr.dsl":"d29ya3NwYWNlICJSZWFsdGltZSBzbWFydCBjaXRpZXMiICJDNCBDb250YWluZXIgbW9kZWwgd2l0aCBUaGluZ3NCb2FyZCwgS2Fma2EsIFNwYXJrIFN0cmVhbWluZyBNTCwgVERlbmdpbmUsIE5lbzRqLCBPbnRvbG9neSBEQiwgVEIgQWRhcHRlciBhbmQgTWFya292IHJpc2sgbW9kZWwiIHsKCiAgIWlkZW50aWZpZXJzIGhpZXJhcmNoaWNhbAoKICBtb2RlbCB7CiAgICBvcGVyYXRvciA9IHBlcnNvbiAiT3BlcmF0b3IiICJWaWV3cyBkYXNoYm9hcmRzIGFuZCByZWFjdHMgdG8gYWxlcnRzLiIKCiAgICBpb3QgPSBzb2Z0d2FyZVN5c3RlbSAiSW9UL09UIERldmljZXMiICJEZXZpY2VzLCBnYXRld2F5cywgUExDL1JUVSBzb3VyY2VzIHNlbmRpbmcgdGVsZW1ldHJ5LiIKICAgIHNvY2lhbCA9IHNvZnR3YXJlU3lzdGVtICJTb2NpYWwgTmV0d29ya3MiICJFeHRlcm5hbCBzb2NpYWwgZmVlZHMsIEFQSXMsIHdlYmhvb2tzLiIKICAgIHN5cyA9IHNvZnR3YXJlU3lzdGVtICJTZW5zb3JzICYgSW5mcmFzdHJ1Y3R1cmUgKFN5c2xvZykiICJOZXR3b3JrIGRldmljZXMsIHNlcnZlcnMsIGFuZCBzZW5zb3JzIGVtaXR0aW5nIHN5c2xvZy4iCgogICAgcnRzYSA9IHNvZnR3YXJlU3lzdGVtICJTdHJlYW1pbmcgUGxhdGZvcm0iICJQcm9jZXNzZXMgcmVhbC10aW1lIGRhdGEgYW5kIHNlcnZlcyBkYXNoYm9hcmRzIHRocm91Z2ggVGhpbmdzQm9hcmQuIiB7CgogICAgICB0aGluZ3Nib2FyZCA9IGNvbnRhaW5lciAiVGhpbmdzQm9hcmQiICJEaXJlY3QgSW9UL09UIGluZ2VzdCwgZGFzaGJvYXJkcywgYWxhcm1zLCBydWxlIGVuZ2luZSwgZGV2aWNlL2VudGl0eSBtYW5hZ2VtZW50LiIgIlRoaW5nc0JvYXJkIgogICAgICBwZ190YiA9IGNvbnRhaW5lciAiUG9zdGdyZVNRTCAoVGhpbmdzQm9hcmQpIiAiVGhpbmdzQm9hcmQgaW50ZXJuYWwgbWV0YWRhdGEsIHJlbGF0aW9ucywgYWxhcm1zIGFuZCBkYXNoYm9hcmQgc3RhdGUuIiAiUG9zdGdyZVNRTCIKCiAgICAgIGthZmthID0gY29udGFpbmVyICJBcGFjaGUgS2Fma2EiICJSZWFsLXRpbWUgZXZlbnQgc3RyZWFtaW5nIGJhY2tib25lIGZvciByYXcgYW5kIHByb2Nlc3NlZCBldmVudHMuIiAiQXBhY2hlIEthZmthIgoKICAgICAgc29jaWFsX2FwaSA9IGNvbnRhaW5lciAiU29jaWFsIEluZ2VzdCBBUEkiICJDb2xsZWN0cyBzb2NpYWwgZmVlZHMgYW5kIHB1Ymxpc2hlcyBub3JtYWxpemVkIGV2ZW50cyB0byBLYWZrYS4iICJGYXN0QVBJIC8gRmxhc2siCiAgICAgIHN5c2xvZ19hcGkgPSBjb250YWluZXIgIlN5c2xvZyBJbmdlc3QgQVBJIiAiQ29sbGVjdHMgUkZDMzE2NC9SRkM1NDI0IHN5c2xvZ3MgYW5kIHB1Ymxpc2hlcyBub3JtYWxpemVkIGV2ZW50cyB0byBLYWZrYS4iICJGYXN0QVBJIC8gRmxhc2sgKyBVRFAvVENQIGxpc3RlbmVyIgoKICAgICAgc3BhcmtfbWwgPSBjb250YWluZXIgIlNwYXJrIFN0cmVhbWluZyBNTCIgIkNvbnN1bWVzIHJhdyB0ZWxlbWV0cnksIHZhbGlkYXRlcyBkYXRhLCBmaWxscyBtaXNzaW5nIHZhbHVlcywgZGV0ZWN0cyBhbm9tYWxpZXMsIGFuZCBwdWJsaXNoZXMgY2xlYW5lZCBkYXRhLiIgIlNwYXJrIFN0cnVjdHVyZWQgU3RyZWFtaW5nIC8gTUxsaWIiCgogICAgICB0ZGVuZ2luZSA9IGNvbnRhaW5lciAiVERlbmdpbmUiICJTdG9yZXMgY2xlYW5lZCB0aW1lc3RhbXBlZCB0aW1lLXNlcmllcyBmb3IgaGlzdG9yaWNhbCBhbmFseXNpcy4iICJURGVuZ2luZSIKCiAgICAgIHRiX2FkYXB0ZXIgPSBjb250YWluZXIgIlRCIEFkYXB0ZXIgQVBJIiAiUmVhZHMgaGlzdG9yaWNhbCBhbmQgYWdncmVnYXRlZCBzZXJpZXMgZnJvbSBURGVuZ2luZSBhbmQgcHVibGlzaGVzIHRoZW0gdG8gVGhpbmdzQm9hcmQgZm9yIHZpc3VhbGl6YXRpb24uIiAiRmFzdEFQSSAvIEZsYXNrIgoKICAgICAgbmVvNGogPSBjb250YWluZXIgIk5lbzRqIiAiU3RvcmVzIGdyYXBoIGNvbnRleHQgb2YgYXNzZXRzLCBkZXBlbmRlbmNpZXMsIHRocmVhdHMsIHZ1bG5lcmFiaWxpdGllcyBhbmQgc3RhdGUgcmVsYXRpb25zaGlwcy4iICJOZW80aiIKICAgICAgb250b2xvZ3lfZGIgPSBjb250YWluZXIgIk9udG9sb2d5IERCIiAiU3RvcmVzIG9udG9sb2d5IGNvbmNlcHRzLCBydWxlcyBhbmQgbWl0aWdhdGlvbnMgdXNlZCBieSB0aGUgcmlzayBtb2RlbC4iICJSREYgU3RvcmUgLyBPV0wgU3RvcmUgLyBHcmFwaCBEQiIKCiAgICAgIG1hcmtvdiA9IGNvbnRhaW5lciAiTWFya292IFJpc2sgJiBNaXRpZ2F0aW9uIFNlcnZpY2UiICJDb25zdW1lcyBjbGVhbmVkIGRhdGEsIGNhbGN1bGF0ZXMgTWFya292IHN0YXRlcyBhbmQgcmlzayBzY29yZXMsIHVzZXMgZ3JhcGggYW5kIG9udG9sb2d5IGNvbnRleHQsIGFuZCBzZWxlY3RzIG1pdGlnYXRpb25zLiIgIlB5dGhvbiAvIFNjYWxhIC8gSmF2YSBzZXJ2aWNlIgogICAgfQoKICAgIG9wZXJhdG9yIC0+IHJ0c2EudGhpbmdzYm9hcmQgIlZpZXdzIHRlbGVtZXRyeSwgaGlzdG9yaWNhbCBkYXRhLCBhbGFybXMsIE1hcmtvdiBzdGF0ZXMgYW5kIG1pdGlnYXRpb24gcmVjb21tZW5kYXRpb25zIiAiSFRUUFMiCgogICAgaW90IC0+IHJ0c2EudGhpbmdzYm9hcmQgIlNlbmRzIHJhdyB0ZWxlbWV0cnkgZGlyZWN0bHkiICJNUVRUIC8gSFRUUCAvIENvQVAiCgogICAgc29jaWFsIC0+IHJ0c2Euc29jaWFsX2FwaSAiV2ViaG9va3MgYW5kIEFQSSBwdWxscyIgIkhUVFBTIgogICAgcnRzYS5zb2NpYWxfYXBpIC0+IHJ0c2Eua2Fma2EgIlB1Ymxpc2hlcyBzb2NpYWwuZXZlbnRzIiAiS2Fma2EgUHJvZHVjZXIiCgogICAgc3lzIC0+IHJ0c2Euc3lzbG9nX2FwaSAiU2VuZHMgc3lzbG9nIG1lc3NhZ2VzIiAiVURQL1RDUCA1MTQiCiAgICBydHNhLnN5c2xvZ19hcGkgLT4gcnRzYS5rYWZrYSAiUHVibGlzaGVzIGluZnJhLnN5c2xvZyIgIkthZmthIFByb2R1Y2VyIgoKICAgIHJ0c2EudGhpbmdzYm9hcmQgLT4gcnRzYS5wZ190YiAiU3RvcmVzIGludGVybmFsIHBsYXRmb3JtIGRhdGEiICJKREJDIC8gU1FMIgogICAgcnRzYS50aGluZ3Nib2FyZCAtPiBydHNhLmthZmthICJQdWJsaXNoZXMgcmF3IGRldmljZSB0ZWxlbWV0cnkvZXZlbnRzIGZvciBkb3duc3RyZWFtIGFuYWx5dGljcyIgIlJ1bGUgRW5naW5lIC8gS2Fma2EgaW50ZWdyYXRpb24iCgogICAgcnRzYS5rYWZrYSAtPiBydHNhLnNwYXJrX21sICJDb25zdW1lcyByYXcgdGVsZW1ldHJ5L2V2ZW50cyIgIkthZmthIENvbnN1bWVyIgoKICAgIHJ0c2Euc3BhcmtfbWwgLT4gcnRzYS50ZGVuZ2luZSAiV3JpdGVzIGNsZWFuZWQgdGltZXN0YW1wZWQgdGltZS1zZXJpZXMiICJTUUwgLyBOYXRpdmUgY2xpZW50IgogICAgcnRzYS5zcGFya19tbCAtPiBydHNhLm1hcmtvdiAiUHVibGlzaGVzIGNsZWFuZWQvaW1wdXRlZCBkYXRhIGZvciByaXNrLXN0YXRlIGNhbGN1bGF0aW9uIiAiS2Fma2EgLyBTdHJlYW0gLyBBUEkiCiAgICBydHNhLnNwYXJrX21sIC0+IHJ0c2EudGhpbmdzYm9hcmQgIk5vdGlmaWVzIGRldGVjdGVkIGFub21hbGllcyBmb3IgYWxlcnRpbmciICJSRVNUIEFQSSAvIE1RVFQiCgogICAgcnRzYS50ZGVuZ2luZSAtPiBydHNhLnRiX2FkYXB0ZXIgIlByb3ZpZGVzIGhpc3RvcmljYWwgYW5kIGFnZ3JlZ2F0ZWQgdGltZS1zZXJpZXMiICJTUUwgLyBOYXRpdmUgY2xpZW50IgogICAgcnRzYS50Yl9hZGFwdGVyIC0+IHJ0c2EudGhpbmdzYm9hcmQgIlB1Ymxpc2hlcyBoaXN0b3JpY2FsL2FnZ3JlZ2F0ZWQgdGVsZW1ldHJ5IGZvciB2aXN1YWxpemF0aW9uIiAiUkVTVCBBUEkgLyBNUVRUIgoKICAgIHJ0c2EubWFya292IC0+IHJ0c2EubmVvNGogIlJlYWRzL3dyaXRlcyBncmFwaCBjb250ZXh0IGFuZCBzdGF0ZSByZWxhdGlvbnNoaXBzIiAiQm9sdCAvIEN5cGhlciIKICAgIHJ0c2EubWFya292IC0+IHJ0c2Eub250b2xvZ3lfZGIgIlJlYWRzIG9udG9sb2d5IHJ1bGVzIGFuZCBtaXRpZ2F0aW9uIGtub3dsZWRnZSIgIlNQQVJRTCAvIEFQSSIKICAgIHJ0c2EubmVvNGogLT4gcnRzYS50aGluZ3Nib2FyZCAiUHVibGlzaGVzIE1hcmtvdiBzdGF0ZXMsIHJpc2sgc2NvcmVzIGFuZCBtaXRpZ2F0aW9uIHJlY29tbWVuZGF0aW9ucyIgIlJFU1QgQVBJIC8gTVFUVCIKICB9CgogIHZpZXdzIHsKICAgIGNvbnRhaW5lciBydHNhICJzdHJlYW1pbmctcGxhdGZvcm0iIHsKICAgICAgdGl0bGUgIlN0cmVhbWluZyBQbGF0Zm9ybSAtIENvbnRhaW5lciBWaWV3IgogICAgICBpbmNsdWRlICoKICAgICAgYXV0b2xheW91dCBscgogICAgfQoKICAgIHN0eWxlcyB7CiAgICAgIGVsZW1lbnQgIlBlcnNvbiIgewogICAgICAgIHNoYXBlIFBlcnNvbgogICAgICAgIGJhY2tncm91bmQgIzBmNzY2ZQogICAgICAgIGNvbG9yICNmZmZmZmYKICAgICAgfQoKICAgICAgZWxlbWVudCAiU29mdHdhcmUgU3lzdGVtIiB7CiAgICAgICAgYmFja2dyb3VuZCAjMTE2OGJkCiAgICAgICAgY29sb3IgI2ZmZmZmZgogICAgICB9CgogICAgICBlbGVtZW50ICJDb250YWluZXIiIHsKICAgICAgICBiYWNrZ3JvdW5kICM0MzhkZDUKICAgICAgICBjb2xvciAjZmZmZmZmCiAgICAgIH0KCiAgICAgIHJlbGF0aW9uc2hpcCAiUmVsYXRpb25zaGlwIiB7CiAgICAgICAgcm91dGluZyBjdXJ2ZWQKICAgICAgfQogICAgfQogIH0KfQ=="},"views":{"configuration":{"branding":{},"lastSavedView":"streaming-platform","metadataSymbols":"SquareBrackets","styles":{"elements":[{"background":"#0f766e","color":"#ffffff","shape":"Person","tag":"Person"},{"background":"#1168bd","color":"#ffffff","tag":"Software System"},{"background":"#438dd5","color":"#ffffff","tag":"Container"}],"relationships":[{"routing":"Curved","tag":"Relationship"}]},"terminology":{}},"containerViews":[{"automaticLayout":{"applied":true,"edgeSeparation":0,"implementation":"Graphviz","nodeSeparation":300,"rankDirection":"LeftRight","rankSeparation":300,"vertices":false},"dimensions":{"height":3817,"width":6120},"elements":[{"id":"1","x":3975,"y":165},{"id":"2","x":3950,"y":3282},{"id":"3","x":200,"y":2082},{"id":"4","x":200,"y":2682},{"id":"6","x":4700,"y":1849},{"id":"7","x":5450,"y":1849},{"id":"8","x":1700,"y":2382},{"id":"9","x":950,"y":2082},{"id":"10","x":950,"y":2682},{"id":"11","x":2450,"y":1549},{"id":"12","x":3200,"y":865},{"id":"13","x":3950,"y":865},{"id":"14","x":3950,"y":2374},{"id":"15","x":3950,"y":1774},{"id":"16","x":3200,"y":1774}],"externalSoftwareSystemBoundariesVisible":false,"key":"streaming-platform","order":1,"relationships":[{"id":"17","vertices":[{"x":4400,"y":686}]},{"id":"19","vertices":[{"x":4400,"y":3161}]},{"id":"21"},{"id":"23"},{"id":"24"},{"id":"26"},{"id":"27"},{"id":"28","vertices":[{"x":4400,"y":2824},{"x":3950,"y":2824}]},{"id":"29"},{"id":"30"},{"id":"31"},{"id":"32","vertices":[{"x":3200,"y":1624},{"x":4400,"y":1624}]},{"id":"33"},{"id":"34"},{"id":"35"},{"id":"36"},{"id":"37"}],"softwareSystemId":"5","title":"Streaming Platform - Container View"}]}}