# autonomy-logic/openplc-runtime architecture

> Architecture diagram of the GitHub repository autonomy-logic/openplc-runtime, generated by GitDiagram from its file tree, README and a sample of source files (last updated 2026-06-14). It is an AI-made overview: check details against the source.

- Interactive diagram: https://gitdiagram.com/autonomy-logic/openplc-runtime
- Repository: https://github.com/autonomy-logic/openplc-runtime

## Overview

1. This repository is a headless PLC runtime, not a user-facing app. Its architecture is centered on two cooperating processes: a Python web server that exposes the control plane, and a C/C++ PLC runtime that executes IEC 61131-3 logic with real-time scheduling.

2. The top-level boundary is between the external controller and the runtime. The main external client is the OpenPLC Editor v4, which uploads programs, polls compilation state, starts/stops execution, and connects to debug streams. There is also support for cloud-controlled operation via Autonomy Edge Cloud, but the same runtime-facing APIs are used.

3. The Python subsystem in `webserver/` is the orchestration layer. It hosts the HTTPS REST API, JWT authentication, user management, file upload validation, runtime status endpoints, and the Socket.IO/WebSocket debug interface. It also manages startup/shutdown of the PLC runtime process and bridges editor requests into runtime commands.

4. The C/C++ subsystem in `core/src/plc_app/` is the execution engine. It owns PLC state, scan-cycle timing, watchdog/logging utilities, socket communication, and dynamic loading of compiled PLC programs. This is the part that must stay deterministic and is the only layer that needs real-time priority.

5. Communication between the two main processes happens over Unix domain sockets under `/run/runtime/`. That IPC boundary is important: the web server does not execute PLC code directly; it sends control commands and receives logs/status through the socket while the runtime remains isolated as a separate process.

6. Program deployment follows a compile-and-load pipeline. The editor uploads a ZIP package to the web server, the server validates and extracts it, and the build flow compiles it into a shared library. The runtime then loads that shared library dynamically, so PLC logic is treated as a deployable binary artifact rather than source code executed in place.

7. The scan-cycle and I/O boundary lives inside the runtime plus the driver layer. `core/src/drivers/` provides the plugin system for hardware interaction, with plugin configuration, driver abstractions, and plugin-specific implementations. This is where external fieldbus or device communication is integrated into the PLC loop.

8. The driver subsystem is split by implementation language. Native plugins include EtherCAT and S7comm, while Python plugins cover Modbus master/slave and OPC UA. Shared Python support code in `core/src/drivers/plugins/python/shared/` provides buffer access, config decoding, type conversion, synchronization, and logging so plugins can interact with PLC memory safely and consistently.

9. OPC UA is a significant protocol-specific subsystem within the Python plugins. It has its own address-space, security, subscription, and server modules, plus dedicated tests and config models. In architectural terms, it acts as both a network-facing data server and a bridge into PLC memory/debug data.

10. The runtime has a security and control plane layered on top of the transport. TLS is used on port 8443, authentication is JWT-based, upload handling includes validation against path traversal and archive abuse, and secrets are stored in the runtime data directory. These controls live mainly in the Python web layer, not in the real-time core.

11. Build and packaging are split across tooling layers. CMake builds the C/C++ runtime and native plugin code; Python packaging/venvs support the web server and Python plugins; Dockerfiles and GitHub workflows package cross-platform runtime images and installers. The build outputs include the PLC executable and compiled program libraries.

12. For an architecture diagram, the key nodes are: external editor/cloud client, Python web/API server, Unix-socket IPC, PLC runtime core, dynamic PLC program library, and plugin drivers reaching hardware/network protocols. The main data flow is editor upload -> validation/compile -> shared library load -> real-time scan cycle -> plugin I/O and debug/status back to the editor.

Main entry points: [`webserver/app.py`](https://github.com/autonomy-logic/openplc-runtime/blob/main/webserver/app.py) (Web server), [`core/src/drivers/plugin_driver.c`](https://github.com/autonomy-logic/openplc-runtime/blob/main/core/src/drivers/plugin_driver.c) (Plugin API), [`core/src/plc_app/plc_main.c`](https://github.com/autonomy-logic/openplc-runtime/blob/main/core/src/plc_app/plc_main.c) (PLC app), [`CMakeLists.txt`](https://github.com/autonomy-logic/openplc-runtime/blob/main/CMakeLists.txt) (Build & package)

## Diagram (Mermaid)

```mermaid
flowchart TD

subgraph group_external["External control"]
  node_editor_client(("Editor v4<br/>external client"))
  node_cloud_client(("Cloud Ops<br/>external client"))
end

subgraph group_web["Python control plane"]
  node_web_app["Web server<br/>python service<br/>[app.py]"]
  node_rest_api["REST API<br/>http interface<br/>[restapi.py]"]
  node_auth["Auth & creds<br/>security layer<br/>[credentials.py]"]
  node_runtime_mgr["Runtime manager<br/>orchestrator<br/>[runtimemanager.py]"]
  node_debug_ws["Debug stream<br/>socket interface<br/>[debug_websocket.py]"]
  node_unix_client["Unix client<br/>ipc client<br/>[unixclient.py]"]
end

subgraph group_runtime["PLC runtime"]
  node_plc_main["PLC app<br/>runtime entrypoint<br/>[plc_main.c]"]
  node_state_mgr["State manager<br/>runtime state"]
  node_scan_cycle["Scan cycle<br/>real-time loop"]
  node_ipc_socket["Unix socket<br/>ipc transport<br/>[unix_socket.c]"]
  node_loader["Program loader<br/>binary loader<br/>[python_loader.c]"]
end

subgraph group_drivers["Driver plugins"]
  node_plugin_api["Plugin API<br/>driver framework<br/>[plugin_driver.c]"]
  node_plugin_cfg["Plugin config<br/>driver config<br/>[plugin_config.c]"]
  node_native_io["Native I/O<br/>native plugins<br/>[ethercat_plugin.c]"]
  node_python_io["Python I/O<br/>python plugins<br/>[buffer_accessor.py]"]
  node_opcua["OPC UA<br/>protocol server<br/>[server.py]"]
end

subgraph group_tooling["Build and packaging"]
  node_build["Build & package<br/>tooling<br/>[CMakeLists.txt]"]
end

node_editor_client -->|"upload/control"| node_web_app
node_cloud_client -->|"remote control"| node_web_app
node_web_app -->|"serves"| node_rest_api
node_web_app -->|"secures"| node_auth
node_web_app -->|"supervises"| node_runtime_mgr
node_web_app -->|"streams debug"| node_debug_ws
node_web_app -->|"IPC bridge"| node_unix_client
node_unix_client -->|"connects"| node_ipc_socket
node_ipc_socket -->|"commands"| node_plc_main
node_plc_main -->|"owns"| node_state_mgr
node_state_mgr -->|"schedules"| node_scan_cycle
node_scan_cycle -->|"loads"| node_loader
node_scan_cycle -->|"uses"| node_plugin_api
node_plugin_api -->|"configures"| node_plugin_cfg
node_plugin_api -->|"hosts"| node_native_io
node_plugin_api -->|"hosts"| node_python_io
node_python_io -->|"includes"| node_opcua
node_opcua -->|"memory bridge"| node_plc_main
node_build -.->|"packages"| node_web_app
node_build -.->|"builds"| node_plc_main
node_build -.->|"deploys"| node_loader

click node_web_app "https://github.com/autonomy-logic/openplc-runtime/blob/main/webserver/app.py"
click node_rest_api "https://github.com/autonomy-logic/openplc-runtime/blob/main/webserver/restapi.py"
click node_auth "https://github.com/autonomy-logic/openplc-runtime/blob/main/webserver/credentials.py"
click node_runtime_mgr "https://github.com/autonomy-logic/openplc-runtime/blob/main/webserver/runtimemanager.py"
click node_debug_ws "https://github.com/autonomy-logic/openplc-runtime/blob/main/webserver/debug_websocket.py"
click node_unix_client "https://github.com/autonomy-logic/openplc-runtime/blob/main/webserver/unixclient.py"
click node_plc_main "https://github.com/autonomy-logic/openplc-runtime/blob/main/core/src/plc_app/plc_main.c"
click node_state_mgr "https://github.com/autonomy-logic/openplc-runtime/blob/main/core/src/plc_app/plc_state_manager.cpp"
click node_scan_cycle "https://github.com/autonomy-logic/openplc-runtime/blob/main/core/src/plc_app/scan_cycle_manager.c"
click node_ipc_socket "https://github.com/autonomy-logic/openplc-runtime/blob/main/core/src/plc_app/unix_socket.c"
click node_loader "https://github.com/autonomy-logic/openplc-runtime/blob/main/core/src/plc_app/python_loader.c"
click node_plugin_api "https://github.com/autonomy-logic/openplc-runtime/blob/main/core/src/drivers/plugin_driver.c"
click node_plugin_cfg "https://github.com/autonomy-logic/openplc-runtime/blob/main/core/src/drivers/plugin_config.c"
click node_native_io "https://github.com/autonomy-logic/openplc-runtime/blob/main/core/src/drivers/plugins/native/ethercat/ethercat_plugin.c"
click node_python_io "https://github.com/autonomy-logic/openplc-runtime/blob/main/core/src/drivers/plugins/python/shared/buffer_accessor.py"
click node_opcua "https://github.com/autonomy-logic/openplc-runtime/blob/main/core/src/drivers/plugins/python/opcua/server.py"
click node_build "https://github.com/autonomy-logic/openplc-runtime/blob/main/CMakeLists.txt"

classDef toneNeutral fill:#f8fafc,stroke:#334155,stroke-width:1.5px,color:#0f172a
classDef toneBlue fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px,color:#172554
classDef toneAmber fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#78350f
classDef toneMint fill:#dcfce7,stroke:#16a34a,stroke-width:1.5px,color:#14532d
classDef toneRose fill:#ffe4e6,stroke:#e11d48,stroke-width:1.5px,color:#881337
classDef toneIndigo fill:#e0e7ff,stroke:#4f46e5,stroke-width:1.5px,color:#312e81
classDef toneTeal fill:#ccfbf1,stroke:#0f766e,stroke-width:1.5px,color:#134e4a
class node_editor_client,node_cloud_client toneBlue
class node_web_app,node_rest_api,node_auth,node_runtime_mgr,node_debug_ws,node_unix_client toneAmber
class node_plc_main,node_state_mgr,node_scan_cycle,node_ipc_socket,node_loader toneMint
class node_plugin_api,node_plugin_cfg,node_native_io,node_python_io,node_opcua toneRose
class node_build toneIndigo
```

## Components

### External control

Clients that operate or monitor the runtime

- **Editor v4**: external client · Primary programmer client that uploads projects, starts/stops execution, and consumes debug/status streams
- **Cloud Ops**: external client · Autonomy Edge Cloud control path using the same control-plane APIs

### Python control plane

HTTPS API, auth, uploads, and editor/runtime orchestration

- [Web server](https://github.com/autonomy-logic/openplc-runtime/blob/main/webserver/app.py): python service · Main Python process that exposes the control plane · `webserver/app.py`
- [REST API](https://github.com/autonomy-logic/openplc-runtime/blob/main/webserver/restapi.py): http interface · Endpoints for upload, runtime status, control, and management · `webserver/restapi.py`
- [Auth & creds](https://github.com/autonomy-logic/openplc-runtime/blob/main/webserver/credentials.py): security layer · JWT authentication and credential handling for the control plane · `webserver/credentials.py`
- [Runtime manager](https://github.com/autonomy-logic/openplc-runtime/blob/main/webserver/runtimemanager.py): orchestrator · Starts, stops, and supervises the PLC runtime process · `webserver/runtimemanager.py`
- [Debug stream](https://github.com/autonomy-logic/openplc-runtime/blob/main/webserver/debug_websocket.py): socket interface · Socket.IO/WebSocket channel for debug data and editor feedback · `webserver/debug_websocket.py`
- [Unix client](https://github.com/autonomy-logic/openplc-runtime/blob/main/webserver/unixclient.py): ipc client · Python-side Unix-domain socket bridge into the runtime · `webserver/unixclient.py`

### PLC runtime

Deterministic execution engine and IPC boundary

- [PLC app](https://github.com/autonomy-logic/openplc-runtime/blob/main/core/src/plc_app/plc_main.c): runtime entrypoint · Native runtime process entry and top-level lifecycle · `core/src/plc_app/plc_main.c`
- [State manager](https://github.com/autonomy-logic/openplc-runtime/blob/main/core/src/plc_app/plc_state_manager.cpp): runtime state · Owns PLC execution state and mode transitions · `core/src/plc_app/plc_state_manager.cpp`
- [Scan cycle](https://github.com/autonomy-logic/openplc-runtime/blob/main/core/src/plc_app/scan_cycle_manager.c): real-time loop · Deterministic scan-cycle timing and execution scheduling · `core/src/plc_app/scan_cycle_manager.c`
- [Unix socket](https://github.com/autonomy-logic/openplc-runtime/blob/main/core/src/plc_app/unix_socket.c): ipc transport · Runtime-side Unix-domain socket endpoint under /run/runtime · `core/src/plc_app/unix_socket.c`
- [Program loader](https://github.com/autonomy-logic/openplc-runtime/blob/main/core/src/plc_app/python_loader.c): binary loader · Dynamically loads compiled PLC program libraries · `core/src/plc_app/python_loader.c`

### Driver plugins

Hardware and protocol integration for PLC I/O

- [Plugin API](https://github.com/autonomy-logic/openplc-runtime/blob/main/core/src/drivers/plugin_driver.c): driver framework · Driver abstraction and plugin configuration layer · `core/src/drivers/plugin_driver.c`
- [Plugin config](https://github.com/autonomy-logic/openplc-runtime/blob/main/core/src/drivers/plugin_config.c): driver config · Plugin configuration, types, and validation helpers · `core/src/drivers/plugin_config.c`
- [Native I/O](https://github.com/autonomy-logic/openplc-runtime/blob/main/core/src/drivers/plugins/native/ethercat/ethercat_plugin.c): native plugins · C/C++ hardware and fieldbus drivers such as EtherCAT and S7comm · `core/src/drivers/plugins/native/ethercat/ethercat_plugin.c`
- [Python I/O](https://github.com/autonomy-logic/openplc-runtime/blob/main/core/src/drivers/plugins/python/shared/buffer_accessor.py): python plugins · Python-based drivers for Modbus and OPC UA using shared runtime helpers · `core/src/drivers/plugins/python/shared/buffer_accessor.py`
- [OPC UA](https://github.com/autonomy-logic/openplc-runtime/blob/main/core/src/drivers/plugins/python/opcua/server.py): protocol server · Network-facing OPC UA server, address space, security, and subscription bridge · `core/src/drivers/plugins/python/opcua/server.py`

### Build and packaging

Compilation and deployment tooling

- [Build & package](https://github.com/autonomy-logic/openplc-runtime/blob/main/CMakeLists.txt): tooling · CMake, Python packaging, Docker, and release workflows · `CMakeLists.txt`

## Connections

- Editor v4 → Web server: upload/control · Editor drives deployment and runtime control through the web server
- Cloud Ops → Web server: remote control · Cloud control uses the same external control plane
- Web server → REST API: serves · Application hosts the HTTP API surface
- Web server → Auth & creds: secures · Authentication and credentials protect the control plane
- Web server → Runtime manager: supervises · Web layer starts and stops the runtime process
- Web server → Debug stream: streams debug · Editor debug data and runtime events flow over WebSocket
- Web server → Unix client: IPC bridge · Python control plane sends commands over Unix-domain sockets
- Unix client → Unix socket: connects · Client-side endpoint for runtime IPC
- Unix socket → PLC app: commands · Runtime receives control, status, and log messages
- PLC app → State manager: owns · Main process drives PLC lifecycle state
- State manager → Scan cycle: schedules · State manager governs real-time scan execution
- Scan cycle → Program loader: loads · Scan/runtime layer loads deployed PLC binaries
- Scan cycle → Plugin API: uses · Execution loop delegates I/O to the driver framework
- Plugin API → Plugin config: configures · Plugins are instantiated from config and type metadata
- Plugin API → Native I/O: hosts · Native implementations provide fieldbus and device access
- Plugin API → Python I/O: hosts · Python implementations provide protocol and device access
- Python I/O → OPC UA: includes · OPC UA is the major Python protocol subsystem
- OPC UA → PLC app: memory bridge · OPC UA reads and writes PLC memory/debug data through the runtime bridge
- Build & package → Web server: packages · Tooling builds and packages the Python control plane
- Build & package → PLC app: builds · Tooling compiles the native runtime and plugin artifacts
- Build & package → Program loader: deploys · Build flow produces deployable shared libraries for the runtime
