opcua-session-manager · master
Docs · IPC

Commands

Seven commands cover every interaction with the daemon. Three for the IPC layer (ping, list, describe), two for the session lifecycle (open, close), and two for dispatch (query, invoke).

The daemon's CommandHandler dispatches on the command field of every request frame. Seven commands are recognised:

Command Path What
ping IPC Daemon liveness probe
list IPC Enumerate active sessions (redacted config)
describe IPC Surface of the underlying client (methods, modules, wire types)
open Session Create or reuse an OPC UA session
close Session Drop a specific session
query Dispatch Invoke an OpcUaClientInterface method (whitelisted)
invoke Dispatch Invoke any method registered on the daemon's client (typed wire)

This page is the operational walkthrough. For the formal request / response schemas, see Reference · IPC commands.

ping

Cheapest possible call. The daemon answers with its current state.

text request / response
→ {"command":"ping"}
← {"success":true,"data":{"status":"ok","sessions":3,"time":1716000000.123}}

Use it for healthchecks, readiness probes, smoke tests after deploy. The sessions count is the number of active OPC UA sessions; time is the daemon's microtime(true) at response.

list

Enumerate active sessions. Credentials are redacted; everything else useful is returned.

text list
→ {"command":"list","authToken":"..."}
← {"success":true,"data":{
    "count": 2,
    "sessions": [
      {
        "id": "a1b2c3d4...",
        "endpointUrl": "opc.tcp://plc.local:4840",
        "lastUsed": 1716000000.1,
        "config": {
          "securityPolicy": "http://opcfoundation.org/UA/SecurityPolicy#Basic256Sha256",
          "securityMode": 3,
          "autoRetry": 3
        }
      },
      ...
    ]
}}

The config map has the sensitive keys (username, password, clientKeyPath, userKeyPath, caCertPath) stripped before display. See ManagedClient · Session reuse for what the full key actually contains.

describe

Asks the daemon for the API surface of the underlying client attached to a session. Used by ManagedClient::__call() to decide whether to dispatch through query or invoke.

text describe
→ {"command":"describe","sessionId":"a1b2c3...","authToken":"..."}
← {"success":true,"data":{
    "methods":     ["read","write","browse",...,"customMethod"],
    "modules":     ["PhpOpcua\\Client\\Module\\ReadWrite\\ReadWriteModule",...],
    "wireClasses": ["PhpOpcua\\Client\\Types\\NodeId",...],
    "enumClasses": ["PhpOpcua\\Client\\Types\\BuiltinType",...]
}}

ManagedClient caches the describe response for the lifetime of the IPC connection; subsequent hasMethod() / hasModule() / getRegisteredMethods() / getLoadedModules() are answered from the cache without round-trips.

open

Create or reuse a session. The request carries the endpoint URL and the typed SessionConfig as a flat map. Set forceNew: true to bypass session reuse.

text open
→ {
    "command": "open",
    "endpointUrl": "opc.tcp://plc.local:4840",
    "config": {
      "securityPolicy": "http://opcfoundation.org/UA/SecurityPolicy#Basic256Sha256",
      "securityMode":   3,
      "username":       "integrations",
      "password":       "secret",
      "clientCertPath": "/etc/opcua/client.pem",
      "clientKeyPath":  "/etc/opcua/client.key",
      "opcuaTimeout":   10.0,
      "autoRetry":      3
    },
    "authToken": "..."
}
← {"success":true,"data":{"sessionId":"a1b2c3...","reused":false}}

reused tells the caller whether the daemon matched an existing session — this is what powers ManagedClient::wasSessionReused().

The full config schema (which fields are accepted, which are part of the session key, which are redacted) is in Reference · IPC commands.

close

Drop a specific session. The daemon sends CloseSession to the server and frees the session-store entry.

text close
→ {"command":"close","sessionId":"a1b2c3...","authToken":"..."}
← {"success":true,"data":null}

ManagedClient::disconnect() issues this command for you — the daemon-side session is torn down when the client disconnects. Use the raw IPC path (or netcat) only when you need to close a session whose ManagedClient instance is no longer in your process (e.g. cleaning up after a crashed worker).

query

Invoke a built-in OpcUaClientInterface method against a session. Gated by the static CommandHandler::ALLOWED_METHODS list (44 entries).

text query
→ {
    "command": "query",
    "sessionId": "a1b2c3...",
    "method": "read",
    "params": [{"ns":2,"id":"PLC/Speed","type":"string"}, 13, false],
    "authToken": "..."
}
← {"success":true,"data":{
    "value": 42.5,
    "type":  11,
    "dimensions": null,
    "statusCode": 0,
    "sourceTimestamp": "2026-05-15T10:30:00.000000+00:00",
    "serverTimestamp": "2026-05-15T10:30:00.123456+00:00"
}}

The params array shape is method-specific. Parameter decoding goes through BuiltInParamDeserializer — see Type serialization for the wire shapes of common types.

Methods outside the allowlist return forbidden_method. For custom methods, use invoke.

invoke

Generic dispatch. Calls any method registered on the daemon's client, gated by $client->hasMethod($name) rather than a static list.

text invoke
→ {
    "command": "invoke",
    "sessionId": "a1b2c3...",
    "method": "addNodes",
    "args": [
        [{"__t": "AddNodesItemSpec", ...wire-encoded body... }]
    ],
    "authToken": "..."
}
← {"success":true,"data":{"data":[
    {"__t": "AddNodesResult", "statusCode": 0, "addedNodeId": {"__t": "NodeId", "ns": 1, "i": "Counter", "type": "string"}}
]}}

The invoke success response wraps the Wire-encoded result inside a data.data key — the inner data is what WireTypeRegistry::encode() emitted on the daemon side, and the outer data is the standard success envelope wrapper.

invoke carries typed args via the Wire registry — every typed value is wrapped with a __t discriminator. The registry comes from ModuleRegistry::buildWireTypeRegistry() on the daemon side; custom modules contribute their own types via ServiceModule::registerWireTypes(). See opcua-client — wire serialization.

invoke is how ManagedClient::__call() reaches third-party module methods — the path enabling pluggable custom services without patching the command handler. See Extensibility · Third-party modules.

Auth across all commands

Every command, except ping, requires the auth token when the daemon was configured with one. ping accepts it too — and validates if present — but you can use it as an unauthenticated liveness probe.

This matters for healthchecks: a Kubernetes liveness probe does not have access to the auth token; the ping command lets it probe regardless.