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
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
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 for setup, key rotation, and the hands-off path via AgentConfig.dispatch_auth.
verify_dispatch_auth(data, config)
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).
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
export const MESH_DISPATCH_JWT_ISSUER = 'svantic-mesh';
Issuer value the mesh always sets on svantic_jwt dispatch tokens.
Example
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