---
updatedAt: 2026-07-21T14:46:26.000Z
agentTools:
  projectIndex: https://api.help.mabl.com/llms.txt
---

# Clear all operator-tunable overrides on a Link Agent

Clear every per-Agent operator-tunable override in one call, restoring the Agent to the resolved defaults ⊕ label-level overrides. The resolved `operator_tunables` view is recomputed on the response. To clear a single override instead, PATCH that field to `null` via `PATCH /link/agents/{id}`. Idempotent — clearing when no per-Agent overrides are set is a no-op success.


# OpenAPI definition

```json
{
  "openapi": "3.0.3",
  "info": {
    "title": "mabl API",
    "description": "APIs for interacting with mabl services",
    "version": "0.563.3-2026-10-09.1791576280-jbaldassari-1791576682-SNAPSHOT"
  },
  "servers": [
    {
      "url": "https://api.mabl.com/"
    }
  ],
  "components": {
    "securitySchemes": {
      "basicAuth": {
        "type": "http",
        "scheme": "basic"
      },
      "bearerAuth": {
        "type": "apiKey",
        "name": "Authorization",
        "in": "header"
      }
    },
    "schemas": {
      "LinkAgentProtocolEnum": {
        "description": "Protocol the Link Agent uses to reach the mabl side.\nLegacy values (pre-mablnet): - `wss`: WebSocket over TLS to a Link Server (the only one of the three that\n  shipped — `ssl` and `https` are historical placeholders).\n- `ssl`, `https`: never implemented; kept in the enum for backwards\n  compatibility with serialized records that captured them.\n\nNew values (mablnet): - `mablnet_wss`: WSS carrier to a mablnet Router, through the Application LB on\n  :443. The fallback substrate, and the only one that traverses an HTTP proxy.\n- `mablnet_quic`: UDP/QUIC carrier to a mablnet Router, on :443. The preferred\n  substrate where the network passes UDP; Agents fall back to `mablnet_wss`\n  where it does not.\n",
        "type": "string",
        "enum": [
          "ssl",
          "https",
          "wss",
          "mablnet_wss",
          "mablnet_quic"
        ]
      },
      "LinkAgentLegacyState": {
        "description": "Legacy WSS Tunnel snapshot.",
        "type": "object",
        "properties": {
          "tunnel_clients_connected": {
            "type": "integer",
            "format": "int32",
            "description": "Number of `TunnelClient` instances reporting `is_connected = true`."
          }
        }
      },
      "LinkAgentOperatorDebugRequest": {
        "description": "Operator-issued debug request, queued by the api on the LinkAgent and pulled by the Agent on its next poll. Discriminated by `action`; each variant has its own arg shape. Idempotent by design — the Agent dedupes by `request_id` (an LRU of the last 64 ids in-process), so the api re-sending a pending request after an Agent restart is a no-op rather than a re-execute.\n",
        "type": "object",
        "required": [
          "request_id",
          "action",
          "requested_at"
        ],
        "properties": {
          "requested_by": {
            "type": "string",
            "description": "Operator identifier (e.g. mabl user id). Audit-only — not used for authorization.\n"
          },
          "action": {
            "type": "string",
            "description": "Discriminator. Each value maps to a typed sub-schema with that action's argument shape.\n",
            "enum": [
              "dump-state",
              "redial-carriers",
              "redial-carrier",
              "force-peer-ca-refetch",
              "synthetic-dial"
            ]
          },
          "request_id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64,
            "description": "Opaque caller-supplied identifier (recommended: ULID). Agent dedupes by this value and surfaces it back on the ack.\n"
          },
          "requested_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the api enqueued this request."
          }
        },
        "discriminator": {
          "propertyName": "action",
          "mapping": {
            "dump-state": "#/components/schemas/LinkAgentDumpStateRequest",
            "redial-carriers": "#/components/schemas/LinkAgentRedialCarriersRequest",
            "redial-carrier": "#/components/schemas/LinkAgentRedialCarrierRequest",
            "force-peer-ca-refetch": "#/components/schemas/LinkAgentForcePeerCaRefetchRequest",
            "synthetic-dial": "#/components/schemas/LinkAgentSyntheticDialRequest"
          }
        }
      },
      "LinkAgentConfiguration": {
        "description": "A Link Agent's effective, credential-free runtime configuration. Populated by both the Java Link Agent (the full surface below) and the mabl-cli Link Agent (its narrower carrier/uplink surface); fields the reporting Agent has no value for are omitted. Never carries secrets — proxy, PAC, and tunnel credentials are represented only by the `proxy_auth_configured` boolean. The first group of fields is surfaced to customers on the Link Agent details view; the rest is support-oriented detail.\n",
        "type": "object",
        "properties": {
          "tunnels": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The unique set of tunnel names served by this same Agent process. A single Agent instance can serve several tunnels; every one of that instance's LinkAgent records reports the same set, so any one of them shows the full picture.\n"
          },
          "http_proxy": {
            "type": "string",
            "description": "The configured upstream HTTP CONNECT proxy as `host:port`. Credentials, if any, are omitted — see `proxy_auth_configured`. Absent when no fixed proxy is configured.\n"
          },
          "proxy_mode": {
            "$ref": "#/components/schemas/LinkAgentProxyMode",
            "description": "Which classes of the Agent's traffic route through its configured HTTP proxy."
          },
          "auto_updates_enabled": {
            "type": "boolean",
            "description": "Whether the Agent applies mablnet worker auto-updates."
          },
          "connection_filter": {
            "$ref": "#/components/schemas/LinkAgentConnectionFilter",
            "description": "The destination allow/deny policy applied to every connection the Agent opens for a test. When mode is `allow`, only destinations matching the list are reachable (so a host absent from the list is refused); when `deny`, matching destinations are refused; `disabled` allows all. The first thing to check when an Agent reaches some hosts but not others.\n"
          },
          "proxy_auth_configured": {
            "type": "boolean",
            "description": "Whether proxy credentials (fixed-proxy or PAC) are configured. The credentials themselves are never reported; this flag lets support confirm auth is present when diagnosing a 407 from the customer's proxy.\n"
          },
          "link_agent_update_channel": {
            "type": "string",
            "description": "The release channel this Agent updates *itself* from — e.g. `stable` (the default, published only when the Agent is tagged), `latest` (every build of the Agent's trunk), a major version such as `3`, or a branch channel. The counterpart of `mablnet_update_channel`, which governs the worker binary rather than the Agent.\n"
          },
          "proxy_exclusions": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Destinations that bypass the proxy and are dialed directly — suffix, IPv4, IPv6, or CIDR expressions.\n"
          },
          "proxy_pac_url": {
            "type": "string",
            "description": "The proxy auto-configuration (PAC) URL the Agent evaluates to pick a proxy per destination, when PAC-based proxying is configured. Absent for fixed-proxy or no-proxy setups.\n"
          },
          "upstream_proxy": {
            "$ref": "#/components/schemas/LinkAgentUpstreamProxy",
            "description": "The fixed upstream proxy applied specifically to customer-test-target dials (the mablnet worker's SOCKS upstream), when configured separately from `http_proxy`.\n"
          },
          "mablnet_update_channel": {
            "type": "string",
            "description": "The release channel this Agent pulls its mablnet worker binary from — e.g. `stable`, `latest`, or a `branch:<name>` channel.\n"
          }
        }
      },
      "LinkAgentConnectionStatus": {
        "description": "Agent-set per-protocol connection health. Keys align with `endpoints` so operators can pair \"where the api told the Agent to go\" with \"what the Agent reports about it\". Reported on heartbeat.\n",
        "type": "object",
        "properties": {
          "is_connected": {
            "type": "boolean",
            "description": "Whether the Agent currently has a working connection on this protocol. Updated on each heartbeat. Per-protocol state lets mabl ops identify accounts where one protocol is failing (e.g. firewall blocking mablnet's :443 while legacy `wss` works) without losing the working path.\n"
          },
          "last_connected_time": {
            "type": "integer",
            "format": "int64",
            "description": "Time of the most recent successful connection on this protocol. Helps operators distinguish \"never worked\" from \"worked recently and just dropped\". Absent if the Agent has never seen a successful connection for this protocol.\n"
          },
          "unreachable_seconds": {
            "type": "integer",
            "format": "int64",
            "description": "How long this protocol has been without a working connection, measured by the Agent from its own clock and reset the moment it connects. Absent while `is_connected` is true, and absent on Agents that predate the field.\n\nA duration rather than a timestamp on purpose: the api compares it against a threshold, and a customer host's clock can be arbitrarily skewed from mabl's. This is what lets the api tell a sustained loss of access apart from an Agent that is still on its first connect attempt — see the `link_endpoint_unreachable` advisory.\n"
          },
          "last_error": {
            "type": "string",
            "description": "Most recent connection error for this protocol, if `is_connected` is false. Empty when the connection is up.\n"
          }
        }
      },
      "LinkAgentMablnetState": {
        "description": "Per-Host mablnet snapshot, populated by the Agent from MablnetClient.",
        "type": "object",
        "properties": {
          "peer_count": {
            "type": "integer",
            "format": "int32",
            "description": "Number of remote peer Hosts the Agent's Host currently knows about."
          },
          "socks_streams_total": {
            "type": "integer",
            "format": "int64",
            "description": "Total inbound SOCKS5 streams accepted since the process started."
          },
          "worker_version": {
            "type": "string",
            "description": "Version of the native mablnet host worker binary the Agent is currently running. Distinct from the Agent's top-level `version` (the Agent software / distribution version): the worker self-updates on its own channel, so this tracks the running worker generation independently. Absent until the worker has started.\n"
          },
          "socks_streams_active": {
            "type": "integer",
            "format": "int32",
            "description": "Number of inbound SOCKS5 streams currently active (handshake + bridging)."
          },
          "rtt_p50_ms": {
            "type": "number",
            "format": "double",
            "description": "p50 of recent carrier RTT, in milliseconds."
          },
          "carriers": {
            "type": "array",
            "maxItems": 32,
            "description": "The Agent's open carriers, one row per (Router, endpoint) pair — so an Agent holding both a QUIC and a WSS carrier to the same Router reports two rows, and the count of distinct `router_id` values answers \"is this Agent multi-homed?\". Rows rather than a map keyed by endpoint because behind the load balancer every carrier on a substrate dials the same address, which cannot tell Routers apart. Empty until the worker's first carrier-stats push and once that snapshot goes stale, alongside `carriers_open` reading 0.\n",
            "items": {
              "$ref": "#/components/schemas/LinkAgentCarrier"
            }
          },
          "rtt_p99_ms": {
            "type": "number",
            "format": "double",
            "description": "p99 of recent carrier RTT, in milliseconds."
          },
          "carriers_open": {
            "type": "integer",
            "format": "int32",
            "description": "Number of carriers currently in OPEN state."
          },
          "send_window_saturation_pct": {
            "type": "number",
            "format": "double",
            "description": "Share of the last heartbeat window (0-100) during which the Agent's tunnel send path was network-bound — SOCKS sessions sitting at their in-flight write-window high-water mark, i.e. customer data queued behind available tunnel capacity. Computed by the Agent from the worker's cumulative saturation clock; clamped, so overlapping saturated sessions read as fully saturated rather than >100. Sustained high values mean the tunnel itself (not the host) is the constraint — the \"add Link Agents\" signal. Absent when the Agent or worker predates the signal.\n"
          }
        }
      },
      "LinkAgentOperatorDebugResult": {
        "description": "Agent → api ack for a previously-issued debug request. The api matches by `request_id`, marks the queued request as completed, and surfaces the result to whoever issued it. The api drops the queued request once the ack lands so the Agent doesn't have to re-send results on subsequent heartbeats.\n",
        "type": "object",
        "required": [
          "request_id",
          "outcome",
          "completed_at"
        ],
        "properties": {
          "completed_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the Agent finished executing this request."
          },
          "detail": {
            "type": "string",
            "maxLength": 256,
            "description": "Free-form additional context. PII-sanitised on the Agent side — categories and identifiers only, no payload contents.\n"
          },
          "request_id": {
            "type": "string",
            "description": "Matches the `request_id` from the corresponding `LinkAgentOperatorDebugRequest`."
          },
          "outcome": {
            "type": "string",
            "enum": [
              "success",
              "failure",
              "skipped-duplicate",
              "unknown-action"
            ],
            "description": "Agent-side outcome. `skipped-duplicate` = Agent saw this id in its recent-requests LRU and short-circuited; `unknown-action` = Agent's version of the schema doesn't know the requested action (older Agent + newer api).\n"
          }
        }
      },
      "LinkAgentOsSettings": {
        "description": "Static host OS resource limits and network sysctls the Agent reads once at startup (from `/proc` on Linux) and reports on announce. These bind a connection-heavy tunnel well before the mablnet protocol's own stream ceiling: every proxied connection costs a file descriptor and an outbound ephemeral port, so a low open-file limit or a narrow ephemeral-port range caps how many concurrent connections the Agent — and its worker subprocess, which inherits the Agent's rlimits — can carry. Surfaced so operators can audit whether a host is tuned for the Agent's load. Fields are individually omitted when unreadable (e.g. on a non-Linux host), so the object may be sparse or absent.\n",
        "type": "object",
        "properties": {
          "open_file_limit_hard": {
            "type": "integer",
            "format": "int64",
            "description": "Hard `RLIMIT_NOFILE` — the ceiling the soft limit can be raised to without privilege. `-1` means unlimited. When this is also low, raising the soft limit needs a `limits.conf` / systemd change rather than a plain `ulimit -n`.\n"
          },
          "udp_send_buffer_achieved": {
            "type": "integer",
            "format": "int64",
            "description": "Actual `SO_SNDBUF` (bytes) a UDP socket obtains for the carrier's target request — the send-side counterpart of `udp_recv_buffer_achieved`, capped near `net.core.wmem_max`.\n"
          },
          "socket_recv_buffer_max": {
            "type": "integer",
            "format": "int64",
            "description": "`net.core.rmem_max` — the max receive socket buffer (bytes) an application may request. Caps carrier throughput on high-latency links when set below what the carrier asks for (the default of ~208 KB is a common bottleneck).\n"
          },
          "ephemeral_port_range_low": {
            "type": "integer",
            "format": "int32",
            "description": "Low end of `net.ipv4.ip_local_port_range` — the first ephemeral port the kernel hands out to outbound connections. With `ephemeral_port_range_high` it bounds how many simultaneous outbound connections (per remote tuple) the host can open.\n"
          },
          "udp_recv_buffer_achieved": {
            "type": "integer",
            "format": "int64",
            "description": "Actual `SO_RCVBUF` (bytes) a UDP socket obtains when it requests the QUIC carrier's target buffer — the post-clamp value `getsockopt` returns, which the kernel caps near `net.core.rmem_max` (and doubles for bookkeeping on Linux). The Agent measures this with a throwaway probe socket, so it is present on every OS (unlike `socket_recv_buffer_max`, which is Linux `/proc`-only). The real signal for whether this host can honor the QUIC carrier's receive flow-control window.\n"
          },
          "open_file_limit": {
            "type": "integer",
            "format": "int64",
            "description": "Soft `RLIMIT_NOFILE` — the max open file descriptors the process may hold and the limit that actually binds at runtime. Each proxied connection consumes one. `-1` means unlimited. The common Linux default (1024) is low for a busy tunnel.\n"
          },
          "max_user_processes": {
            "type": "integer",
            "format": "int64",
            "description": "Soft `RLIMIT_NPROC` — the max processes/threads for the Agent's user. `-1` means unlimited. JVM and worker threads (Netty event loops, etc.) count against it.\n"
          },
          "ephemeral_port_range_high": {
            "type": "integer",
            "format": "int32",
            "description": "High end of `net.ipv4.ip_local_port_range` (inclusive)."
          },
          "udp_buffer_clamped": {
            "type": "boolean",
            "description": "True when the achieved UDP socket buffer fell materially short of the QUIC carrier's request — i.e. the kernel clamped it (the host needs `net.core.rmem_max` / `wmem_max` raised, or, in a container, the host / WSL2 VM tuned). A clamped buffer throttles QUIC throughput on higher-latency links, so this is the health signal for deciding whether to default this host to the QUIC carrier or fall back to WSS.\n"
          },
          "conntrack_max": {
            "type": "integer",
            "format": "int64",
            "description": "`net.netfilter.nf_conntrack_max` — the connection-tracking table size limit, the ceiling for the dynamic `agent_state.host.conntrack_count`. Present only on Linux hosts with the conntrack module loaded (a stateful firewall/NAT); absent otherwise.\n"
          },
          "socket_send_buffer_max": {
            "type": "integer",
            "format": "int64",
            "description": "`net.core.wmem_max` — the max send socket buffer (bytes) an application may request.\n"
          }
        }
      },
      "LinkAgentOperatorTunables": {
        "description": "Operator-tunable parameters for a running Link Agent. Used in two contexts: as the read-only resolved view (`operator_tunables`) and as the sparse overrides (`operator_tunables_overrides`). In the overrides context absent fields fall through to the next merge layer; `null` explicitly clears the override (`x-dao-apply-nulls: true` per-field). In the resolved-view context the api populates every field by merging static defaults (in `LinkLabelServiceImpl`) with label-level overrides and (on `LinkAgent`) agent-level overrides.\n",
        "type": "object",
        "properties": {
          "mablnet_telemetry_export_interval": {
            "type": "string",
            "format": "duration",
            "nullable": true,
            "description": "Cadence at which the Agent-side SDK MeterProvider flushes to the mablnet-metrics-collector. Drop to ~`PT5S` during incidents.\n"
          },
          "mablnet_client_pool_size": {
            "type": "integer",
            "format": "int32",
            "nullable": true,
            "description": "Multi-carrier peer-pool size for CLIENT legs connecting to this tunnel. Deliberately has NO server-side default: unset means each client auto-selects a fixed pool size from its own machine's resources (total memory, capped by cores) — the api can't know them. An explicit value pins the size for every client of the tunnel: `1` = pool off, N > 1 boots N mablnet Hosts per client — one slot identity each, each with its own carriers — and spreads connections across them, lifting the one-pinned-carrier-per-peer throughput cap for many-connection workloads (browser tests). Does not speed up a single bulk stream, and the Link Agent itself is unaffected — this rides to clients on the resolved tunnel (`client_pool_size`). Consumers treat explicit values < 1 as 1; keep N modest (2-4). PATCH `null` to restore client auto-selection.\n"
          },
          "mablnet_max_carriers": {
            "type": "integer",
            "format": "int32",
            "minimum": 1,
            "maximum": 32,
            "nullable": true,
            "description": "Advisory ceiling — overlap budget for in-flight carrier churn.\n"
          },
          "mablnet_allowed_protocols": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LinkAgentProtocolEnum"
            },
            "nullable": true,
            "description": "The set of mablnet carrier protocols this tunnel is allowed to use (default `[mablnet_quic, mablnet_wss]`). This is the mabl-controlled *policy* — which carriers are permitted — NOT a try-order: order here is not significant. Each host selects the best available protocol from this set (and the fallback order among them) using local signals the operator can't know ahead of time — UDP socket-buffer clamp, an HTTP proxy in the path, live reachability. Set `[mablnet_wss]` to disable QUIC for a tunnel on a known UDP-blocked network, or `[mablnet_quic]` to require QUIC. Only `mablnet_*` values are valid; the worker rebuilds on change. PATCH `null` to clear an override.\n"
          },
          "mablnet_carrier_connect_timeouts": {
            "type": "object",
            "additionalProperties": {
              "type": "integer",
              "format": "int32"
            },
            "nullable": true,
            "description": "Per-protocol carrier establishment deadline in milliseconds, keyed by `LinkAgentProtocolEnum` value (default `{mablnet_quic: 8000, mablnet_wss: 45000}`): how long the worker waits for a carrier on that protocol to JOIN before falling back to the next protocol the host tries (from `mablnet_allowed_protocols`). The deadline is intrinsic to the protocol — QUIC fast-fails to escape silently-blackholing networks; WSS is patient — and is independent of order. A protocol absent from the map uses the worker's built-in default. PATCH `null` to clear the override.\n"
          },
          "mablnet_min_carriers": {
            "type": "integer",
            "format": "int32",
            "minimum": 1,
            "maximum": 16,
            "nullable": true,
            "description": "Steady-state target carrier count for the mablnet Host. PATCH `null` to clear an override and fall back to the next merge layer.\n"
          },
          "agent_log_level": {
            "type": "string",
            "enum": [
              "trace",
              "debug",
              "info",
              "warn",
              "error"
            ],
            "nullable": true,
            "description": "Root logger level. Applied in-place — no Host rebuild.\nDeliberately has no server-side default, and should not be given one. The Agent applies this to its process-global root logger on the first announce, which happens *after* it has applied the level from its own config file / command line — so any value set here silently overrides what the customer configured locally. Left unset (the default) the local config stays authoritative; set explicitly, it means an operator chose this level for this tunnel and it wins.\n"
          },
          "mablnet_carrier_recycle_interval": {
            "type": "string",
            "format": "duration",
            "nullable": true,
            "description": "Period between proactive carrier-recycle ticks: the worker retires its oldest carrier (make-before-break, so existing QUIC sessions migrate without disruption) and the LB redistributes the replacement across the Router fleet. Default `PT60M`. Set `PT0S` to disable proactive recycle (the worker still retires unhealthy carriers). PATCH `null` to clear an override.\n"
          },
          "mablnet_join_timeout": {
            "type": "string",
            "format": "duration",
            "nullable": true,
            "description": "Per-carrier-join timeout. Tighten only when investigating a misbehaving LB.\n"
          },
          "ready_heartbeat_interval": {
            "type": "string",
            "format": "duration",
            "nullable": true,
            "description": "Heartbeat cadence once the Agent reaches `ready` status. While the Agent is still bringing up the legacy / mablnet stacks (status != `ready`) it heartbeats at the hard-coded 10 s tick — fast feedback is what ops + the UI need during startup, and the startup window is short. Once `ready` the cadence drops to this value (default `PT60S`); steady-state Agents mostly only need the heartbeat to guard against sudden death (laptop suspend, network drop) and that doesn't repay 10 s × every Agent in the fleet of api-side load. The Agent reverts to 10 s if `status` ever leaves `ready`. Must stay below the api's dead-agent cleanup threshold (10 min) — `PT300S` (5 min) is a safe upper bound.\n"
          },
          "mablnet_carrier_recycle_jitter": {
            "type": "string",
            "format": "duration",
            "nullable": true,
            "description": "Random offset added to the first carrier-recycle tick, to desync the fleet so recycles don't bunch into a thundering-herd reconnect. Default `PT30M`. No effect when `mablnet_carrier_recycle_interval` is `PT0S`.\n"
          }
        }
      },
      "LinkAgentCapacityDimension": {
        "description": "One resource dimension's peak utilization against its ceiling, derived server-side from the heartbeat `agent_state.host` peaks and the static headroom denominators (`cpu_cores`, `total_memory_bytes`, `os_settings`). Peaks — not instantaneous samples — drive the band, so a burst between heartbeats still registers.\n",
        "type": "object",
        "properties": {
          "resource": {
            "$ref": "#/components/schemas/LinkAgentCapacityResourceEnum",
            "description": "Which dimension this entry describes."
          },
          "limit": {
            "type": "integer",
            "format": "int64",
            "description": "Ceiling in the same unit as `used`. Omitted for `cpu`, and when the limit is unbounded (e.g. an unlimited file-descriptor rlimit).\n"
          },
          "utilization_pct": {
            "type": "number",
            "format": "double",
            "description": "Peak usage as a percentage (0–100) of the ceiling for this dimension."
          },
          "used": {
            "type": "integer",
            "format": "int64",
            "description": "Peak raw usage in the dimension's native unit (memory bytes, file-descriptor count, ephemeral-port count). Omitted for `cpu`, whose utilization is already a percentage.\n"
          },
          "status": {
            "$ref": "#/components/schemas/LinkAgentCapacityStatusEnum",
            "description": "Band for this dimension (never `unknown` — an uncomputable dimension is omitted)."
          }
        }
      },
      "LinkAgentCarrier": {
        "description": "One group of the Agent's open carriers: those terminating on the same Router over the same endpoint. Rebuilt from the worker's live carrier snapshot on every heartbeat, open carriers only.\n",
        "type": "object",
        "properties": {
          "endpoint": {
            "type": "string",
            "description": "The carrier wire-form the Agent dialed, e.g. `udp://mabl-quic.link.mabl.com:443` or `wss://mabl-wss.link.mabl.com:443/mablnet/v1/carrier`. Identifies the address the customer's network has to allow, not the Router — Routers share it.\n"
          },
          "protocol": {
            "allOf": [
              {
                "$ref": "#/components/schemas/LinkAgentProtocolEnum"
              }
            ],
            "description": "Transport carrying this group, `mablnet_quic` or `mablnet_wss`. Reported rather than parsed back out of `endpoint`, so no consumer has to know how a substrate spells its scheme."
          },
          "count": {
            "type": "integer",
            "format": "int32",
            "description": "Open carriers in this group."
          },
          "router_id": {
            "type": "string",
            "description": "The Router these carriers terminate on, and the id the per-carrier debug actions target. Absent when the Agent could not learn it — the carrier is no longer tracked, or the Router advertised none. Not derivable from `endpoint`: behind the load balancer every Router answers on the same address.\n"
          }
        }
      },
      "LinkAgentUpstreamProxy": {
        "description": "A fixed upstream HTTP CONNECT proxy applied to customer-test-target dials, with its bypass list. Credentials are never included.\n",
        "type": "object",
        "properties": {
          "port": {
            "type": "integer",
            "format": "int32",
            "description": "Proxy port."
          },
          "host": {
            "type": "string",
            "description": "Proxy host."
          },
          "exclusions": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Destinations that bypass this proxy and are dialed directly."
          }
        }
      },
      "LinkAgentCapabilityEnum": {
        "description": "Optional behaviors a Link Agent has been built to support. Reported by the Agent on startup; mabl-side endpoints gate operator actions on the presence of the relevant capability so that hitting a feature-dependent endpoint against an older Agent (which would silently fail to act on it) is rejected with a clear error.\n- `maintenance`: the Agent polls `GET /link/agents/{id}` to learn about\n  operator-set state and drains its tunnels when placed into maintenance\n  (`status=draining`), reporting `status=drained` once it has finished.\n",
        "type": "string",
        "enum": [
          "maintenance"
        ]
      },
      "ErrorResponseGeneric": {
        "properties": {
          "code": {
            "type": "integer",
            "format": "int32",
            "minimum": 100,
            "maximum": 599
          },
          "id": {
            "type": "string"
          },
          "message": {
            "type": "string"
          }
        }
      },
      "LinkAgentState": {
        "description": "Rolled-up just-in-time snapshot the Agent pushes on every heartbeat. Snapshot semantics — the api stores only the most recent value as the canonical \"current state\" view. Trend graphs live in Cloud Monitoring via the mablnet-metrics-collector OTel pipeline; this surface is for incident-triage ergonomics (\"show me this Agent's view of the world right now\").\n",
        "type": "object",
        "properties": {
          "recent_errors": {
            "type": "array",
            "maxItems": 5,
            "description": "Fingerprint-deduped ring of the 5 most recent error categories the Agent has seen since process start (or since the last `recent_errors` rotation). Categories — not stack traces or messages — so the field is bounded and PII-safe. Full traces stay in the Agent's local log.\n",
            "items": {
              "$ref": "#/components/schemas/LinkAgentRecentError"
            }
          },
          "legacy": {
            "$ref": "#/components/schemas/LinkAgentLegacyState",
            "description": "Legacy WSS Tunnel snapshot. Absent for mablnet-only Agents."
          },
          "host": {
            "$ref": "#/components/schemas/LinkAgentHostState",
            "description": "Host resource-utilization snapshot (CPU, memory) at heartbeat time."
          },
          "mablnet": {
            "$ref": "#/components/schemas/LinkAgentMablnetState",
            "description": "Per-Host snapshot. Absent for legacy-only Agents."
          }
        }
      },
      "LinkAgentCapacity": {
        "description": "Server-derived capacity insights, recomputed on every read (never persisted) from the heartbeat `agent_state.host` utilization peaks, the static headroom denominators (`cpu_cores`, `total_memory_bytes`, `os_settings`), and the `connections_rejected` saturation counter. Answers \"how close is this Agent to its limits, and is its host tuned?\" for the capacity-insights UI. Present once the Agent has announced its memory total — enough for the `supported_tunnels` projection — with the utilization dimensions filling in from its first heartbeat carrying host stats.\n",
        "type": "object",
        "properties": {
          "supported_tunnels": {
            "type": "integer",
            "format": "int32",
            "description": "How many tunnels this host's memory supports — a projection, not a limit the api enforces, and not a health signal: it is informational even when the Agent runs fewer. Pair it with `tunnels_on_host` to render \"running 4 of 14\".\n\nDerived from `total_memory_bytes` alone, allowing for the Agent process and for the extra worker an update runs alongside the others, since each tunnel runs its own worker process. It therefore assumes the host is dedicated to the Agent — a host also running a database supports fewer, which the `memory` dimension's live utilization is what reveals. Absent when the Agent reported no memory total, and `0` for a host too small for even one tunnel.\n\nCapped at 64 however much memory the host has. Memory is the only term measured, and on a large host it is not the binding one — core count, the file-descriptor limit and the ephemeral-port range bite first, and turning those into a tunnel count needs data we do not have. Those limits are reported separately instead: as `open_file_limit` and `ephemeral_port_range` advisories, and as the live `cpu` dimension.\n"
          },
          "health": {
            "$ref": "#/components/schemas/LinkAgentCapacityStatusEnum",
            "description": "Rolled-up health: the worst dimension band, escalated to `critical` whenever `agent_state.host.connections_rejected` is non-zero (the Agent has already turned traffic away). `unknown` when no dimension could be computed.\n"
          },
          "binding_constraint": {
            "$ref": "#/components/schemas/LinkAgentCapacityResourceEnum",
            "description": "The dimension with the least headroom — the one to address first. Absent when no dimension is computable.\n"
          },
          "os_warnings": {
            "type": "array",
            "description": "Conditions on the Agent's host or network the customer should act on — OS settings below recommended values, the host's memory against the tunnels it runs, and mabl endpoints the Agent cannot reach. Empty when the host is well-tuned, adequately sized, and reaching mabl.\n",
            "items": {
              "$ref": "#/components/schemas/LinkAgentOsWarning"
            }
          },
          "dimensions": {
            "type": "array",
            "description": "Per-resource utilization-vs-ceiling, one entry per computable dimension.",
            "items": {
              "$ref": "#/components/schemas/LinkAgentCapacityDimension"
            }
          }
        }
      },
      "LinkAgentProxyMode": {
        "description": "Which classes of the Agent's traffic route through the configured HTTP proxy: `all` — both the mabl control-plane connection and customer-test-target traffic; `mabl` — only the Agent's connection to mabl; `upstream` — only customer-test-target traffic; `none` — no proxy.\n",
        "type": "string",
        "enum": [
          "all",
          "mabl",
          "upstream",
          "none"
        ]
      },
      "LinkAgentCapacityResourceEnum": {
        "description": "A resource dimension whose live utilization the api compares against its ceiling.",
        "type": "string",
        "enum": [
          "cpu",
          "memory",
          "file_descriptors",
          "ephemeral_ports",
          "conntrack",
          "disk",
          "network"
        ]
      },
      "LinkTunnelOwnerType": {
        "description": "The kind of entity a mablnet tunnel is scoped to — its \"owner\". `workspace` is the classic scope: one tunnel per workspace. `company` scopes a single tunnel to an entire company, so every workspace under that company shares one mablnet tenant (and one Link Agent fleet) instead of needing a tunnel each. `user` is a personal tunnel scoped to a single user, private to that user — its egress is the user's own machine, used to route a cloud test run through it. All three are mablnet-only: legacy Link Tunnel is always workspace-scoped.\n",
        "type": "string",
        "enum": [
          "workspace",
          "company",
          "user"
        ]
      },
      "LinkAgentMaintenance": {
        "description": "Operator-requested maintenance state for a Link Agent. While the Agent's status is `draining` or `drained`, mabl-side routing treats it as ineligible for new traffic (mablnet drains its carriers), but in-flight connections continue to be serviced until they finish or the drain timeout expires. The object persists as a record of the most-recent maintenance window after the Agent returns to service.\n",
        "type": "object",
        "properties": {
          "drain_completed": {
            "type": "integer",
            "format": "int64",
            "description": "When the Agent finished draining — either all in-flight connections completed or `drain_timeout_seconds` elapsed (status `drained`). Set server-side when the Agent first reports it has drained; absent while the status is still `draining`. When present, the Agent is safe to terminate.\n",
            "readOnly": true
          },
          "drain_timeout_seconds": {
            "type": "integer",
            "format": "int32",
            "minimum": 0,
            "description": "Operator-supplied upper bound on how long the Agent will wait for in-flight connections to drain before transitioning to `drained`. Omitted for an unbounded wait.\n",
            "readOnly": true
          },
          "drain_started": {
            "type": "integer",
            "format": "int64",
            "description": "When the Agent was placed into maintenance and began draining (epoch millis).",
            "readOnly": true
          }
        }
      },
      "LinkAgentCapacityStatusEnum": {
        "description": "Capacity health band, used both for the Agent overall and per resource dimension. `healthy` = comfortable headroom; `warning` = approaching a limit (tune or scale soon); `critical` = at/over a limit, or the Agent has already rejected connections for want of capacity; `unknown` = not enough data yet (no heartbeat carrying host stats). As an OS-warning severity only `warning` / `critical` are used.\n",
        "type": "string",
        "enum": [
          "healthy",
          "warning",
          "critical",
          "unknown"
        ]
      },
      "LinkAgentEndpoint": {
        "description": "api-set connection target for one protocol's Link Agent → mabl-side connection. Address format is protocol-specific: legacy `wss` carries an `<fqdn>` string (matches the legacy `current_link_server_endpoint` shape); `mablnet_wss` / `mablnet_quic` carry an FQDN that the Agent resolves at connect time. The api is the source of truth for these values — the Agent reads from `endpoints` to learn where to connect and never writes back to it (corresponding agent-set per-protocol health lives in `connection_status`).\n",
        "type": "object",
        "properties": {
          "address": {
            "type": "string",
            "description": "Protocol-specific connection target."
          }
        }
      },
      "LinkAgentRecentError": {
        "description": "One fingerprint-deduplicated entry in `agent_state.recent_errors`.",
        "type": "object",
        "properties": {
          "fingerprint": {
            "type": "string",
            "description": "Stable category for this error (e.g. `carrier-connect-failed`, `peer-ca-fetch-401`, `mablnet-host-rebuild`). Categories, not stack traces — kept narrow so the field stays bounded across noisy upstreams.\n"
          },
          "count": {
            "type": "integer",
            "format": "int32",
            "description": "Number of distinct errors that mapped to this fingerprint in the window."
          },
          "timestamp": {
            "type": "string",
            "format": "date-time",
            "description": "Last time the Agent saw an error matching this fingerprint."
          }
        }
      },
      "LinkAgentOsWarning": {
        "description": "Something about the Agent's host or network that needs the customer's action — an OS setting (`open_file_limit`, `ephemeral_port_range`, `socket_buffer_limits`), the host's memory against the tunnels it runs (`host_memory`), or a mabl endpoint the Agent cannot reach (`link_endpoint_unreachable`). Advisory — it flags a condition the customer can fix, distinct from a dimension's live `status` (which reflects actual utilization). Thresholds live in the api; see the capacity-insights design doc.\n\n`setting` is a stable key, and `current` / `recommended` / `params` carry every value the copy needs, so a client renders its own localized string from them. There is deliberately no prose here: the api has no request locale, so anything it composed would be English for every reader, and a second copy of a sentence the client owns is only somewhere for the two to drift apart. A client is therefore expected to ship copy for a `setting` before — or with — the api starts sending it, and to skip an advisory whose key it does not recognise.\n",
        "type": "object",
        "properties": {
          "severity": {
            "$ref": "#/components/schemas/LinkAgentCapacityStatusEnum",
            "description": "Always `warning` or `critical`."
          },
          "current": {
            "type": "integer",
            "format": "int64",
            "description": "The Agent's current value for the setting. Absent for an advisory that is not about a numeric setting at all (`link_endpoint_unreachable`).\n"
          },
          "params": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Everything this advisory's copy needs that `current` / `recommended` do not already carry. Keys are stable per `setting`:\n\n* `open_file_limit` — none; `current` and `recommended` are the whole story.\n* `ephemeral_port_range` — `low`, `high`: the range endpoints (`current` is its size).\n* `socket_buffer_limits` — `variant`: `sysctls_known` when the host's `net.core.rmem_max` / `wmem_max` were readable and the advice can name them, else `sysctls_unknown`. The two texts differ in structure rather than in values, so a client selects between them (i18next: pass it as `context`) rather than interpolating. Both reproduce from `recommended`; nothing about the remediation needs prose from the api.\n* `host_memory` — `tunnels`: how many tunnels the host runs, the multiplier behind `recommended`.\n* `link_endpoint_unreachable` — `quic_endpoint` / `wss_endpoint`: the `host:port` the Agent could not reach, present only for an endpoint it actually attempted (a mabl-bound HTTP proxy suppresses the QUIC carrier entirely, and naming an endpoint the Agent never dialled would send the customer after the wrong firewall rule). The Agent's own account of the failure is on `connection_status[*].last_error`. Plus `variant`: `fallback_available` when the Agent is reporting a working legacy `wss` connection to carry traffic over meanwhile, `no_fallback` when it is not — either because the tunnel is mablnet-only or because the Agent cannot reach anything at all. The two consequences are different sentences, so a client selects between them (i18next: `context`) rather than interpolating. The transport belongs to the key, not a param: `quic_endpoint` is UDP, `wss_endpoint` is TCP.\n"
          },
          "setting": {
            "type": "string",
            "description": "The flagged property, and the stable key a client localizes on — one of `open_file_limit`, `ephemeral_port_range`, `socket_buffer_limits`, `host_memory`, `link_endpoint_unreachable`. A client that does not recognise a value should skip the advisory: there is no prose here to fall back on.\n"
          },
          "recommended": {
            "type": "integer",
            "format": "int64",
            "description": "The minimum value the api recommends for a busy tunnel. Absent where `current` is.\n"
          }
        }
      },
      "LinkAgentDistributionEnum": {
        "description": "Which Link Agent distribution an instance is — the Java Link Agent (`java`), the mabl-cli Link Agent (`mabl_cli`), or the personal Link Agent built into the mabl desktop app (`mabl_desktop`).\n",
        "type": "string",
        "enum": [
          "java",
          "mabl_cli",
          "mabl_desktop"
        ]
      },
      "LinkAgentConnectionFilter": {
        "description": "A Link Agent's destination allow/deny connection policy.",
        "type": "object",
        "properties": {
          "mode": {
            "type": "string",
            "enum": [
              "allow",
              "deny",
              "disabled"
            ],
            "description": "`allow` — only listed destinations are reachable; `deny` — listed destinations are refused and all others allowed; `disabled` — no filtering, every destination allowed.\n"
          },
          "destinations": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The host/port rules the mode applies to — `host`, `host:port`, `:port`, IPv4/IPv6 literals, and CIDR ranges (IPv6 bracketed as `[addr]:port`).\n"
          }
        }
      },
      "LinkAgentHostState": {
        "description": "Host + worker-process resource-utilization snapshot the Agent assembles at heartbeat time. CPU and memory are host-wide, read from the JVM's OperatingSystemMXBean (so they include the worker subprocess); the file-descriptor, ephemeral-port, and connection-rejection counts are the worker process's own, pushed to the Agent over the worker IPC. Current samples plus high-water marks since the previous heartbeat — peaks matter for \"how close to its limits\" because a periodic sample misses bursts. Pair with the static `cpu_cores` / `total_memory_bytes` / `os_settings` on the Agent for the headroom denominators.\n",
        "type": "object",
        "properties": {
          "swap_used_bytes": {
            "type": "integer",
            "format": "int64",
            "description": "Swap space in use at the most recent sample, in bytes. Non-zero (and growing) swap use is a memory-pressure signal even when `memory_available_bytes` still looks healthy.\n"
          },
          "peak_ephemeral_ports_in_use": {
            "type": "integer",
            "format": "int64",
            "description": "Peak worker ephemeral-port usage since the previous heartbeat."
          },
          "peak_conntrack_count": {
            "type": "integer",
            "format": "int64",
            "description": "Peak netfilter conntrack entries observed since the previous heartbeat."
          },
          "cpu_utilization_pct": {
            "type": "number",
            "format": "double",
            "description": "Host CPU utilization at the most recent sample, as a percentage (0–100) averaged across all cores."
          },
          "open_fds": {
            "type": "integer",
            "format": "int64",
            "description": "Open file descriptors held by the worker process at the most recent sample. Each proxied connection consumes one; compare against `os_settings.open_file_limit` for the dominant connection-ceiling headroom.\n"
          },
          "disk_free_bytes": {
            "type": "integer",
            "format": "int64",
            "description": "Free space, in bytes, on the filesystem holding the Agent's working directory. A full disk breaks worker auto-updates, update staging, and logging.\n"
          },
          "swap_total_bytes": {
            "type": "integer",
            "format": "int64",
            "description": "Total swap space on the host, in bytes (the denominator for `swap_used_bytes`)."
          },
          "memory_available_bytes": {
            "type": "integer",
            "format": "int64",
            "description": "Host memory available at the most recent sample, in bytes."
          },
          "load_average": {
            "type": "number",
            "format": "double",
            "description": "System load average (1-minute) at the most recent sample, from the JVM OperatingSystemMXBean. Compare against `cpu_cores`: a load approaching or exceeding the core count means the run queue is backed up (CPU- or IO-bound) in a way instantaneous CPU% can miss. Absent on Windows (the JVM doesn't expose it there).\n"
          },
          "conntrack_count": {
            "type": "integer",
            "format": "int64",
            "description": "Netfilter connection-tracking entries currently in use at the most recent sample (Linux `nf_conntrack_count`). On a connection-heavy host behind a stateful firewall/NAT, conntrack-table exhaustion drops new connections before any FD/port limit is hit. Absent when the conntrack module isn't loaded.\n"
          },
          "memory_used_bytes": {
            "type": "integer",
            "format": "int64",
            "description": "Host memory in use at the most recent sample, in bytes."
          },
          "peak_open_fds": {
            "type": "integer",
            "format": "int64",
            "description": "Peak worker open file descriptors since the previous heartbeat."
          },
          "connections_rejected": {
            "type": "integer",
            "format": "int64",
            "description": "Cumulative count, since the worker process started, of outbound dials the worker rejected because a host resource limit was hit (open-file-descriptor or ephemeral-port exhaustion) — not ordinary connection-refused/timeout failures. The unambiguous saturation signal: any nonzero value means the Agent has already turned customer traffic away for want of capacity.\n"
          },
          "ephemeral_ports_in_use": {
            "type": "integer",
            "format": "int64",
            "description": "Outbound connections the worker currently holds open, each consuming one ephemeral port — compare against the `os_settings.ephemeral_port_range` size. Counts active connections only; sockets lingering in TIME_WAIT (which still hold a port briefly) are not included.\n"
          },
          "peak_cpu_utilization_pct": {
            "type": "number",
            "format": "double",
            "description": "Highest CPU-utilization percentage (0–100) observed since the previous heartbeat."
          },
          "peak_memory_used_bytes": {
            "type": "integer",
            "format": "int64",
            "description": "Peak host memory in use since the previous heartbeat, in bytes."
          },
          "disk_total_bytes": {
            "type": "integer",
            "format": "int64",
            "description": "Total size, in bytes, of the filesystem holding the Agent's working directory."
          },
          "udp_rcvbuf_errors": {
            "type": "integer",
            "format": "int64",
            "description": "UDP datagrams the kernel dropped for lack of receive-buffer space since the previous heartbeat (Linux `/proc/net/snmp` `Udp: RcvbufErrors`). Read as a trend, not as an attribution: the kernel sums this over **every** UDP socket in the host's network namespace, so it covers the worker's QUIC carrier sockets but also everything else on the box — a browser's QUIC connections, mDNS, a local DNS resolver — and on a general-purpose host those usually dominate it. A nonzero value therefore means inbound UDP throughput is being lost somewhere on the host, not that the carrier lost anything. Correlate with `os_settings.udp_buffer_clamped` before concluding the buffers are too small: that flag is what says a requested socket buffer was actually capped, and it is the only case where raising `net.core.rmem_max` changes the outcome. Absent off Linux.\n"
          }
        }
      },
      "LinkAgent": {
        "description": "Represents a single instance of a running Link Agent",
        "type": "object",
        "properties": {
          "owner_id": {
            "type": "string",
            "description": "The id of the owning entity — equals `workspace_id` for `workspace`-owned agents, the company id for `company`-owned agents.\n"
          },
          "total_memory_bytes": {
            "type": "integer",
            "format": "int64",
            "description": "Total physical memory on the Agent host, in bytes — the headroom denominator for memory utilization."
          },
          "current_link_server_endpoint": {
            "type": "string",
            "description": "DEPRECATED: legacy single-endpoint field. Use `endpoints[wss].address` for the same value; the legacy field stays populated for pre-mablnet Agents that don't yet understand the `endpoints` map.\n"
          },
          "distribution": {
            "$ref": "#/components/schemas/LinkAgentDistributionEnum",
            "description": "Which Link Agent distribution this instance is — the Java Link Agent, the mabl-cli Link Agent, or the mabl desktop app's personal Link Agent. Reported by the Agent at startup. Absent from agents that predate the field; consumers fall back to the Java distribution (using java_version when present) so update notifications aren't miscategorized during the rollout.\n"
          },
          "capacity": {
            "$ref": "#/components/schemas/LinkAgentCapacity",
            "readOnly": true,
            "description": "Server-derived capacity insights — per-dimension headroom, the binding constraint, rolled-up health, and OS-tuning warnings — recomputed on every read from `agent_state.host` and the static headroom denominators (`cpu_cores`, `total_memory_bytes`, `os_settings`). Read-only; never sent by the Agent. Absent until the first heartbeat carrying host stats.\n"
          },
          "current_link_server_id": {
            "type": "string",
            "description": "DEPRECATED: legacy single-Link-Server binding. Mablnet Agents are not bound to a single Server — they multi-home across Routers. Stays populated for legacy Agents.\n"
          },
          "workspace_id": {
            "type": "string",
            "description": "The workspace that owns this Link Agent, for `workspace`-owned tunnels. Unset for `company`-owned agents (mablnet-only) — read `owner_type`/`owner_id` for the canonical scope.\n"
          },
          "machine_identity": {
            "type": "string",
            "maxLength": 128,
            "description": "Opaque persisted per-machine identity — a mabl-minted UUID stored in the standard OS-specific mabl config directory — required when announcing a personal (`user`-owned) tunnel. The server derives the tunnel's label from it (SHA-256 of `machine_identity \":\" link_infrastructure_key`, lowercase hex, truncated to the 24-char label limit), so one machine keeps one stable tunnel identity per cluster across restarts, reboots, and hostname changes. Not used for workspace- or company-owned agents.\n"
          },
          "os_settings": {
            "$ref": "#/components/schemas/LinkAgentOsSettings",
            "description": "Static host OS resource limits and network sysctls, read once at startup and reported on announce. Used by the capacity-insights UI to flag a host that isn't tuned for the Agent's connection load (e.g. a 1024 open-file limit, a narrow ephemeral-port range, or undersized socket buffers). Set on announce and left untouched by heartbeats.\n"
          },
          "hostname": {
            "type": "string",
            "description": "The Link Agent instance's local hostname"
          },
          "protocol": {
            "$ref": "#/components/schemas/LinkAgentProtocolEnum",
            "description": "DEPRECATED: legacy single-protocol field. Agents on the migration cutover ([deployment/mabl.md § Migration from mabl Link]) report a `protocols` set instead. The legacy field stays populated for backwards compatibility with pre-mablnet Agents and is set to the agent's primary protocol (the first entry in `protocols` when the Agent reports both).\n"
          },
          "operating_system_version": {
            "type": "string",
            "description": "Value of the 'os.version' Java system property"
          },
          "operator_tunables": {
            "$ref": "#/components/schemas/LinkAgentOperatorTunables",
            "readOnly": true,
            "description": "Resolved operator tunables this specific Agent process should apply. Computed on every read as static defaults ⊕ `LinkLabel.operator_tunables_overrides` ⊕ this LinkAgent's `operator_tunables_overrides` (last-write-wins per field). Read-only on the wire — the api rejects PATCHes targeting this field. The Agent reads ONLY this field; it knows nothing about labels, overrides, or merge precedence.\n"
          },
          "operator_tunables_overrides": {
            "$ref": "#/components/schemas/LinkAgentOperatorTunables",
            "description": "Sparse per-Agent operator-tunable overrides. Ephemeral by design — gone when the LinkAgent row is reaped (heartbeat-timeout cleanup). Use for one-off triage of a specific Agent; long-lived tuning belongs on the LinkLabel. PATCH a field's value to set the override; PATCH the field's value to `null` to clear that single override (the resolved view falls back to the next merge layer). To clear every per-Agent override at once, DELETE `/link/agents/{id}/operator-tunables-overrides`.\n"
          },
          "id": {
            "type": "string",
            "description": "UUID for this Link Agent instance"
          },
          "protocols": {
            "type": "array",
            "description": "The set of protocols this Link Agent is currently attempting to keep connections open for. Successor to the singular `protocol` field. A dual-stack Agent during the mablnet migration reports both `wss` (legacy Link Tunnel) and one of the `mablnet_*` values; mabl ops watches this set to determine which accounts are migration-eligible.\n",
            "items": {
              "$ref": "#/components/schemas/LinkAgentProtocolEnum"
            }
          },
          "agent_configuration": {
            "$ref": "#/components/schemas/LinkAgentConfiguration",
            "description": "The Agent's effective runtime configuration (the configuration file ⊕ command line ⊕ environment, merged), sanitized of every credential, reported on announce and refreshed whenever the Agent's configuration changes at runtime. Lets support and customers see the effective proxy, connection-filter, channel, and tunnel settings that actually govern this Agent's traffic — the usual explanation for an Agent that reaches some destinations but not others — without having to ask for the configuration file. The Agent may be configured entirely by command-line flags or environment, so this merged view, not any single file, is what governs it. Credentials (proxy passwords, PAC auth, tunnel API keys) are never included; their presence is reported only via the `*_configured` booleans.\n"
          },
          "is_connected": {
            "type": "boolean",
            "description": "DEPRECATED: legacy single-protocol connection state. Agents reporting `protocols` populate `endpoints` with per-protocol connection state; this field stays populated as `true` if any of the per-protocol states are connected.\n"
          },
          "created_time": {
            "type": "integer",
            "format": "int64",
            "description": "Time at which the Link Agent was created",
            "readOnly": true
          },
          "endpoints": {
            "type": "object",
            "description": "Per-protocol endpoint the Link Agent should connect to, keyed by protocol name (matches `LinkAgentProtocolEnum`). Legacy Link Tunnel Agents only see (and need) the `wss` entry, which mirrors `current_link_server_endpoint`. Mablnet-capable Agents additionally see `mablnet_wss` and (when UDP/QUIC is exposed externally) `mablnet_quic`, pointing at the Application LB FQDN for this environment.\napi-set and read-only from the client perspective: the value flows api → Agent. Per-protocol connection health flowing the other direction (Agent → api) is reported via `connection_status`.\nComputed on every read from the LinkLabel + workspace migration flags + deployment config (`com.mabl.mablnet.domain` + the owner's link infrastructure key); NOT persisted on the LinkAgent itself. A feature-flag flip, FQDN change, or LinkLabel-level update propagates to running Agents on their next poll (≈ 10 s), with no agent restart and no backfill cron. Downstream consumers that need the same view (BigQuery, Pub/Sub) should read via the api rather than the raw DAO entity.\n",
            "readOnly": true,
            "additionalProperties": {
              "$ref": "#/components/schemas/LinkAgentEndpoint"
            }
          },
          "address": {
            "type": "string",
            "description": "The Link Agent instance's local IP address"
          },
          "capabilities": {
            "type": "array",
            "description": "Optional behaviors this Link Agent supports. Reported by the Agent at startup. Operator endpoints that depend on Agent-side handling (`POST /link/agents/{id}/maintenance`, etc.) reject requests when the relevant capability is missing — older Agents that predate the capability simply omit it.\n",
            "items": {
              "$ref": "#/components/schemas/LinkAgentCapabilityEnum"
            }
          },
          "link_infrastructure_key": {
            "type": "string",
            "description": "On request, personal (`user`-owned) announces only: the explicit placement choice when the user's workspace associations resolve to more than one link infrastructure — the ambiguous announce fails with an error naming the candidate keys, and the shared cluster's key is always a valid choice. Omit when the associations are unambiguous. Ignored on requests for workspace- and company-owned agents. On response, for every agent: the link infrastructure key of the dedicated cluster this agent's tunnel is on, omitted on the shared cluster (absent means shared). A personal tunnel reports its recorded placement; a workspace or company agent its owning company's current key, which changes when the company moves between clusters. An agent fetches its peer-CA bundle for this key (`GET /link/mablnet/peer-ca?infrastructure_key=`).\n"
          },
          "last_heartbeat_time": {
            "type": "integer",
            "format": "int64",
            "description": "Time at which the Link Agent last sent a heartbeat",
            "readOnly": true
          },
          "connection_status": {
            "type": "object",
            "description": "Per-protocol connection health, keyed by protocol name (same keys as `endpoints`). Reported by the Agent on heartbeat and merged in.\n",
            "additionalProperties": {
              "$ref": "#/components/schemas/LinkAgentConnectionStatus"
            }
          },
          "mablnet_tenant_id_hex": {
            "type": "string",
            "description": "Zero-padded 8-character lowercase hex form of `mablnet_tenant_id`, e.g. `bee85e2b`. Provided for log/grep/observability ergonomics; the numeric and hex forms always agree (both computed together). Same lifecycle as `mablnet_tenant_id` — computed on every read, not persisted.\n",
            "readOnly": true
          },
          "operator_debug_results": {
            "type": "array",
            "maxItems": 16,
            "description": "Acks pushed by the Agent on heartbeat for previously-queued debug requests. The api matches by `request_id`, drops the matching queue entry, and surfaces the outcome to whichever caller originally enqueued the request.\n",
            "items": {
              "$ref": "#/components/schemas/LinkAgentOperatorDebugResult"
            }
          },
          "operator_debug_queue": {
            "type": "array",
            "maxItems": 16,
            "description": "Debug requests queued by mabl ops for this Agent to execute on its next poll. Bounded — the api rejects enqueues that would exceed `maxItems`. The Agent drains entries on its next tick, executes idempotently (and short-circuits any `request_id` it's seen recently), and acks the outcome via `operator_debug_results` on the next heartbeat. The api drops the matching queue entry on ack so the queue stays self-draining.\n",
            "items": {
              "$ref": "#/components/schemas/LinkAgentOperatorDebugRequest"
            }
          },
          "cpu_cores": {
            "type": "integer",
            "format": "int32",
            "description": "Number of CPU cores available to the Agent host — the headroom denominator for CPU utilization."
          },
          "label": {
            "type": "string",
            "pattern": "[a-z0-9\\-]{1,24}",
            "description": "The user-provided label for this Link Agent instance"
          },
          "operating_system_name": {
            "type": "string",
            "description": "Value of the 'os.name' Java system property"
          },
          "version": {
            "type": "string",
            "description": "The version of the Link Agent software that this instance is running"
          },
          "java_vm_name": {
            "type": "string",
            "description": "Value of the 'java.vm.name' Java system property"
          },
          "mablnet_tenant_id": {
            "type": "integer",
            "format": "int64",
            "description": "Denormalised view of `LinkLabel.mablnet_tenant_id` for this Agent's label. Computed on every read alongside `endpoints` (see that field's description for the full server-set-fields contract); NOT persisted on the LinkAgent. Tenant ids themselves are immutable for a LinkLabel's lifetime, so the value is stable across reads in practice.\n",
            "readOnly": true
          },
          "last_updated_time": {
            "type": "integer",
            "format": "int64",
            "description": "Time at which the Link Agent record last changed",
            "readOnly": true
          },
          "no_ssl_verify": {
            "type": "boolean",
            "description": "Flag indicating whether the agent was started with SSL verification disabled"
          },
          "operating_system_architecture": {
            "type": "string",
            "description": "Value of the 'os.arch' Java system property"
          },
          "tunnels_on_host": {
            "type": "integer",
            "format": "int32",
            "description": "How many tunnels this Agent process supervises, as the Agent itself counts them. Every mablnet tunnel runs its own worker process, so this is the multiplier on the host's memory requirement — the api pairs it with `total_memory_bytes` to flag an undersized host. Each of a multi-tunnel process's Agent records reports the same value. Reported by the Agent rather than derived from records sharing a hostname, which containers and cloned VM images make unreliable. Absent from Agents predating the field.\n"
          },
          "java_version": {
            "type": "string",
            "description": "Value of the 'java.version' Java system property"
          },
          "agent_state": {
            "$ref": "#/components/schemas/LinkAgentState",
            "description": "Just-in-time snapshot pushed by the Agent on every heartbeat. Snapshot semantics — overwritten wholesale by each heartbeat; no history (trend data lives in Cloud Monitoring via the mablnet-metrics-collector OTel pipeline). Used by operator UIs for incident-triage views (\"what does this Agent's world look like right now?\").\n"
          },
          "last_updated_by_id": {
            "type": "string",
            "description": "The principal behind the most recent change to this Link Agent — the API key for a status change reported by the agent, or the user who started or ended a maintenance window. Server-set from the authenticated caller; supplying it is ignored.",
            "readOnly": true
          },
          "created_by_id": {
            "type": "string",
            "description": "The principal that registered this Link Agent — normally the API key the agent runs under. Server-set from the authenticated caller; supplying it is ignored. Activity feed attribution reads this, so an agent without it is reported as having been registered by mabl itself.",
            "readOnly": true
          },
          "deleted_time": {
            "type": "integer",
            "format": "int64",
            "description": "Time at which the Link Agent was stopped or removed",
            "readOnly": true
          },
          "maintenance": {
            "$ref": "#/components/schemas/LinkAgentMaintenance",
            "description": "State for the Agent's most-recent operator-requested maintenance window. Populated when entering maintenance (via `POST /link/agents/{id}/maintenance`); left in place after exit as a historical record. Whether the Agent is currently in a maintenance window is authoritatively determined by `status` being `draining` or `drained`, not by the presence of this field.\n"
          },
          "owner_type": {
            "$ref": "#/components/schemas/LinkTunnelOwnerType",
            "description": "The kind of entity that owns this Link Agent's tunnel. A `workspace`-owned agent also populates `workspace_id`; a `company`-owned agent leaves it unset. Must match the owning Link Label's `owner_type`.\n"
          },
          "point_of_contact": {
            "type": "string",
            "description": "Optional point of contact (a name or email address) for this Link Agent, supplied by the operator via config file or command-line at startup. Surfaced so support can tell who to contact about a given Agent. Free-form — not validated as an email.\n"
          },
          "status": {
            "description": "The current status of the Link Agent",
            "type": "string",
            "enum": [
              "initializing",
              "updating",
              "announcing",
              "locating",
              "connecting",
              "ready",
              "draining",
              "drained",
              "terminated"
            ]
          }
        }
      }
    },
    "responses": {
      "ErrorResponse": {
        "description": "Unknown error",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponseGeneric"
            }
          }
        }
      },
      "ErrorResponse401": {
        "description": "User not authenticated",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponseGeneric"
            }
          }
        }
      },
      "ErrorResponse403": {
        "description": "User not authorized",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponseGeneric"
            }
          }
        }
      },
      "ErrorResponse400": {
        "description": "Invalid or missing parameter",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponseGeneric"
            }
          }
        }
      }
    }
  },
  "security": [
    {
      "basicAuth": []
    },
    {
      "bearerAuth": []
    }
  ],
  "paths": {
    "/link/agents/{id}/operator-tunables-overrides": {
      "delete": {
        "summary": "Clear all operator-tunable overrides on a Link Agent",
        "description": "Clear every per-Agent operator-tunable override in one call, restoring the Agent to the resolved defaults ⊕ label-level overrides. The resolved `operator_tunables` view is recomputed on the response. To clear a single override instead, PATCH that field to `null` via `PATCH /link/agents/{id}`. Idempotent — clearing when no per-Agent overrides are set is a no-op success.\n",
        "operationId": "clearLinkAgentOperatorTunablesOverrides",
        "responses": {
          "200": {
            "description": "The updated Link Agent (with the resolved `operator_tunables` view recomputed).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LinkAgent"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ErrorResponse400"
          },
          "401": {
            "$ref": "#/components/responses/ErrorResponse401"
          },
          "403": {
            "$ref": "#/components/responses/ErrorResponse403"
          },
          "404": {
            "description": "Link Agent not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseGeneric"
                }
              }
            }
          },
          "412": {
            "description": "If-Match precondition failed (ETag mismatch).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponseGeneric"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/ErrorResponse"
          }
        },
        "parameters": [
          {
            "schema": {
              "type": "string"
            },
            "in": "path",
            "name": "id",
            "description": "The ID of the agent whose operator-tunable overrides should be cleared",
            "required": true
          },
          {
            "schema": {
              "type": "string"
            },
            "in": "header",
            "name": "If-Match",
            "description": "Conditional clear — succeeds only if the agent's current ETag matches.",
            "required": false
          }
        ],
        "tags": [
          "LinkAgent"
        ]
      }
    }
  },
  "tags": [
    {
      "name": "LinkAgent"
    }
  ]
}
```