> ## 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.

# Dispatch auth

# Dispatch auth

```typescript theme={null}
import {
  verify_dispatch_auth,
  DispatchAuthVerifyError,
  MESH_DISPATCH_JWT_ISSUER,
  type DispatchAuthContext,
  type VerifierConfig,
  type DispatchAuthVerifyErrorCode,
} from '@svantic/sdk';
```

## What it is

Dispatch auth is the SDK module that proves an inbound A2A dispatch really came from the Svantic mesh. Every capability invocation Svantic routes to an agent can carry a `svantic_auth` envelope inside the `DataPart`. That envelope is either an HS256 JWT issued by the mesh (`scheme: 'svantic_jwt'`) or a shared-secret comparison (`scheme: 'shared_secret'`). `verify_dispatch_auth` parses the envelope, checks the signature / secret, enforces audience binding and clock skew, and hands you a typed `DispatchAuthContext` you can log or pass into authorization decisions.

Two key points:

* **Hosted-mode agents should enable it.** Without dispatch auth, anyone who learns the agent's public URL can invoke capabilities.
* **Connected-mode agents get it for free on the transport.** The outbound WebSocket is already authenticated, so dispatch auth there is accepted but optional.

The SDK calls this for you automatically when `AgentConfig.dispatch_auth` is set. You only import the low-level helpers when you're extracting auth from raw payloads yourself — e.g. a custom transport, a test harness, or a middleware before the SDK touches the request.

## When to use it directly

* You're hosting an agent outside `Agent.start()` / `attach()` (custom transport).
* You want an authorization check *inside* a capability handler based on the verified subject.
* You're writing a compatibility layer that consumes A2A dispatches and want the same safety guarantees.

## Functional usage

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

async function handle_dispatch(payload: Record<string, unknown>) {
  try {
    const ctx = verify_dispatch_auth(payload, {
      agent_instance_id: 'orders-agent-prod-1',
      jwt_secret: process.env.SVANTIC_DISPATCH_JWT_SECRET, // enables svantic_jwt
      shared_secret: process.env.SVANTIC_DISPATCH_SHARED_SECRET, // enables shared_secret
      required: true, // reject anything missing/invalid
    });

    log.info({
      scheme: ctx.scheme,
      caller: ctx.claims?.sub,
    }, 'dispatch accepted');

    // ctx.claims is strongly typed when scheme === 'svantic_jwt'.
    return await run_capability(payload);
  } catch (err) {
    if (err instanceof DispatchAuthVerifyError) {
      log.warn({ code: err.code }, 'dispatch rejected');
      throw new HttpError(401, 'unauthorized');
    }
    throw err;
  }
}
```

See the [Securing dispatches guide](../guides/securing-dispatches) for setup, key rotation, and the hands-off path via `AgentConfig.dispatch_auth`.

## `verify_dispatch_auth(data, config)`

```typescript theme={null}
function verify_dispatch_auth(
  data: Record<string, unknown> | undefined | null,
  config: VerifierConfig,
): DispatchAuthContext;
```

Verify a dispatch. `data` is the `DataPart` payload as received by the SDK (the object your capability handler sees, before argument extraction).

* Returns a [`DispatchAuthContext`](#dispatchauthcontext) on success.
* Throws [`DispatchAuthVerifyError`](#dispatchauthverifyerror) on any failure; `err.code` lets you branch on the specific reason.

Supported schemes:

* **`svantic_jwt`** — HS256 JWT signed with the agent's `signing_secret`. The verifier checks `iss` = `'svantic-mesh'`, `aud` = `agent:<instance_id>`, and `exp`. Claims (`tenant_id`, `agent_type`, `instance_id`, `dispatch_id`, `jti`) are surfaced on the returned context.
* **`shared_secret`** — opaque token compared in constant time against a locally configured secret. Supports per-credentials-ref secrets via `shared_secrets` map.

## `VerifierConfig`

| Field             | Type                                                  | Purpose                                                                                                                                       |
| ----------------- | ----------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `instance_id`     | `string`                                              | This agent's instance id. The verifier requires the JWT audience to be exactly `agent:<instance_id>` to prevent token reuse across instances. |
| `signing_secret?` | `string`                                              | HS256 shared secret for `svantic_jwt` verification.                                                                                           |
| `shared_secrets?` | `ReadonlyMap<string, string> \| { default?: string }` | Material for `shared_secret` verification. Map keyed by `credentials_ref`, or a single `default` secret.                                      |
| `now?`            | `() => number`                                        | Clock (epoch ms). Defaults to `Date.now`.                                                                                                     |
| `clock_skew_s?`   | `number`                                              | JWT-expiry tolerance in seconds. Defaults to 5.                                                                                               |

## `DispatchAuthContext`

Returned by a successful verification. Only `scheme` and `expires_at` are guaranteed; JWT claims are populated for `svantic_jwt` only.

| Field              | Type                               | Description                                                                   |
| ------------------ | ---------------------------------- | ----------------------------------------------------------------------------- |
| `scheme`           | `'svantic_jwt' \| 'shared_secret'` | Which scheme verified.                                                        |
| `expires_at`       | `number`                           | Epoch seconds when the envelope expires.                                      |
| `tenant_id?`       | `string`                           | JWT claim.                                                                    |
| `agent_type?`      | `string`                           | JWT claim.                                                                    |
| `instance_id?`     | `string`                           | JWT claim.                                                                    |
| `dispatch_id?`     | `string`                           | JWT claim.                                                                    |
| `jti?`             | `string`                           | JWT id — use for replay prevention if you want it.                            |
| `credentials_ref?` | `string`                           | Echoed ref for `shared_secret`, useful when the agent holds multiple secrets. |

## `DispatchAuthVerifyError`

Thrown for every failure. `err.code` is one of:

| Code                 | Meaning                                                                        |
| -------------------- | ------------------------------------------------------------------------------ |
| `missing_envelope`   | Auth was required but none was attached.                                       |
| `malformed_envelope` | Envelope failed schema validation.                                             |
| `unsupported_scheme` | Scheme not handled by this verifier.                                           |
| `invalid_signature`  | JWT signature did not verify.                                                  |
| `wrong_issuer`       | JWT `iss` was not `svantic-mesh`.                                              |
| `wrong_audience`     | JWT `aud` did not match this agent.                                            |
| `expired`            | JWT `exp` or envelope `expires_at` is in the past.                             |
| `secret_mismatch`    | `shared_secret` token did not match local secret.                              |
| `no_local_secret`    | `shared_secret` verification requested but the agent has no configured secret. |
| `not_configured`     | `svantic_jwt` verification requested but the agent has no `signing_secret`.    |

## Constants

### `MESH_DISPATCH_JWT_ISSUER`

```typescript theme={null}
export const MESH_DISPATCH_JWT_ISSUER = 'svantic-mesh';
```

Issuer value the mesh always sets on `svantic_jwt` dispatch tokens.

## Example

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

agent.define_capability({
  name: 'publish_post',
  description: '…',
  parameters: { /* … */ },
  handler: async (args, ctx) => {
    // `data` is what the SDK received — pass it through from the executor
    // integration in your wrapper, or use the SDK's built-in wiring once enabled.
    try {
      const auth_ctx = verify_dispatch_auth(args as any, {
        instance_id: agent.instance_id,
        signing_secret: process.env.SVANTIC_DISPATCH_SECRET,
      });
      console.log('Authorized caller tenant', auth_ctx.tenant_id);
    } catch (err) {
      if (err instanceof DispatchAuthVerifyError) {
        throw new Error(`Unauthorized: ${err.code}`);
      }
      throw err;
    }

    return { ok: true };
  },
});
```

## See also

* [Securing dispatches guide](../guides/securing-dispatches)
* [Connecting to Svantic](../guides/connecting)
