Runbooks / Relay Agent

Relay Agent

The IdentityMesh Relay Agent is a lightweight Windows service that enables connectivity to remote identity systems without inbound firewall rules. It runs as the IdentityMeshRelayAgent Windows service and loads connector plug-ins from a local Connectors/ folder at startup.

Overview

A relay agent is a lightweight Windows service deployed in a remote network (branch office, partner data center, air-gapped environment) that executes import and export operations on behalf of the central sync engine. This eliminates the need to open inbound firewall ports or VPN tunnels to reach remote identity sources.

Key characteristics:

Architecture

Remote Network                          Central Network
┌─────────────────────┐                 ┌──────────────────────────────────────┐
│                     │                 │                                      │
│  Identity Source    │                 │  Admin API                           │
│  (AD, SQL, etc.)    │                 │  ┌──────────────────────────────┐    │
│        ▲            │                 │  │  /hubs/relay (SignalR Hub)   │    │
│        │            │                 │  │  RelayHub                    │    │
│        │ LDAP/SQL   │                 │  └────────────▲─────────────────┘    │
│        │            │                 │               │                      │
│  ┌─────┴──────────┐ │  outbound       │               │                      │
│  │ Relay Agent    │──┼──HTTPS──────────┼───────────────┘                      │
│  │ (Windows Svc)  │ │  SignalR        │                                      │
│  │                │ │                 │  Sync Engine ──► Projection ──► DB   │
│  │ Connectors/    │ │                 │                                      │
│  │  ├ AD.dll      │ │                 └──────────────────────────────────────┘
│  │  └ SQL.dll     │ │
│  └────────────────┘ │
│                     │
└─────────────────────┘

Data Flow

  1. The sync engine (or Admin API) initiates an import/export via the POST /api/relay/import or POST /api/relay/export REST endpoint.
  2. The Admin API sends the operation to the target agent through the RelayHub SignalR hub. Configuration and auth material are included in the message.
  3. The relay agent receives the command, loads the appropriate connector DLL, initializes it with the provided configuration, and executes the operation locally against the identity source.
  4. Import results are streamed back in batches of 100 objects via ImportBatchReady hub calls, then finalized with ImportCompleted.
  5. Connector logs are relayed back through the RelayLog hub method and persisted to the IM_ConnectorLogs table centrally.

SignalR Transport

The relay agent uses the ASP.NET Core SignalR client with automatic reconnect. Reconnect intervals: 0s, 2s, 5s, 10s, 30s. On reconnect the agent re-registers itself with the hub.

Hub URL: /hubs/relay (gated by the RelayAuth policy — accepts the RelayApiKey scheme via X-Agent-Id + X-Agent-ApiKey headers, or the JwtBearer scheme with the relay.connect app role; see the Authentication modes section under Security).

Direct Mode vs Relay Mode

Connectors default to direct mode, where the sync engine loads the connector in-process and connects to the identity source directly.

Relay mode is opt-in per connector. When a connector’s RelayAgentId is set (via the Admin UI or the PUT /api/connectors/{id} endpoint), the sync engine delegates that connector’s import and export operations to the assigned relay agent instead of executing them locally.

AspectDirect ModeRelay Mode
Connector runs inSync engine processRelay agent process
Network accessEngine must reach identity sourceAgent must reach identity source
Firewall requirementsInbound or VPN to identity sourceOutbound HTTPS only from remote network
ConfigurationConfigJson on connectorSame ConfigJson, agent resolves locally
Auth materialResolved locally by engineSent from central DB through SignalR
LogsWritten locally to DBRelayed back through RelayLog hub method

Deployment

Prerequisites

Step-by-Step

  1. Register the agent in the Admin UI under Relay Agents. The UI calls POST /api/relay-agents with the agent name. The API returns a one-time agentId and apiKey — save both immediately.

  2. Install the relay agent on the remote server. Copy the published IdentityMesh.Relay.Agent binaries to a directory such as C:\Program Files\IdentityMesh\RelayAgent\.

  3. Copy connector DLLs into the Connectors/ subfolder under the agent’s install directory. For example:

    C:\Program Files\IdentityMesh\RelayAgent\
      IdentityMesh.Relay.Agent.exe
      appsettings.json
      Connectors\
        IdentityMesh.Connectors.ActiveDirectory.dll
        IdentityMesh.Connectors.Sql.dll

    Only include the connector DLLs needed for identity sources in this network.

  4. Configure appsettings.json:

    {
      "Relay": {
        "HubUrl": "https://identitymesh-api.contoso.com/hubs/relay",
        "AgentId": "a1b2c3d4-...",
        "AgentName": "Branch-Office-West",
        "ApiKey": "the-api-key-from-step-1"
      }
    }
    SettingDescription
    HubUrlFull URL to the RelayHub endpoint on the Admin API
    AgentIdGUID returned when registering the agent
    AgentNameDisplay name (defaults to Environment.MachineName if omitted)
    ApiKeyAPI key returned at registration (sent as X-Agent-ApiKey header)
  5. Register the Windows service:

    sc create IdentityMeshRelayAgent binPath="C:\Program Files\IdentityMesh\RelayAgent\IdentityMesh.Relay.Agent.exe"
  6. Start the service. The agent connects to the Admin API, sends a RegisterAgent message with the agent ID, machine name, version, and list of loaded connector types. The Admin API marks the agent as Online.

  7. Assign connectors to the agent. In the Admin UI, edit a connector and set its Relay Agent dropdown to the registered agent. This sets RelayAgentId on the connector record.

  8. Verify — the Dashboard and Relay Agents page show the agent status as Online with the loaded connector types.

Security

Authentication modes

Relay agents support two authentication modes, selected via Relay:Authentication:Mode in appsettings.json. Both modes are validated on the same SignalR upgrade request and gated by the same RelayAuth policy on /hubs/relay.

ModeWhen to useCarries credential as
ApiKey (default)On-prem deployments without an Entra tenant; air-gapped sitesX-Agent-Id + X-Agent-ApiKey headers
BearerCloud-native deployments; customers already using Entra for M2M authAuthorization: Bearer <jwt> (acquired via MSAL)

The hub’s authorization policy accepts either scheme. ApiKey-mode agents are identified by their X-Agent-Id header (validated against IM_RelayAgents.ApiKeyHash). Bearer-mode agents are identified by the agent_id claim in their token (or, as a fallback, the service principal’s object id).

API Key Authentication (default)

Each relay agent is assigned a unique API key at registration time. The API key is SHA-256 hashed before storage in the database (ApiKeyHash column on IM_RelayAgents). The agent sends the raw key in the X-Agent-ApiKey HTTP header — together with X-Agent-Id — on every SignalR connection.

The Admin API validates both headers on the SignalR upgrade request: the agent’s row is looked up by X-Agent-Id, the presented key is hashed, and the result is compared against ApiKeyHash in constant time. A connection without the headers, with an unknown agent id, or with a wrong key is refused with 401. Subsequent hub method calls re-use the authenticated identity stamped on the connection, so a caller cannot drive updates against another agent’s row even if it knows that agent’s GUID.

The API key is returned only once at creation. If lost, delete the agent and re-register.

Bearer (Entra service principal)

Recommended for cloud-native deployments and customers using Entra for all M2M authentication. The relay acquires an OAuth2 access token via the MSAL ConfidentialClientApplication (client-credentials flow) and SignalR attaches it as Authorization: Bearer ... on every connect attempt. MSAL caches tokens in memory and auto-renews them shortly before expiry, so there is no per-request round-trip to Entra after the first connect.

Setup:

  1. Register an Entra app for the relay (one app registration per relay, or one app for all relays if your governance allows it). If the relay runs on Azure-hosted infrastructure (VM, App Service, Container Apps), prefer a managed identity instead of a client secret.

  2. Define an app role on the IdentityMesh API app registration. In the API’s app registration → App roles, add a role named relay.connect with Allowed member types = Applications. This role is what the API’s RelayAuth policy looks for on JWT-authed callers.

  3. Assign the relay.connect role to the relay’s app/identity. In the IdentityMesh API app registration → Enterprise applications → your relay app → Users and groups → add an assignment with the relay.connect role.

  4. Configure the agent_id claim. In the relay’s app registration → Token configurationAdd optional claim, add a claim named agent_id whose value is the agent’s IM_RelayAgents.AgentId GUID. Without this claim, the hub falls back to using the service principal’s oid as the agent identifier, which only works if you create the IM_RelayAgents row with the SP’s object id as its AgentId.

  5. Configure appsettings.json with the Bearer block:

    {
      "Relay": {
        "HubUrl": "https://identitymesh-api.contoso.com/hubs/relay",
        "AgentId": "a1b2c3d4-...",
        "Authentication": { "Mode": "Bearer" },
        "Bearer": {
          "TenantId": "<entra tenant guid>",
          "ClientId": "<relay app registration client id>",
          "ClientSecret": "<from secret store>",
          "Scope": "api://identitymesh/.default"
        }
      }
    }
    SettingDescription
    Authentication:Mode"Bearer" to enable MSAL; "ApiKey" (default) for header auth
    Bearer:TenantIdEntra tenant GUID hosting the relay’s app registration
    Bearer:ClientIdApplication (client) id of the relay’s app registration
    Bearer:ClientSecretClient secret from the relay’s app registration. For Azure-hosted relays use a managed identity instead and omit this field.
    Bearer:ScopeScope to request. Typically <api-app-id-uri>/.default, e.g. api://identitymesh/.default.
  6. Restart the relay service. The agent will acquire its first token at connect time; rotation is handled automatically by MSAL’s in-memory cache.

In Bearer mode the legacy Relay:ApiKey value is ignored and may be left empty.

TLS

All communication between the agent and the Admin API must use HTTPS (TLS 1.2+). The HubUrl in agent configuration should always use the https:// scheme.

Secret Handling

Connector credentials (passwords, connection strings) are stored centrally in the IdentityMesh database. When the sync engine dispatches an operation to a relay agent, it includes the resolved AuthMaterial in the SignalR message. Secrets are never stored on the relay agent machine.

Heartbeat and Offline Detection

The agent sends a heartbeat every 30 seconds. The RelayAgentMonitor background service on the Admin API checks every 30 seconds for agents whose last heartbeat is older than 2 minutes and marks them Offline.

Agent Management API Endpoints

Relay Agents

MethodPathDescription
GET/api/relay-agentsList all registered relay agents with status
POST/api/relay-agentsRegister a new agent (returns agentId and one-time apiKey)
DELETE/api/relay-agents/{id}Delete agent and unassign its connectors

Relay Operations

MethodPathDescription
POST/api/relay/importDispatch an import operation to a relay agent
POST/api/relay/exportDispatch an export operation to a relay agent
POST/api/relay/cancel/{correlationId}Cancel a running relay operation

RelayHub SignalR Methods

Agent-to-Hub (Agent calls these on the hub)

MethodParametersDescription
RegisterAgentagentId, agentName, machineName, version, connectorTypes[]Register/re-register the agent
HeartbeatagentId30-second keep-alive
ImportBatchReadycorrelationId, batch[]Stream a batch of import results (up to 100)
ImportCompletedcorrelationId, resultSignal import success with summary
ImportFailedcorrelationId, errorSignal import failure
ExportCompletedcorrelationIdSignal export success
ExportFailedcorrelationId, errorSignal export failure
RelayLogcorrelationId, connectorId, runId, level, category, message, externalId?, elapsedMs?, detail?Forward a connector log entry

Hub-to-Agent (Hub pushes these to the agent)

MethodParametersDescription
ExecuteImportcorrelationId, connectorId, connectorType, configJson, authMaterial, importRequestStart an import on the agent
ExecuteExportcorrelationId, connectorId, connectorType, configJson, authMaterial, changes[]Start an export on the agent
CancelOperationcorrelationIdCancel a running operation

Connector Loading

The ConnectorExecutor scans the Connectors/ directory at startup for DLLs matching the IdentityMesh.Connectors.*.dll pattern. Each DLL is loaded into its own AssemblyLoadContext. Types implementing IIdentityConnector are discovered via reflection and registered. This approach avoids hard assembly references and allows the same connector DLLs used by the sync engine to work unmodified in the relay agent.

Troubleshooting