Skip to content

Configuration

All configuration lives in appsettings.json (or environment-specific overrides like appsettings.Development.json).

AMS Router

The embedded ADS router provides cross-platform ADS connectivity without requiring a local TwinCAT installation.

{
  "AmsRouter": {
    "Name": "Adsify",
    "NetId": "192.168.1.78.1.1",
    "TcpPort": 48898,
    "LoopbackIP": "127.0.0.1",
    "LoopbackPort": 48898,
    "ChannelPortType": "Loopback",
    "RemoteConnections": [
      {
        "Name": "Line1-PLC",
        "Address": "192.168.10.143",
        "NetId": "5.80.201.1.1.1",
        "Type": "TCP_IP"
      }
    ]
  }
}
FieldDescription
NameName of this ADS router instance
NetIdAMS Net ID of this machine (typically <your-ip>.1.1)
TcpPortADS TCP port (default: 48898)
RemoteConnectionsArray of PLC routes — each needs Name, Address (IP), NetId, and Type (TCP_IP)

PLC Targets

Maps aliases to PLC connection details. The AmsNetId must match a RemoteConnections entry.

{
  "PlcTargets": {
    "Line1": {
      "AmsNetId": "5.80.201.1.1.1",
      "Port": 851,
      "DisplayName": "Assembly Line 1",
      "TimeoutMs": 5000,
      "SymbolBrowseTimeoutMs": 30000
    }
  }
}
FieldDescriptionDefault
AmsNetIdAMS Net ID of the PLC(required)
PortADS port (851 = PLC runtime 1)851
DisplayNameHuman-readable name shown in API responses""
TimeoutMsTimeout for ADS operations5000
SymbolBrowseTimeoutMsTimeout for symbol browsing30000
EtherCatEtherCAT diagnostics config (see below)null (disabled)

EtherCAT Diagnostics (per PLC)

Optional. When present, the EtherCAT polling service reads diagnostics for this PLC.

{
  "PlcTargets": {
    "Line1": {
      "AmsNetId": "192.168.1.136.1.1",
      "Port": 851,
      "EtherCat": {
        "PollingIntervalMs": 1000,
        "CrcErrorThreshold": 100,
        "EnableNotifications": true,
        "TimeoutMs": 5000
      }
    }
  }
}
FieldDescriptionDefault
PollingIntervalMsBackground polling interval for diagnostics1000
CrcErrorThresholdPer-port CRC count that triggers CrcErrorThresholdExceeded notification100
EnableNotificationsEnable SignalR events for this PLC’s EtherCAT bustrue
TimeoutMsADS read timeout for EtherCAT operations5000

Feature Flags

Enable or disable entire feature slices. Disabled features return 404 and are excluded from the OpenAPI spec.

{
  "Features": {
    "Variables": { "Enabled": true },
    "Symbols": { "Enabled": true },
    "DeviceInfo": { "Enabled": true },
    "Lifecycle": { "Enabled": false },
    "Notifications": { "Enabled": false },
    "Files": { "Enabled": false },
    "EtherCatDiagnostics": { "Enabled": false },
    "Mcp": { "Enabled": false }
  }
}

Authentication

See Authentication for full details.

{
  "Authentication": {
    "Authority": "https://keycloak.example.com/realms/adsify",
    "Audience": "adsify-api",
    "RequireHttpsMetadata": true,
    "RoleClaimType": "realm_access.roles",
    "ValidIssuers": ["https://keycloak.example.com/realms/adsify"]
  }
}

Notifications

{
  "Notifications": {
    "DefaultCycleTimeMs": 100,
    "MaxSymbolsPerClient": 50,
    "HeartbeatIntervalSeconds": 30
  }
}

CORS

{
  "Cors": {
    "AllowedOrigins": ["https://hmi.example.com"],
    "AllowCredentials": true
  }
}

Set AllowedOrigins to an empty array to allow any origin (development only).

Rate Limiting

Per-user, per-endpoint rate limiting protects the API from excessive requests. A global per-user limiter applies to all endpoints, and additional per-endpoint policies provide finer control.

{
  "RateLimiting": {
    "Enabled": true,
    "GlobalPerUser": { "PermitLimit": 200, "WindowSeconds": 1 },
    "VariableReads": { "PermitLimit": 100, "WindowSeconds": 1 },
    "VariableWrites": { "PermitLimit": 20, "WindowSeconds": 1 },
    "BatchOperations": { "PermitLimit": 5, "WindowSeconds": 1 },
    "SymbolBrowsing": { "PermitLimit": 10, "WindowSeconds": 1 },
    "FileOperations": { "PermitLimit": 5, "WindowSeconds": 1 },
    "NotificationConnections": { "PermitLimit": 5, "WindowSeconds": 60 }
  }
}

The partition key is composed of the authenticated user ID (or client IP as fallback) combined with the plcId. When a limit is exceeded, the API returns 429 Too Many Requests with a Retry-After header indicating when the client may retry.

Security Headers

Response headers applied to all API responses for defense-in-depth:

{
  "SecurityHeaders": {
    "StrictTransportSecurity": "max-age=31536000; includeSubDomains",
    "XContentTypeOptions": "nosniff",
    "XFrameOptions": "DENY",
    "ContentSecurityPolicy": "default-src 'none'; frame-ancestors 'none'",
    "CacheControl": "no-store",
    "ReferrerPolicy": "no-referrer",
    "PermissionsPolicy": "()"
  }
}

Connection Limits

Controls the maximum number of real-time connections (SSE, WebSocket) per user:

{
  "ConnectionLimits": {
    "MaxSseConnectionsPerUser": 5,
    "MaxWebSocketConnectionsPerUser": 5,
    "MaxTotalSubscriptionsPerUser": 100,
    "MaxConnectionLifetimeMinutes": 480,
    "IdleTimeoutMinutes": 30
  }
}
FieldDescriptionDefault
MaxSseConnectionsPerUserMaximum concurrent SSE connections per user5
MaxWebSocketConnectionsPerUserMaximum concurrent WebSocket connections per user5
MaxTotalSubscriptionsPerUserMaximum variable subscriptions across all connections100
MaxConnectionLifetimeMinutesMaximum lifetime of a single connection480
IdleTimeoutMinutesDisconnect after this many minutes of inactivity30

Symbol Access Control

Controls which symbols can be written per PLC. Configured under each PLC target:

{
  "PlcTargets": {
    "plc1": {
      "SymbolAccess": {
        "Mode": "denylist",
        "Writable": [],
        "Denied": []
      }
    }
  }
}
FieldDescription
Modeallowlist — only symbols matching Writable patterns can be written. denylist — all symbols can be written except those matching Denied patterns.
WritableGlob patterns for writable symbols (used in allowlist mode)
DeniedGlob patterns for denied symbols (used in denylist mode)

Glob patterns: * matches one path segment, ** matches any depth. For example, MAIN.* matches MAIN.nCounter but not MAIN.stMotor.bEnabled, while MAIN.** matches both.

Value Constraints

Per-symbol validation rules that are enforced on writes. Configured under each PLC target:

{
  "PlcTargets": {
    "plc1": {
      "ValueConstraints": {
        "MAIN.nSpeed": {
          "Min": 0,
          "Max": 100
        },
        "MAIN.sMode": {
          "Enum": [1, 2, 3]
        },
        "MAIN.sName": {
          "Pattern": "^[A-Z].*"
        }
      }
    }
  }
}
ConstraintDescription
Min / MaxNumeric range validation. The write is rejected if the value falls outside the range.
EnumDiscrete allowed values. The write is rejected if the value is not in the list.
PatternRegular expression for string validation. The write is rejected if the value does not match.
ReadOnlyWhen true, all writes to this symbol are rejected.

Writes that violate constraints return 400 with error code VALUE_OUT_OF_RANGE.