gRPC API Configuration

gRPC API Configuration

SmartWeb exposes a server-to-server gRPC API (HTTP/2) that lets clients invoke D2000 RPC procedures, subscribe to object value changes, stream archive and EDA data, and receive RPCs that D2000 initiates against the client. Multiple independent gRPC server instances can be started on different ports, each with its own TLS configuration and access filter.

The gRPC API is based on a standard gRPC proto contract; the gRPC service is named D2Api.

Methods of the D2Api service

Method

gRPC cardinality

Purpose

Method

gRPC cardinality

Purpose

rpc

unary

Invoke a single D2000 RPC procedure and receive the result

rpcConversation

bidirectional streaming

Multi-step RPC conversation between client and D2000

rpcSBA

unary

Invoke a Simple Byte Array (binary) RPC

subscribeObject

server streaming

Real-time value-change subscription for a D2000 object

loadArchive

server streaming

Read historical archive rows for a time range

loadEdaVector

server streaming

Read EDA vector values

updateEdaVector

unary

Write timestamped values to an EDA vector

subscribeToRpc

server streaming

Receive RPCs that D2000 initiates against the client

In addition, every gRPC server instance also registers:

  • grpc.reflection.v1alpha.ServerReflection — enables introspection via grpc_cli / Postman

  • grpc.health.v1.Health — standard gRPC health protocol (see gRPC health checks)

Configuration

The gRPC API is configured under smartweb.application.grpcApi.configItems[]. Each entry starts an independent gRPC server with its own bind address, port, TLS settings, response headers and access filter.

smartweb: application: grpcApi: configItems: - enabled: true host: "0.0.0.0" # bind address; "0.0.0.0" listens on all interfaces port: 9090 socketTimeoutMs: 60000 # keep-alive time on the connection httpHeaders: # static response metadata injected into every reply - "X-Server: SmartWeb-gRPC" - "X-Api-Version: 1.0" ssl: enabled: true keyStore: path: cert/keystore.p12 password: changeit keyAlias: smartweb trustStore: path: cert/truststore.p12 password: changeit clientAuth: REQUIRE # NONE | OPTIONAL | REQUIRE accessFilter: allowedD2RpcEventNames: - "E.MyRpc" allowedD2RpcMethodNames: - "Calculate" - "GetStatus" allowedD2EdaVectorReadCodes: - "*" allowedD2EdaVectorUpdateCodes: - "*"

Property

Description

Default

Property

Description

Default

enabled

Enables or disables this gRPC server instance

true

host

Bind address

0.0.0.0

port

Listening port

9090

socketTimeoutMs

keep-alive time, in milliseconds

httpHeaders

Static metadata headers injected into every response, format "Key: Value"

none

ssl.enabled

Enable TLS for this instance

false

ssl.keyStore.path

PKCS12 keystore path (absolute or relative to the config directory)

ssl.keyStore.password

Keystore password

changeit

ssl.keyAlias

Key alias (the first alias in the keystore is used if omitted)

ssl.trustStore.path

Truststore path (required for validated mTLS)

ssl.trustStore.password

Truststore password

changeit

ssl.clientAuth

NONE (no client cert), OPTIONAL (cert is accepted but not required), REQUIRE (full mTLS)

NONE

accessFilter.allowedD2RpcEventNames

Wildcard list of allowed D2000 event names

["*"]

accessFilter.allowedD2RpcMethodNames

Wildcard list of allowed RPC method names

["*"]

accessFilter.allowedD2EdaVectorReadCodes

Wildcard list of EDA vector codes that may be read

["*"]

accessFilter.allowedD2EdaVectorUpdateCodes

Wildcard list of EDA vector codes that may be written

["*"]

[!NOTE] Each configItems entry produces an independent gRPC server. You can run, for example, a strict mTLS instance on port 9090 for external partners, an internal TLS-only instance on port 9091, and a plaintext instance on port 9092 bound to a private interface — all in the same SmartWeb process.

Access filter

The access filter is applied to every individual call, including every message inside a bidirectional conversation. Wildcard matching is case-insensitive:

Pattern

Matches

Pattern

Matches

*

Any value

E.TRAY_*

Any event name starting with E.TRAY_

Calculate

The exact method name Calculate

A denied call returns Status.PERMISSION_DENIED and terminates the stream.

Example — restrict access to a three-step conversation flow (Register("User_01")Calculate(...)CloseE()):

accessFilter: allowedD2RpcEventNames: - "E.TRAY_testSender1" allowedD2RpcMethodNames: - "Register" - "Calculate" - "CloseE"

Response metadata headers

The httpHeaders list is injected into the response metadata of every gRPC call.

httpHeaders: - "X-Server: SmartWeb-gRPC" - "X-Api-Version: 1.0" - "Access-Control-Allow-Origin: *"

[!NOTE] gRPC metadata keys are normalised to lowercase internally.

TLS / mTLS

Each gRPC server instance has its own TLS configuration. When clientAuth is OPTIONAL or REQUIRE, the peer TLS certificate chain is made available to the application's authentication layer.

Self-signed development certificate

keytool -genkeypair -alias smartweb -keyalg RSA -keysize 2048 \ -storetype PKCS12 -keystore keystore.p12 -validity 365 \ -storepass changeit \ -dname "CN=localhost, OU=d2000, O=Ipesoft, L=Zilina, ST=SK, C=SK" \ -ext "SAN=dns:localhost,ip:127.0.0.1"

Full mTLS setup (server + CA + client)

# Server keystore keytool -genkeypair -alias smartweb -keyalg RSA -keysize 2048 -storetype PKCS12 \ -keystore keystore.p12 -validity 365 -storepass changeit \ -dname "CN=localhost, OU=d2000, O=Ipesoft, L=Zilina, ST=SK, C=SK" \ -ext "SAN=dns:localhost,ip:127.0.0.1" # CA keystore keytool -genkeypair -alias ca -keyalg RSA -keysize 2048 -storetype PKCS12 \ -keystore ca-keystore.p12 -validity 3650 -storepass changeit \ -dname "CN=MyCA, OU=d2000, O=Ipesoft, L=Zilina, ST=SK, C=SK" \ -ext "BasicConstraints:critical=ca:true" # Export CA certificate keytool -exportcert -alias ca -keystore ca-keystore.p12 -storepass changeit -rfc -file ca.crt # Client keystore keytool -genkeypair -alias smartweb-client -keyalg RSA -keysize 2048 -storetype PKCS12 \ -keystore client-keystore.p12 -validity 365 -storepass changeit \ -dname "CN=smartweb-client, OU=d2000, O=Ipesoft, L=Zilina, ST=SK, C=SK" # Client CSR keytool -certreq -alias smartweb-client -keystore client-keystore.p12 -storepass changeit -file client.csr # CA signs the client CSR keytool -gencert -alias ca -keystore ca-keystore.p12 -storepass changeit \ -infile client.csr -outfile client-signed.crt -validity 365 -rfc \ -ext "KeyUsage=digitalSignature" -ext "ExtendedKeyUsage=clientAuth" # Import CA + signed client cert into the client keystore (trust chain) keytool -importcert -alias ca -file ca.crt -keystore client-keystore.p12 -storepass changeit -noprompt keytool -importcert -alias smartweb-client -file client-signed.crt \ -keystore client-keystore.p12 -storepass changeit -noprompt # Server truststore — used to validate client certificates keytool -importcert -alias ca -file ca.crt -keystore truststore.p12 \ -storetype PKCS12 -storepass changeit -noprompt

File

Purpose

Needed at runtime

File

Purpose

Needed at runtime

keystore.p12

Server private key + server certificate

Server

ca-keystore.p12

CA private key + CA certificate (used only for signing)

Offline

ca.crt

CA public certificate

Used to build the truststore only

client-keystore.p12

Client private key + CA-signed certificate

Client

truststore.p12

Server truststore containing the CA certificate

Server (mTLS only)

[!WARNING] When clientAuth is OPTIONAL or REQUIRE but no truststore is configured, the client certificate is not validated against any CA chain — it is only made available to the application layer for inspection. Do not use this configuration in production.

gRPC health checks

Each gRPC server instance implements the standard grpc.health.v1.Health service. The health status is evaluated dynamically on every Check call.

Service name

Status source

Service name

Status source

"" (empty)

Overall — SERVING only when D2 API, EDA (if configured) and all connectors are healthy

"d2api"

D2000 connection

"eda"

EDA connection (returns SERVING if EDA is not configured)

"connector_<name>"

Named D2 service connector

Unknown service names return Status.NOT_FOUND.

Example check via grpc_cli:

grpc_cli call localhost:9090 grpc.health.v1.Health/Check "service: 'd2api'" grpc_cli call localhost:9090 grpc.health.v1.Health/Check "service: ''"

Example configurations

Multi-instance: strict mTLS, internal TLS, and plaintext

server: port: 8443 smartweb: connections: - host: localhost port: 3120 authentication: authModes: - AUTH_CREDENTIALS_IN_SESSION - AUTH_CERTIFICATE_REMOTELY apiKeys: enabled: true application: serverSsl: enabled: true keyStore: path: cert/keystore.p12 password: changeit keyAlias: smartweb trustStore: path: cert/truststore.p12 password: changeit clientAuth: REQUIRE grpcApi: configItems: # Instance 1 — mTLS on port 9090, all events and methods allowed - enabled: true host: "0.0.0.0" port: 9090 socketTimeoutMs: 60000 httpHeaders: - "X-Server: SmartWeb-gRPC" - "X-Api-Version: 1.0" ssl: enabled: true keyStore: path: cert/keystore.p12 password: changeit keyAlias: smartweb trustStore: path: cert/truststore.p12 password: changeit clientAuth: REQUIRE accessFilter: allowedD2RpcEventNames: ["*"] allowedD2RpcMethodNames: ["*"] # Instance 2 — one-way TLS on port 9091, restricted to E.TRAY_* events - enabled: true host: "0.0.0.0" port: 9091 socketTimeoutMs: 60000 ssl: enabled: true keyStore: path: cert/keystore.p12 password: changeit keyAlias: smartweb clientAuth: OPTIONAL accessFilter: allowedD2RpcEventNames: - "E.TRAY_*" allowedD2RpcMethodNames: - "*" # Instance 3 — plaintext, internal interface only, default filter (all allowed) - enabled: true host: "127.0.0.1" port: 9092 ssl: enabled: false

Tightly scoped filter — single conversation flow

smartweb: application: grpcApi: configItems: - enabled: true host: "0.0.0.0" port: 9090 ssl: enabled: true keyStore: path: cert/keystore.p12 password: changeit clientAuth: REQUIRE accessFilter: allowedD2RpcEventNames: - "E.TRAY_testSender1" - "E.RPC_TEST_FUNCTIONS" allowedD2RpcMethodNames: - "Register*" - "Calculate" - "CloseE" - "OutputParams"

Diagnostic logging for the gRPC layer

logging: level: io.grpc.netty: WARN io.grpc.services: WARN com.ipesoft.smartweb.core.grpc: DEBUG

Java client example

The following example calls the OutputParams RPC procedure, defined in D2000 in an ESL script. The procedure has six output (OUT) parameters, including a structured parameter of type SD.TestSmart:

RPC PROCEDURE OutputParams(BOOL _bool, INT _int, REAL _real, TIME _time, TEXT _text, RECORD NOALIAS (SD.TestSmart) _struct) _bool := @TRUE _int := 63 _real := 3.14 _time := SysTime _text := %GenD2UID() REDIM _struct[1] _struct[1]^Bool := @TRUE _struct[1]^Int := 2 _struct[1]^Real := 2.324 _struct[1]^ATime := SysTime - 60 * 60 _struct[1]^RTime := 1024 _struct[1]^Text := "TEST" END OutputParams

The SD.TestSmart structure has six columns: Bool (BOOL), Int (INT), Real (REAL), ATime (absolute time), RTime (relative time) and Text (TEXT).

Since all of the procedure's parameters are output parameters, the client sends a placeholder UnivalValue for each one, with the type (type) set and a name (returnAs) under which the output value will appear in the RpcResponse.values map:

ManagedChannel channel = ManagedChannelBuilder .forAddress("smartweb.example.com", 9090) .useTransportSecurity() .build(); D2ApiGrpc.D2ApiBlockingStub stub = D2ApiGrpc.newBlockingStub(channel) .withCallCredentials(new ApiKeyCredentials("my-api-key")); RpcResponse response = stub.rpc(RpcCall.newBuilder() .setEventName("E.RPC_TEST_FUNCTIONS") .setName("OutputParams") .addParameter(UnivalValue.newBuilder().setType(UnivalType.Bool).setReturnAs("bool")) .addParameter(UnivalValue.newBuilder().setType(UnivalType.Int).setReturnAs("int")) .addParameter(UnivalValue.newBuilder().setType(UnivalType.Real).setReturnAs("real")) .addParameter(UnivalValue.newBuilder().setType(UnivalType.Time).setReturnAs("time")) .addParameter(UnivalValue.newBuilder().setType(UnivalType.Text).setReturnAs("text")) .addParameter(UnivalValue.newBuilder().setType(UnivalType.Record).setReturnAs("struct")) .build()); // Scalar output parameters — from the RpcResponse.values map, keyed by the returnAs names Map<String, UnivalValue> out = response.getValuesMap(); BoolValue bool = out.get("bool").getBoolValue(); // False / True / Oscilate long i = out.get("int").getIntValue(); // 63 double real = out.get("real").getRealValue(); // 3.14 Timestamp time = out.get("time").getTime(); String text = out.get("text").getText(); // Structured (RECORD) output parameter of type SD.TestSmart RecordValue struct = out.get("struct").getRecordValue(); RecordRow row = struct.getRow(0); // first row (REDIM _struct[1]) // the column order matches the SD.TestSmart structure definition: BoolValue colBool = row.getValue(0).getBoolValue(); // Bool long colInt = row.getValue(1).getIntValue(); // Int double colReal = row.getValue(2).getRealValue(); // Real Timestamp colATime = row.getValue(3).getTime(); // ATime — absolute time Duration colRTime = row.getValue(4).getTimespan(); // RTime — relative time String colText = row.getValue(5).getText(); // Text