---
title: "Resolve the mablnet tunnel target for an owner + label"
method: GET
path: "/link/mablnet/tunnels/resolve"
tags: ["LinkMablnet"]
---

# Resolve the mablnet tunnel target for an owner + label

`GET /link/mablnet/tunnels/resolve`

Resolves which mablnet tunnel to reach for a given `label`, and where (the mablnet WSS Router endpoint + whether a live agent is serving it). Two modes:
**Workspace-first (default, `owner_type` omitted).** The cloud-test-run path: a test executor knows the run's `workspace_id` and the configured label but not the tunnel's owner. If a workspace-owned tunnel `(workspace_id, label)` exists it wins, and is returned even if no agent is currently live (so the caller fails the run rather than silently routing elsewhere); otherwise the workspace's company is resolved and a company-owned tunnel `(company, label)` is returned if one exists. `404` when neither exists. (Company tunnels carry no `workspace_id`, so the workspace agent listing cannot find them — this is why the endpoint encapsulates the resolution.)
**Owner-aware (`owner_type` set).** Resolves the tunnel for an explicit owner — used by user-facing tooling (`mabl link-agents test …`) to reach a `user` (personal) or an explicit `company` tunnel. `owner_id` is required for `company`/`workspace`; for `user` it must be the caller's own id (a personal tunnel is private to its owner). Authorized like `POST /link/mablnet/authorize/peer`: a `user` owner only by that user, a `company` owner by a company admin/member, a `workspace` owner by a user who can modify it. `404` when no such tunnel exists.
`has_live_agent` reports whether a `ready` Link Agent advertising `mablnet_wss` has heartbeated within the liveness window for the resolved owner — the caller's pre-run "is this tunnel serviceable" gate, mirroring the legacy Link Server check.

## Query parameters

- `workspace_id` string
- `label` string, required
- `owner_type` 'workspace' | 'company' | 'user' — 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.
- `owner_id` string

## Response `200`

The resolved tunnel target.

- MablnetTunnelTarget — The resolved mablnet tunnel a cloud test run should reach for a `(workspace_id, label)`, returned by `GET /link/mablnet/tunnels/resolve`. Unlike the legacy path's list of Link Servers, a mablnet tunnel is a single tenant reached by dialing a mablnet Router; intra-tenant load-balancing across the tunnel's agents is the Router's job, so one target (not a list) is returned per label. The client connects with a carrier from `carrier_endpoints`, choosing the best of the `allowed_protocols` (and the fallback order among them) from what it knows locally.
  - `owner_type` 'workspace' | 'company' | 'user', required — 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.
  - `owner_id` string, required — The resolved owner id — the run's workspace id for a workspace tunnel, the company id for a company tunnel. This is the SOCKS5 username a client presents to the agent's socks5 service, whose tripwire is `(owner_id, label)`.
  - `label` string — The resolved tunnel's Link label — the second half of the SOCKS5 tripwire `(owner_id, label)`. Echoed so a caller resolving several tunnels at once (e.g. all of a user's live personal tunnels) can tell them apart.
  - `carrier_endpoints` object, required — The deployment's mablnet Router FQDN for each available carrier protocol, keyed by the `LinkAgentProtocolEnum` value (`mablnet_wss`, `mablnet_quic`). Bare hosts; the client normalises each to its scheme (`wss://` / `udp://`). A protocol absent from the map isn't offered by this deployment (e.g. QUIC before it has rolled out to the region).
  - `allowed_protocols` LinkAgentProtocolEnum[], required — The set of mablnet carrier protocols this tunnel is allowed to use — the mabl-controlled policy, operator-tunable per tunnel via `LinkLabel.operator_tunables_overrides`. Order is not significant. Only `mablnet_*` values appear, and every entry has a matching key in `carrier_endpoints`. The client picks the best available protocol from this set, and the fallback order among them, using local signals the operator can't know (UDP-buffer clamp, proxy in the path, reachability).
  - `carrier_connect_timeouts` object — Per-protocol carrier establishment deadline in milliseconds, keyed by `LinkAgentProtocolEnum` value — how long the client waits for a carrier on that protocol to JOIN before falling back to the next protocol it tries (from `allowed_protocols`). The mabl-controlled, operator-tunable default (`{mablnet_quic: 8000, mablnet_wss: 45000}`); a protocol absent from the map uses the client's built-in default.
  - `client_pool_size` integer — Multi-carrier peer-pool size the client leg should boot for this tunnel, present only when the operator pinned one (the tunable `mablnet_client_pool_size`). Absent means the client auto-selects a fixed pool size from its own machine's resources (total memory, capped by cores). An explicit N > 1 boots N mablnet Hosts — 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; `1` = pool off. Treat explicit values < 1 as 1.
  - `has_live_agent` boolean, required — True when a `ready` Link Agent advertising a mablnet carrier for the resolved owner + label has heartbeated within the liveness window. The executor's pre-run serviceability gate.
  - `mablnet_tenant_id_hex` string — Zero-padded 8-character lowercase hex of the tunnel's mablnet tenant id, when the resolved Link Label carries one. Diagnostic / correlation aid; not required to connect.
  - `infrastructure_key` string — The link infrastructure cluster key the `carrier_endpoints` belong to — the owning company's resolved key for workspace/company tunnels, the announce-time placement recorded on the Link Label for personal tunnels. Lets consumers apply cluster-eligibility rules (e.g. the personal-tunnel stripe-set filter) without re-resolving.

## Other responses

- `401` — User not authenticated
- `403` — User not authorized
- `404` — No resolvable mablnet tunnel for that workspace + label — either no tunnel (workspace- or company-owned) exists, or the owner's cluster publishes no carrier endpoint. Both answer 404 deliberately: a status that distinguished them would tell an unauthorized caller which labels exist.
- `default` — Unknown error

---

[API](https://skmtc.net/mabl/apis/mabl-api.md) · [All operations](https://skmtc.net/mabl/apis/mabl-api/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/mabl/mabl-api/revisions/d0fc0114da66/schema)
