# gateway

A `gateway` block runs a managed subprocess that bridges Squadron to an external system — a Discord channel, a Slack workspace, a Microsoft Teams chat, a custom web dashboard, anything you build against the Gateway SDK.

The gateway subscribes to live `human_input_requested` / `human_input_resolved` events from Squadron and surfaces them in its target system; user actions in that system flow back to Squadron through the same record store. From the agent's perspective, an answer in Discord is indistinguishable from one typed into the Command Center Inbox.

A gateway can also receive **mission-lifecycle notifications** (`mission_completed` / `mission_failed` / `mission_stopped`) when a mission opts in via a [`notification`](/missions/notifications) block — a one-way post, separate from the interactive human-input flow.

Squadron supports at most one `gateway` block per instance.

## Minimal Example — Discord

```hcl
variable "discord_bot_token" {
  secret = true
}

variable "discord_channel_id" {}

gateway "discord" {
  source  = "github.com/mlund01/squadron-gateway-discord"
  version = "v0.0.1"

  settings = {
    bot_token       = vars.discord_bot_token
    channel_id      = vars.discord_channel_id
    checkpoint_path = "${path.cwd}/.squadron/discord-gateway.json"
  }
}
```

Restart Squadron and the next `builtins.human.ask` call will appear in the configured Discord channel as a message with quick-reply buttons (or a multi-select dropdown when `multi_select = true` on the tool call).

## Posting from an agent — `builtins.gateway.post`

When a gateway is configured, agents gain a built-in tool, **`builtins.gateway.post`**, for posting a message to the gateway's channel. Use it to send a heads-up, status update, or summary to the team mid-mission.

```hcl
agent "announcer" {
  model = models.anthropic.claude_sonnet_4
  tools = [builtins.gateway.post]
}
```

**The gateway owns the message contract.** Each gateway advertises (via the SDK's `MessageToolSpec`) the tool's description and a JSON Schema for its parameters, so the LLM sees exactly the rich-message shape that gateway supports — squadron just forwards the agent's payload through. See your gateway's README for the exact fields it accepts.

A gateway that advertises no spec falls back to a simple `{ message, channel }` shape. The tool is one-way (the agent doesn't wait for a reply); for interactive prompts use `builtins.human.ask` instead.

**Attachments are local files.** Independent of the gateway-owned fields, squadron adds an `attachments` parameter (present only when the mission has [memory or a scratchpad](/missions/memory)). It takes a list of `{ slot, path }` objects referencing squadron's own files — for example `{ "slot": "scratchpad", "path": "report.pdf" }`. Squadron reads each file and ships the bytes to the gateway to upload. Attachments are **never** URLs the model picks, so there is no outbound-fetch (SSRF) surface; the per-file cap is 25 MB.

> **Tip:** rich payloads (Block Kit `blocks`, Discord `embeds`) are large nested JSON. Smaller models sometimes drop the nested field and post text only. If an agent needs to emit rich layouts reliably, give it a capable model.

`builtins.gateway.post` is only a valid tool reference **when a gateway block is present** — `squadron verify` rejects it otherwise.

## Fields

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `name` (label) | string | yes | Used in logs and the install path (`.squadron/gateways/<platform>/<name>/<version>/gateway`). |
| `source` | string | yes (unless `version = "local"`) | GitHub source path. Squadron downloads the matching release archive on first load. |
| `version` | string | yes | Release tag (e.g. `v0.0.1`) or `local` to use a developer-built binary. |
| `settings` | map | no | Free-form key/value map passed to the gateway's `Configure`. Each gateway documents its own keys. |

## Lifecycle

- `squadron verify` pre-flights the install (download + checksum verify) and exits non-zero if the gateway can't be installed — same fail-loudly behavior as plugins.
- `squadron engage` re-runs the install pre-flight before the daemon signals ready, so a broken gateway fails the `engage` command instead of leaving you with a half-running daemon.
- Once installed, Squadron launches the gateway as a managed subprocess at startup (same install model as native plugins).
- A watchdog polls the subprocess and **automatically restarts** on crash with exponential backoff (capped at 30s).
- The SDK includes orphan prevention — if the parent Squadron process dies, the gateway exits on the next watchdog tick rather than lingering.
- On reconnect, the gateway uses a checkpoint (timestamp of the last event it processed) to catch up via `ListHumanInputs(Since=…)` so no questions are dropped during a transient disconnect.

## Local Development

Use `version = "local"` to skip the GitHub download and use a binary you've placed at the cache path:

```hcl
gateway "discord" {
  version = "local"
  settings = { ... }
}
```

Build and install:

```bash
go build -o gateway .
mkdir -p ~/.squadron/gateways/darwin-arm64/discord/local
mv gateway ~/.squadron/gateways/darwin-arm64/discord/local/gateway
```

Adjust the platform path to match `runtime.GOOS-runtime.GOARCH`.

## Available Gateways

- [`squadron-gateway-discord`](https://github.com/mlund01/squadron-gateway-discord) — bridges questions to a Discord channel.

## Building Your Own

Gateways are Go programs built against [`squadron-gateway-sdk`](https://github.com/mlund01/squadron-gateway-sdk). See the SDK README for the `Gateway` interface, hello-world template, catch-up patterns, and the release-archive shape Squadron expects when distributing your gateway.
