> ## Documentation Index
> Fetch the complete documentation index at: https://docs.meshagent.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Routes

> Manage project routes that map domains and paths to room services, room content, or managed agents.

Routes map a domain to room HTTP services, files in room storage, or a managed agent websocket.

Use them when you want a stable URL for something inside a room, such as a web app, static website, or HTTP integration.

For dynamic applications, a route can proxy to a published service port. For static sites, a route can serve a room storage subpath directly without running a web server.

Routes can front either a fully public site or a private app protected by MeshAgent. For browser apps, MeshAgent can act as an identity-aware proxy in front of your service: it authenticates the user, checks that they are allowed into the room, and only then forwards the request to your app.

For deployment basics, see [Service YAML](../services/deployment/deploy_services). Routes are managed from the [MeshAgent CLI](../reference/meshagent_cli_help).

## How routes work

* A route selects a room or managed agent backend.
* Each room route path targets either a published service port with `targetPort` or room storage with `targetContent`.
* When a request arrives, MeshAgent chooses the longest matching path and applies that target's security and response options.
* Service targets are proxied to the published port. Content targets are read directly from room storage.

## Create a route

Use a MeshAgent-managed domain such as `*.meshagent.app`:

1. Deploy a service that exposes an HTTP endpoint and marks its port as published.
2. Create a route:

```console theme={null}
meshagent route create --room my-room --port 5002 --domain my-app.meshagent.app
```

3. The route is ready as soon as it is created.

You can also create or update a route from a RouteSpec file:

```yaml theme={null}
kind: Route
version: v1
metadata:
  name: my-app
  annotations: {}
domain: my-app.meshagent.app
backend:
  room:
    name: my-room
paths:
  - path: /
    pathType: prefix
    targetPort: 5002
    unavailable: errors/unavailable.html
```

For a service route, `unavailable` names a file in the room storage root to return when the room, published service, container, or tunnel is unavailable. The fallback response has status `503`. A leading `/` is accepted but remains storage-relative, so `/errors/unavailable.html` does not address the host filesystem. Application responses, including an intentional `503` returned by the service itself, are passed through unchanged.

```console theme={null}
meshagent route create -f route.yaml
meshagent route update my-app.meshagent.app -f route.yaml
```

Managed agent routes use an agent backend and expose the agent websocket on the route domain:

```yaml theme={null}
kind: Route
version: v1
metadata:
  name: my-agent
  annotations: {}
domain: my-agent.meshagent.app
backend:
  agent:
    name: my-agent
```

## Serve room content directly

Use `targetContent` when a site already exists in room storage and does not need an application server. `subpath` is relative to the room storage root; the matched public route path is removed before the remaining request path is appended.

For a single content path, create the route directly from the CLI:

```console theme={null}
meshagent route create \
  --domain docs.meshagent.app \
  --room docs-room \
  --content-path websites/docs \
  --index \
  --compression brotli \
  --cors '[{"allowedOrigins":["https://app.example.com"]}]'
```

Use `--path /docs` to mount the content below a public URL path, `--iap` to require identity-aware access, and `meshagent route update` with the same options to change an existing route. `--room-path` is an alias for `--content-path`.

```yaml theme={null}
kind: Route
version: v1
metadata:
  name: docs
  annotations: {}
domain: docs.meshagent.app
backend:
  room:
    name: docs-room
paths:
  - path: /
    pathType: prefix
    targetContent:
      subpath: websites/docs
      notFound: errors/404.html
      index: true
      iap: false
      compression: brotli
      cors:
        - allowedOrigins:
            - https://app.example.com
          allowedMethods: [GET, HEAD]
          allowedHeaders: [Authorization]
          exposeHeaders: [Content-Length]
          maxAgeSeconds: 3600
          allowCredentials: false
```

This exposes `websites/docs/logo.svg` as `/logo.svg`. With `index: true`, requests for the route root and directories serve `index.html`, such as `websites/docs/index.html` and `websites/docs/guide/index.html`.

When a requested object does not exist, `notFound` names a fallback file relative to the same `targetContent.subpath`. In the example, a missing object serves `websites/docs/errors/404.html` with status `404`. A leading `/` is accepted but still resolves beneath `websites/docs`; it never changes the storage root. If the fallback file is also missing, the normal not-found response is returned.

Content routes support `GET`, `HEAD`, and CORS preflight `OPTIONS` requests. CORS rules use the familiar object-storage controls for allowed origins, methods, and headers, exposed response headers, preflight cache age, and credentials. Credentialed CORS rules must list explicit origins rather than `*`.

Web serving requires MeshAgent's built-in GCS or local-filesystem room storage provider. Other room storage implementations are rejected with `unsupported room storage type: X for web serving` rather than being accessed through a running room.

`compression` accepts `brotli`, `gzip`, or `none` and defaults to `brotli`. Compression is negotiated with the request's `Accept-Encoding` header; clients that do not advertise the selected encoding receive the original content.

Set `iap: true` to protect the content with MeshAgent's identity-aware proxy. The router authenticates the IAP session and checks room site access before reading the file. CORS preflight responses do not expose file content and do not require an IAP cookie.

A route can mix service and content targets on different paths. Each individual path must set exactly one of `targetPort` or `targetContent`.

## Mark the port as published

In your service config, the HTTP port must be marked as published:

```yaml theme={null}
ports:
  - num: 5002
    type: http
    published: true
```

## Public and private published ports

`published: true` makes a port routable from a route.

`public` controls whether that routed URL is open to the internet or protected by MeshAgent:

* `public: true`: MeshAgent forwards requests without requiring room authentication.
* `public: false`: MeshAgent requires the caller to authenticate before the request can reach the app.
* If you omit `public`, the port is treated as private.

For API clients and server-to-server callers, a private published port expects a participant token:

```http theme={null}
Authorization: Bearer <participant-token>
```

That token must be valid for the room. If the caller does not have access to the room, MeshAgent rejects the request before it reaches your app.

## Integrated security for browser apps

For browser-based apps, use cookie validation so MeshAgent behaves like an identity-aware proxy in front of your route. This is the easiest way to publish a private app without making the app itself handle MeshAgent tokens directly.

```yaml theme={null}
ports:
  - num: 5002
    type: http
    published: true
    public: false
    liveness: /healthz
    annotations:
      meshagent.request.validation.method: cookie
```

You can set `meshagent.request.validation.method: cookie` on the port or on a specific endpoint. Endpoint annotations override port annotations.

With that configuration, the request flow looks like this:

1. A user visits the routed URL.
2. If they do not already have a valid MeshAgent IAP session for that route, MeshAgent redirects the browser to sign in.
3. After sign-in, MeshAgent stores a secure, HTTP-only session cookie and retries the request through the route.
4. On each request, MeshAgent validates that the session still maps to a participant token for the target room.
5. If the user is not allowed in the room, the request is rejected before it reaches your app.

This gives you a stable URL with MeshAgent-managed authentication and room-level authorization in front of the service.

For normal browser navigation, unauthenticated `GET` requests are redirected into the login flow automatically. Non-`GET` requests without a valid session are rejected until the browser has signed in.

## Headers your app receives

When a request passes through cookie-based IAP, MeshAgent removes the internal `__meshagent_iap` cookie before forwarding the request to your app and adds trusted identity headers:

| Header                  | Meaning                                                                                                 |
| ----------------------- | ------------------------------------------------------------------------------------------------------- |
| `X-MESHAGENT-USER`      | The participant token `name`, typically the signed-in user's email or display identity.                 |
| `X-MESHAGENT-API-SCOPE` | The participant token API permissions, serialized as JSON. If the token has no API grant, this is `{}`. |

These headers are intended for the destination app to consume.

MeshAgent also strips any client-supplied `X-MESHAGENT-USER` or `X-MESHAGENT-API-SCOPE` headers before forwarding the request, so callers cannot spoof them without actually going through MeshAgent IAP.

## Queue-backed routes

Routes are not limited to proxying traffic into an HTTP app. They can also turn incoming HTTP requests into queue messages for agents or workers inside the room.

This is useful when you want:

* a stable public URL
* no always-on HTTP app inside the room
* an internal queue that workers can process asynchronously

When `meshagent.request.queue` is configured on the matched port or endpoint, MeshAgent enqueues the request body instead of proxying the request to a destination app.

```yaml theme={null}
ports:
  - num: 5002
    type: http
    published: true
    public: false
    annotations:
      meshagent.request.queue: inbound-events
      meshagent.request.validation.method: bearer
```

With that configuration:

1. A request arrives at the route.
2. MeshAgent validates the caller using the configured route auth method.
3. If validation succeeds, MeshAgent publishes the request body to the queue.
4. MeshAgent returns `202 Accepted`.

Today the queued message payload is:

```json theme={null}
{"body":"<raw request body text>"}
```

### Required annotations

| Annotation                            | Purpose                                                                                 |
| ------------------------------------- | --------------------------------------------------------------------------------------- |
| `meshagent.request.queue`             | Queue name to publish into.                                                             |
| `meshagent.request.validation.method` | Optional request validation method. Supported values are `bearer`, `jwt`, and `cookie`. |

You can place these annotations on the port or on a specific endpoint. Endpoint annotations override port annotations.

### Secret-backed validation

The validation secret is not copied into the route itself. Instead, MeshAgent reads it from room secrets at request time and uses it to verify the incoming webhook or signed request before anything is placed on the queue.

This keeps the shared secret inside the room security boundary while still letting you publish an external URL.

Supported validation methods currently include:

* `github`
* `salesforce`
* `sentry`
* `slack`
* `shopify`
* `stripe`
* `telegram`
* `twilio`
* `whatsapp`
* `zendesk`

### What to store in the room secret

Store the provider's original shared secret value in the room secret.

Do not store:

* the incoming signature header value
* a computed HMAC or digest
* a JSON wrapper object unless the provider explicitly gives you a plain secret inside it

Use the raw secret string or key that the provider tells you to use for request verification.

| Method       | Secret value to store in the room                                                                                                                                                                    | Provider docs                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `github`     | The webhook secret token you configured for that GitHub webhook.                                                                                                                                     | [GitHub: Validating webhook deliveries](https://docs.github.com/en/webhooks/using-webhooks/validating-webhook-deliveries)                                                                                                                                                                                                                                                                                                                                                                                     |
| `slack`      | Your Slack app's signing secret.                                                                                                                                                                     | [Slack: Verifying requests from Slack](https://api.slack.com/docs/verifying-requests-from-slack)                                                                                                                                                                                                                                                                                                                                                                                                              |
| `stripe`     | The webhook endpoint's signing secret. This is not a Stripe API key.                                                                                                                                 | [Stripe: Receive events with an HTTPS server](https://docs.stripe.com/webhooks/test)                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `shopify`    | For Shopify app webhooks, your app client secret.                                                                                                                                                    | [Shopify: Deliver webhooks through HTTPS](https://shopify.dev/docs/apps/build/webhooks/subscribe/https), [Shopify: About client credentials](https://shopify.dev/docs/apps/build/authentication-authorization/client-secrets)                                                                                                                                                                                                                                                                                 |
| `telegram`   | The Telegram webhook secret token you pass to `setWebhook` as `secret_token`.                                                                                                                        | [Telegram Bot API: setWebhook](https://core.telegram.org/bots/api#setwebhook)                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| `twilio`     | Your Twilio Auth Token used for request validation.                                                                                                                                                  | [Twilio: Security](https://www.twilio.com/docs/usage/security), [Twilio: REST API Auth Token](https://www.twilio.com/docs/iam/api/authtoken)                                                                                                                                                                                                                                                                                                                                                                  |
| `whatsapp`   | Your Meta app secret used for `X-Hub-Signature-256` validation.                                                                                                                                      | [WhatsApp Cloud API: Set up webhooks](https://developers.facebook.com/docs/whatsapp/cloud-api/guides/set-up-webhooks)                                                                                                                                                                                                                                                                                                                                                                                         |
| `zendesk`    | The webhook signing secret key from the Zendesk webhook configuration.                                                                                                                               | [Zendesk: Verifying webhook authenticity](https://developer.zendesk.com/documentation/event-connectors/webhooks/verifying)                                                                                                                                                                                                                                                                                                                                                                                    |
| `sentry`     | The Sentry service hook `secret` value for that hook.                                                                                                                                                | [Sentry: Register a New Service Hook](https://docs.sentry.io/api/projects/register-a-new-service-hook/), [Sentry: Retrieve a Service Hook](https://docs.sentry.io/api/projects/retrieve-a-service-hook/)                                                                                                                                                                                                                                                                                                      |
| `salesforce` | The signing key or shared secret for the Salesforce webhook product that is sending the request. Common cases are Marketing Cloud ENS callback signature keys and Data Cloud generated signing keys. | [Salesforce Marketing Cloud ENS: Notification Signing](https://developer.salesforce.com/docs/marketing/marketing-cloud/guide/ens-notification-signing.html), [Salesforce Data Cloud: Generate a Secret Key for Signature Validation](https://developer.salesforce.com/docs/data/data-cloud-ref/guide/c360a-api-generate-secret-key-for-signature-validation.htm), [Salesforce Data Cloud: Payload Signature](https://developer.salesforce.com/docs/data/data-cloud-ref/guide/c360a-api-payload-signature.htm) |

Queue-backed validated routes must remain non-public. They are intended for authenticated or signature-validated ingress handled by MeshAgent, not open anonymous forwarding.

## Liveness and startup behavior

`liveness` is the HTTP path MeshAgent uses to decide when a published port is actually ready to serve traffic.

```yaml theme={null}
ports:
  - num: 5002
    type: http
    published: true
    liveness: /healthz
```

When a request hits a route and MeshAgent cannot connect to the target port yet, it checks the `liveness` URL and waits for it to return `2xx`. Once the service is live, MeshAgent retries the original request.

This matters during startup, cold starts, and restarts:

* With a liveness URL, MeshAgent can wait for the app to finish booting instead of immediately failing the first request.
* Without a liveness URL, an early request is more likely to fail with a bad gateway while the process is still starting.

You should give published HTTP ports a liveness URL that is:

* Cheap to evaluate.
* Available without external user auth.
* Wired to real readiness, not just process start.

A good pattern is `/healthz` or `/ready` returning `200` only after the app is ready to serve the same traffic the route will send.

## Manage routes

```bash theme={null}
meshagent route list
meshagent route get my-app.meshagent.app
meshagent route update my-app.meshagent.app --port 5003
meshagent route delete my-app.meshagent.app
```

The default `route list` table includes each public path, service port or room content path, `index`, `iap`, `compression`, and CORS rules. Use `meshagent route list --output json` or `meshagent route get DOMAIN` for the complete RouteSpec, including routes with multiple path targets.

To create or update a route, you need permission to administer the target room.

## Related docs

* [Feeds](./feeds)
* [Projects](./projects)
* [Service YAML](../services/deployment/deploy_services)
* [MeshAgent Image](../services/containers/meshagent_image)
* [Participant Tokens](../rest_api/participant_tokens)
