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

# Securing dispatches

# Securing dispatches

Hosted-mode agents serve A2A on a public URL — if anyone learns the URL, they can POST to it. **Dispatch auth** is the SDK's answer: Svantic attaches a signed envelope to every dispatch, and the SDK verifies it before your handler runs. Requests without a valid envelope are rejected.

Connected-mode agents get transport-level trust from the authenticated WebSocket; dispatch auth is still accepted but optional.

This guide is the operator's view. For the full verifier API, see [Dispatch auth reference](../reference/dispatch-auth).

## Pick a scheme

| Scheme              | How it works                                                                                                                        | Use when                                                                           |
| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
| **`svantic_jwt`**   | Svantic signs an HS256 JWT with a shared secret. Claims include `tenant_id`, `agent_type`, `instance_id`, `dispatch_id`, and `exp`. | Default choice. Gives you audit claims for free.                                   |
| **`shared_secret`** | Svantic attaches an opaque token you've also configured on the agent. Constant-time comparison on the agent side.                   | You can't hold a per-agent secret, or you want the simplest possible verification. |

Both are safe; `svantic_jwt` is richer. Most agents want it.

## Turn it on

### Step 1 — generate a secret

```bash theme={null}
openssl rand -base64 48
```

### Step 2 — store it in the Svantic dashboard

**Settings → Dispatch Auth → \[your agent] → Rotate secret.** Paste the value. Svantic will start signing envelopes to this agent with it.

### Step 3 — configure the SDK

```typescript theme={null}
import { MeshConnector } from '@svantic/sdk/mesh';

const mesh = new MeshConnector(agent, {
  client_id: process.env.SVANTIC_CLIENT_ID!,
  client_secret: process.env.SVANTIC_CLIENT_SECRET!,
  dispatch_auth: {
    scheme: 'svantic_jwt',
    required: true,
  },
});
```

`dispatch_auth` is part of [`MeshConnectorConfig`](../reference/types#meshconnectorconfig). It can also be set via `AttachConfig.mesh.dispatch_auth`.

### Step 4 — make the secret available to the agent

The SDK reads the verifier config from your `VerifierConfig`:

```typescript theme={null}
import { verify_dispatch_auth, DispatchAuthVerifyError } from '@svantic/sdk';

const VERIFIER_CONFIG = {
  instance_id: agent.instance_id,
  signing_secret: process.env.SVANTIC_DISPATCH_SECRET!,
  clock_skew_s: 5,
};
```

Store `SVANTIC_DISPATCH_SECRET` the same way you store any other secret — never commit it, never bake it into images.

## Verify inside a handler

Until dispatch-auth wiring is turned on in the executor by default, call the verifier from handlers where the check matters:

```typescript theme={null}
import { verify_dispatch_auth, DispatchAuthVerifyError } from '@svantic/sdk';

agent.define_capability({
  name: 'publish_release',
  description: 'Ship a release.',
  parameters: { /* … */ },
  handler: async (args, ctx) => {
    try {
      const authed = verify_dispatch_auth(args as any, VERIFIER_CONFIG);
      if (authed.tenant_id !== ctx.tenant_id) {
        throw new Error('tenant_mismatch');
      }
    } catch (err) {
      if (err instanceof DispatchAuthVerifyError) {
        throw new Error(`Unauthorized: ${err.code}`);
      }
      throw err;
    }

    // … safe to proceed
  },
});
```

Branch on `err.code` when you need to distinguish "missing envelope" from "expired" in logs or metrics.

## Rotating the secret

1. In the dashboard, add a **new** secret alongside the old one. Svantic continues to sign with the old one while the new one propagates.
2. Deploy the agent with *both* secrets trusted (use `shared_secrets` map with multiple entries keyed by `credentials_ref`, or deploy two instances in parallel).
3. Cut the dashboard to sign with the new secret.
4. Once no envelopes signed with the old secret are still in flight (wait at least twice the envelope TTL), remove the old secret from the agent.

## Choosing between `shared_secret` and `svantic_jwt`

* `shared_secret` verification is a constant-time string compare. No library dependency on the verifier side, no key format concerns.
* `svantic_jwt` carries tenant / agent / dispatch claims you can log or enforce on (per the `DispatchAuthContext`), and `exp` is checked for you.

Pick `svantic_jwt` unless you have a specific reason not to.

## Connected-mode agents

Connected agents authenticate the transport (the WebSocket is opened with your agent credentials), so every dispatch is already attributable. Dispatch auth is still accepted — treat it as defense in depth. Turn it on in hosted mode first.

## See also

* [Dispatch auth reference](../reference/dispatch-auth)
* [`MeshConnectorConfig`](../reference/types#meshconnectorconfig)
* [Connecting to Svantic](./connecting)
