TypeScript implementation of the i3X Beta specification — bridging OPC UA industrial automation to a modern REST API
node-i3x converts OPC UA address spaces into i3X-compliant REST/JSON endpoints. It exposes industrial automation data through a clean HTTP API with:
- Stable element IDs — deterministic, namespace-URI-based identifiers that survive server restarts
- Real-time subscriptions — long-poll and Server-Sent Events (SSE) for live value updates
- Historical data access — query time-series history for any monitored variable
- Two connector modes — connect to a remote OPC UA server over TCP, or embed one in-process with zero network overhead
Built with node-opcua, Fastify, and a hexagonal (ports & adapters) architecture.
Diagram source (mermaid)
graph TB
Client["HTTP Client"]
subgraph node-i3x
REST["@node-i3x/rest-server<br/>Fastify routes — i3X Beta API"]
Core["@node-i3x/core<br/>Domain services · Models · Stable IDs"]
OPC["@node-i3x/opcua-connector<br/>OPC UA client adapter (remote)"]
Pseudo["@node-i3x/pseudo-session-connector<br/>PseudoSession adapter (embedded)"]
App["@node-i3x/app<br/>Composition Root"]
end
OPCUA["OPC UA Server"]
Client <-->|HTTP / JSON| REST
REST --> Core
Core -->|IDataSourcePort| OPC
Core -->|IDataSourcePort| Pseudo
OPC <-->|OPC UA TCP / Binary| OPCUA
Pseudo <-->|In-process| OPCUA
App -.->|wires| REST
App -.->|wires| Core
App -.->|wires| OPC
App -.->|wires| Pseudo
┌─────────────────────────────────────────────────────────┐
│ @node-i3x/app │
│ (Composition Root / Wiring) │
│ │
│ ┌─────────────────────┐ ┌──────────────────────────┐ │
│ │ @node-i3x/rest-server│ │ @node-i3x/opcua-connector│ │
│ │ Fastify routes │ │ node-opcua client │ │
│ │ INBOUND ADAPTER │ │ OUTBOUND ADAPTER │ │
│ └──────────┬──────────┘ └─────────────┬───────────┘ │
│ │ uses ports implements│ ports │
│ ▼ ▼ │
│ ┌─────────────────────────────────────────────────────┐│
│ │ @node-i3x/core ││
│ │ Domain Models · Services · Port Interfaces ││
│ │ ZERO DEPENDENCIES ││
│ └─────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────┘
| Package | Description | Status |
|---|---|---|
@node-i3x/core |
Domain core: models, ports (IDataSourcePort, ILogger), services (ModelService, ValueService, HistoryService, SubscriptionService), stable ID mapper |
✅ Stable |
@node-i3x/opcua-connector |
OPC UA client adapter — implements IDataSourcePort using node-opcua (remote TCP/binary transport) |
✅ Stable |
@node-i3x/pseudo-session-connector |
PseudoSession adapter — implements IDataSourcePort using node-opcua PseudoSession (embedded in-process, zero network) |
✅ Stable |
@node-i3x/rest-server |
Fastify REST routes implementing the i3X Beta API surface | ✅ Stable |
@node-i3x/app |
Composition root — wires all packages together, entry point | ✅ Stable |
@node-i3x/demo-embedded |
Embedded demo with live dashboard — OPC UA server + i3X API wired via PseudoSession | 🧪 Demo |
# Clone the repository
git clone https://github.com/node-opcua/node-i3x.git
cd node-i3x
# Install dependencies
npm install
# Configure environment
cp .env.example .env
# Edit .env — set I3X_OPCUA_ENDPOINT to your OPC UA server
# Start in development mode (remote OPC UA)
npm run dev
# Or run the embedded demo (no external OPC UA server needed)
npm run demo -w packages/demo-embeddedThe server starts on http://127.0.0.1:8000 by default. Verify with:
curl http://127.0.0.1:8000/health
# → { "status": "ok" }All configuration is via environment variables (or .env file):
| Variable | Default | Description |
|---|---|---|
I3X_OPCUA_ENDPOINT |
opc.tcp://localhost:4840 |
OPC UA server endpoint URL |
I3X_OPCUA_SECURITY_MODE |
None |
Security mode (None, Sign, SignAndEncrypt) |
I3X_OPCUA_BROWSE_CONCURRENCY |
16 |
Max concurrent browse operations |
I3X_OPCUA_METADATA_CACHE_TTL_SECONDS |
300 |
Metadata cache TTL (seconds) |
I3X_OPCUA_OPTIMIZED_CLIENT |
auto |
auto = detect @sterfive module, disabled = skip |
I3X_MODEL_REFRESH_INTERVAL_SECONDS |
60 |
Model auto-refresh interval |
I3X_MODEL_PRELOAD_ON_STARTUP |
true |
Preload the OPC UA model on startup |
I3X_FAIL_STARTUP_ON_MODEL_PRELOAD_ERROR |
false |
Exit if model preload fails |
I3X_SUBSCRIPTION_INTERVAL_SECONDS |
5 |
Subscription polling interval |
I3X_PORT |
8000 |
HTTP server port |
I3X_HOST |
127.0.0.1 |
HTTP server bind address |
I3X_LOG_LEVEL |
info |
Log level (debug, info, warn, error) |
I3X_SKIP_OPCUA_CONNECT |
0 |
Skip OPC UA connection (for testing) |
# Run all tests (vitest)
npm test
# Run tests in watch mode
npm run test:watch
# TypeScript type checking
npm run typecheck
# Lint + format check (Biome)
npm run lint
# Auto-fix lint and format issues
npm run lint:fix
# Build all packages (tsup — minified ESM + .d.ts)
npm run build| Tool | Purpose |
|---|---|
| TypeScript | Language (ESM-only) |
| tsup | Build (esbuild, minified ESM + .d.ts) |
| Vitest | Testing |
| Biome | Lint + Format |
| Fastify | HTTP framework |
| node-opcua | OPC UA client SDK |
OPC UA NodeId values are volatile — namespace indices can shuffle across server restarts. node-i3x generates stable, deterministic element IDs by:
- Computing the full namespace-URI browse path for each node (e.g.,
nsu=http://example.org;s=Sensor1/Temperature) - Hashing the path with a deterministic hash function
- Producing a fixed-length hex ID that remains constant regardless of namespace index changes
This ensures that REST clients can bookmark and cache element IDs across server lifecycles.
The codebase follows a strict hexagonal / ports & adapters pattern:
@node-i3x/coredefines domain models and port interfaces (IDataSourcePort,ILogger) with zero external dependencies- Outbound adapters (
opcua-connector,pseudo-session-connector) implementIDataSourcePort - Inbound adapter (
rest-server) translates HTTP requests into domain service calls @node-i3x/appis the composition root that wires adapters to ports
| Mode | Package | Transport | Use Case |
|---|---|---|---|
| Remote | @node-i3x/opcua-connector |
OPC UA TCP/Binary | Connect to any standard OPC UA server on the network |
| Embedded | @node-i3x/pseudo-session-connector |
In-process (zero network) | Run an OPC UA server and the i3X API in the same process — ideal for demos, testing, and edge deployments |
The i3X Beta REST API surface:
| Method | Endpoint | Description |
|---|---|---|
GET |
/health |
Health check |
GET |
/ready |
Readiness check (data source connected?) |
GET |
/v1/info |
Server info, spec version, capabilities |
| Method | Endpoint | Description |
|---|---|---|
GET |
/v1/namespaces |
List all OPC UA namespaces |
| Method | Endpoint | Description |
|---|---|---|
GET |
/v1/objects |
List all objects (optional: ?root=true, ?typeElementId=...) |
POST |
/v1/objects/list |
Bulk-fetch objects by element IDs |
POST |
/v1/objects/value |
Bulk-read current values |
POST |
/v1/objects/related |
Bulk-fetch related objects (children / parent) |
POST |
/v1/objects/history |
Bulk-read historical values |
PUT |
/v1/objects/:elementId/value |
Write a value to a single element |
| Method | Endpoint | Description |
|---|---|---|
GET |
/v1/objecttypes |
List all object types (optional: ?namespaceUri=...) |
| Method | Endpoint | Description |
|---|---|---|
GET |
/v1/relationshiptypes |
List relationship types (stub — 501) |
| Method | Endpoint | Description |
|---|---|---|
POST |
/v1/subscriptions |
Create a new subscription |
POST |
/v1/subscriptions/register |
Register element IDs for monitoring |
POST |
/v1/subscriptions/unregister |
Unregister element IDs |
POST |
/v1/subscriptions/sync |
Poll for value updates (long-poll) |
POST |
/v1/subscriptions/stream |
Stream value updates via SSE |
POST |
/v1/subscriptions/list |
List subscription details |
POST |
/v1/subscriptions/delete |
Delete subscriptions |
📘 Full OpenAPI specification:
openapi.json
docker build -t node-i3x .
docker run -p 8000:8000 \
-e I3X_OPCUA_ENDPOINT=opc.tcp://host.docker.internal:4840 \
node-i3xFor production deployments, install the optional @sterfive/opcua-optimized-client module for up to 200% performance improvement:
- Intelligent transaction batching
- Automatic server-limit handling
- Advanced auto-healing connection logic
The module is detected automatically at startup — no code changes needed. It is listed as an optionalDependency in @node-i3x/opcua-connector.
Contact Sterfive Support for access.
- Official site: https://i3x.dev
- GitHub: https://github.com/cesmii/i3X
- CESMII: https://cesmii.org
This project is dual-licensed:
| License | Use Case |
|---|---|
| AGPL-3.0-or-later | Open-source use — you must share your source code if you deploy this as a network service |
| Commercial | Proprietary / closed-source use — no copyleft obligations |
The AGPL-3.0 restrictions can be lifted by acquiring a commercial license at sterfive.com.
Contact contact@sterfive.com for pricing and terms.
See LICENSE for full details.
Built by Sterfive — Industrial-grade OPC UA solutions for the modern web.