Wiring Configuration

The YAML format that declares topology — nodes, connections, and everything a connection carries.

The wiring configuration is the YAML file that describes the topology of an Itara deployment: which nodes exist, and how they’re connected to one another (or to the outside world).

This document describes the file format itself — the schema, the required and optional fields, and what makes a document valid.

Top-level structure

nodes:
  - id: "gatewayNode"
    component: "gateway"
  - id: "calculatorNode"
    component: "calculator"
 
connections:
  - id: "gateway-to-calculator"
    caller:
      nodeId: "gatewayNode"
    callee:
      nodeId: "calculatorNode"
    transport:
      id: http
      params:
        host: "${CALC_HOST:-localhost}"
        port: "${CALC_PORT:-8081}"
    serializer:
      id: json
FieldTypeRequiredDescription
nodeslist of NodeNo (defaults to empty)Every node in the topology.
connectionslist of ConnectionNo (defaults to empty)Every connection between nodes.

An empty or comment-only file is valid and parses to an empty configuration (no nodes, no connections).

Environment variable substitution

Before the YAML is parsed, the raw file content goes through a textual substitution pass. Any occurrence of:

  • ${VAR_NAME} — replaced with the value of environment variable VAR_NAME.
  • ${VAR_NAME:-default} — replaced with the value of VAR_NAME if set, otherwise the literal default. is substituted in-place, before the YAML parser ever sees the file. This means a substituted value is always plain text at parse time — e.g. an env var substituted into a port: field arrives as a numeric string and the YAML parser coerces it the same way a literal 8081 would be.

A variable with no default that isn’t set in the environment is left as the literal placeholder text (${VAR_NAME}) in the document.

Substitution applies anywhere in the file — it’s a plain text pass over the whole document, not scoped to particular fields.

YAML merge keys and anchors

Standard YAML anchors/aliases and merge keys (<<) are supported and resolved before the document is mapped onto the config model — so shared blocks (e.g. a common transport.params map used by several connections) can be factored out with a YAML anchor and merged in with <<.

Nodes

A node is either a component node or a virtual node. The two share an id and are distinguished by the kind field.

nodes:
  - id: "orderServiceNode"
    component: "order-service"        # component node (kind omitted → defaults to "component")
 
  - id: "inventoryNode"
    kind: component                   # component node (kind explicit)
    component: "inventory"
 
  - id: "orderPlacedChannel"
    kind: virtual
    contract: "order-events/order-placed"
    address: "demo.events.order-placed"

Common fields

FieldTypeRequiredDescription
idstringYesThis node’s own identifier. Must be unique, non-blank and match the id character set. Used elsewhere in the file (connections[].caller.nodeId / connections[].callee.nodeId) to refer to this node.
kindstring: component | virtualNo — defaults to componentDiscriminates which node type this is. Case-insensitive (Component, VIRTUAL, etc. are all accepted and normalized). A config without a kind field is a config full of component nodes.

Component nodes (kind: component)

A component node is a deployable component instance — something with an activator and a contract implementation.

FieldTypeRequiredDescription
componentstringYesThe component id. Must match the artifact.id of a kind = "component" metadata file discoverable by the agent.

Virtual nodes (kind: virtual)

A virtual node represents a communication channel with no component implementation of its own — a named point that decouples producers from consumers via a broker (e.g. a Kafka topic). It has no activator and no agent-managed lifecycle.

FieldTypeRequiredDescription
contractstringYesThe event contract this channel carries, in <events-artifact-id>/<contract-id> form, e.g. "order-events/order-placed".
addressstringYesThe broker-specific channel address, e.g. a Kafka topic name. Opaque to the wiring config itself — meaning is entirely up to the transport/broker in use.

Id character set

Both node ids and connection ids are restricted to:

[A-Za-z0-9._-]+

i.e. letters, digits, ., _, and -. This set is deliberately conservative — it’s Kafka’s own topic-name character set, and it stays safe unencoded in HTTP header values and URL path segments, both of which a transport implementation is free to use to propagate an id.

Connections

A connection declares how one node calls another — or how an external caller reaches into the topology.

connections:
  - id: "gateway-to-calculator"
    caller:
      nodeId: "gatewayNode"
      failureSemantics:
        id: built-in
        timeout: 2s
        handleTimeout: true
        absoluteTimeout: 10s
        maxRetry: 3
        params:
          waitDuration: 500ms
    callee:
      nodeId: "calculatorNode"
      authorization:
        id: rule-table
        params:
          allow: "shout"
          deny: "whisper"
    transport:
      id: http
      handleTimeout: true
      params:
        host: "${CALC_HOST:-localhost}"
        port: "${CALC_PORT:-8081}"
    serializer:
      id: json
      params:
        schemaRegistryUrl: "${SCHEMA_REGISTRY_URL:-http://localhost:8081}"
    authentication:
      id: shared-secret
      params:
        secret: "${GATEWAY_SECRET}"

Top-level connection fields

FieldTypeRequiredDescription
idstringYesThis connection’s own identifier — distinct from the node references inside caller/callee. Must be unique across the entire wiring configuration. Same character set as node ids.
calleeCallee blockYesThe called node (callee.nodeId), and optionally callee-side plugin configuration.
callerCaller blockNoThe calling node (caller.nodeId), and optionally caller-side plugin configuration. Absent means the caller is external to the Itara topology.
transportTransportEntryYes, resolvable at connection-level or per-sideThe transport this connection uses.
serializerSerializerEntryConditionally — see belowThe serializer this connection uses. Required for every connection except direct ones; resolvable at connection-level or per-side.
authenticationAuthenticationEntryNoAuthentication mechanism. Valid at connection-level, caller-side, or callee-side. Absent means noop.

A connection with no caller block is an external connection — it represents traffic entering the topology from outside (e.g. a public HTTP endpoint), rather than a call between two nodes both managed by Itara.

A connection whose transport.id is (case-insensitively) "direct" is a direct connection — an in-process, colocated call. Direct connections never cross a process boundary, so nothing on them is ever serialized, and they don’t require a serializer block. A direct connection cannot be external — an in-process call has no meaning without a caller also managed by Itara.

Caller and callee blocks

callee:
  nodeId: "calculatorNode"
  authorization:
    id: rule-table
    params: { allow: "shout", deny: "whisper" }

caller:
  nodeId: "gatewayNode"
  failureSemantics:
    id: built-in
    maxRetry: 3

Each block may additionally hold transport, serializer, and authentication configuration, independent of the connection level. failureSemantics is valid on caller only; authorization is valid on callee only.

FieldTypeRequiredDescription
nodeIdstringYes within its blockThe node this side refers to.

Override happens per plugin kind, not per side as a whole — a caller block overriding just transport while serializer still falls back to connection-level is valid. Where a side-specific declaration for a given plugin kind is present, it replaces the connection-level declaration of that kind for that side entirely — block-level, not field-level; nothing merges.

Transport block

Valid at connection-level, caller-side, or callee-side (see Caller and callee blocks).

transport:
  id: http
  handleTimeout: true
  params:
    host: "${CALC_HOST:-localhost}"
    port: "8081"
FieldTypeRequiredDescription
idstringYesThe transport type identifier. Must match the artifact.id of a kind = "transport" metadata file discoverable by the agent — except for the special value direct, which selects the built-in in-process transport and requires no corresponding artifact.
handleTimeoutbooleanNo — defaults to falseWhether the transport should enforce the per-attempt timeout natively for this connection. Only meaningful if the transport’s own metadata declares the matching capability; the actual timeout value lives in failureSemantics.timeout, not here.
paramsmap of string → scalarNo — defaults to {}Transport-specific connection parameters, passed through to the transport implementation as-is. The wiring config has no schema for this map and no knowledge of what any particular transport expects. Values must be scalars (string, number, or boolean); nested maps or lists are not valid here.

Serializer block

Valid at connection-level, caller-side, or callee-side.

serializer:
  id: json
  params:
    schemaRegistryUrl: "${SCHEMA_REGISTRY_URL:-http://localhost:8081}"
FieldTypeRequiredDescription
idstringYes (see connection-level rule)The serializer id. Must match the artifact.id of a kind = "serializer" metadata file discoverable by the agent.
paramsmap of string → scalarNo — defaults to {}Serializer-specific parameters, passed through as-is. Same shape and same “no schema” property as transport.params.

The block (and its id in particular) is required on every connection except direct ones — there is no serializer choice that’s safe to assume silently for a connection that crosses a process boundary.

failureSemantics block

Valid on the caller block only — retry/timeout is exclusively the caller’s concern.

failureSemantics:
  id: built-in
  timeout: 2s
  handleTimeout: true
  absoluteTimeout: 10s
  maxRetry: 3
  params:
    waitDuration: 500ms
FieldTypeRequiredDescription
idstringNo — defaults to noopThe failure-semantics implementation id. Must match the artifact.id of a kind = "failure-semantics" metadata file, unless left at the noop default.
timeoutduration string, e.g. "2s", "500ms"NoPer-attempt timeout.
handleTimeoutbooleanNo — defaults to falseWhether the failure-semantics implementation should enforce the per-attempt timeout by externally interrupting the transport thread. Only meaningful if the implementation’s own metadata declares support for it.
absoluteTimeoutduration string, e.g. "10s"NoHard ceiling on total execution time across all attempts, including retries.
maxRetryintegerNoMaximum number of retries. Total attempts made = maxRetry + 1.
paramsmap of string → scalarNo — defaults to {}Implementation-specific parameters (e.g. backoff configuration), passed through as-is.

The block as a whole is optional; omitting it entirely means the noop implementation is used — no retries, no timeout enforcement, no circuit breaking.

authentication block

Valid at connection-level, caller-side, or callee-side.

authentication:
  id: shared-secret
  params:
    secret: "${GATEWAY_SECRET}"
FieldTypeRequiredDescription
idstringNo — defaults to noopThe authentication mechanism id. Must match the artifact.id of a kind = "authentication" metadata file, unless left at the noop default. If the block is present, id may not be blank — omit the block entirely to get the noop default instead of supplying an empty id.
paramsmap of string → scalarNo — defaults to {}Implementation-specific parameters, passed through as-is.

authorization block

Valid on the callee block only — authorization is exclusively the callee’s own access-control decision.

authorization:
  id: rule-table
  params:
    allow: "shout"
    deny: "whisper"
FieldTypeRequiredDescription
idstringNo — defaults to noopThe authorization mechanism id. Must match the artifact.id of a kind = "authorization" metadata file, unless left at the noop default. Same non-blank rule as authentication when the block is present.
paramsmap of string → scalarNo — defaults to {}Implementation-specific parameters, passed through as-is.

Full example

nodes:
  - id: "order-service-node"
    component: "order-service"
  - id: "pricing-service-node"
    component: "pricing-service"
  - id: "orderPlacedChannel"
    kind: virtual
    contract: "order-events/order-placed"
    address: "demo.events.order-placed"
 
connections:
  # In-process call — order-service calls pricing-service directly.
  - id: "order-to-pricing"
    caller:
      nodeId: "order-service-node"
    callee:
      nodeId: "pricing-service-node"
    transport:
      id: direct
 
  # Inbound HTTP entry point — no 'caller' block, so the caller is external.
  - id: "gateway-to-order"
    callee:
      nodeId: "order-service-node"
    transport:
      id: http
      params:
        host: "${ORDER_HOST:-localhost}"
        port: "${ORDER_PORT:-8080}"
    serializer:
      id: json
    authentication:
      id: shared-secret
      params:
        secret: "${GATEWAY_SECRET}"
 
  # order-service publishes onto a virtual (broker-backed) channel.
  - id: "order-to-order-placed-channel"
    caller:
      nodeId: "order-service-node"
    caller:
      nodeId: "orderPlacedChannel"
    transport:
      id: kafka
      params:
        bootstrapServers: "${KAFKA_BOOTSTRAP:-localhost:9092}"
    serializer:
      id: protobuf