Run Kiro Crew on remote development hosts, EC2 instances, home servers, or disposable Fargate tasks and reach them from one local dashboard. Long-lived instances expose a loopback Gateway through an SSH or AWS SSM port forward. Fargate exposes a task-scoped turn API through AWS SSM, with no inbound network rule.
Opt-in. Off by default.
Set instances.enabled to true and restart the Gateway, connect a crew under Settings → Remote Crew, then enable Settings → Developer → Feature Previews → Remote crew sessions. A remote crew is another machine you have connected, not one of your agents.
With the preview on, the Sessions list puts Local first, then one collapsible group per connected crew. Each group header carries an Online, Reconnecting, Error, or Offline badge, so you can see which machines are reachable before you open a session. Collapsing a group hides its rows without changing your selection. A dashboard with no connected crews shows no group headers.
Opening a session from a crew group opens a window onto that crew's own session. The transcript, the running state, and any pending approval are read from the peer through the hub's proxy, and every action (send, stop, approve, continue, regenerate, rewind) calls the peer's own route. Nothing from the session is stored on this machine beyond an in-memory cache.
Both machines must run the same Kiro Crew version. On a mismatch the window names the version each side runs; update one so they match.
Auto-connect behavior is under Settings → Remote Crew → Auto-connect crews.
Before 0.9, the Chat on a crew preview ran each turn of a local chat on the selected remote crew through a turn relay while the transcript stayed on this machine. That relay no longer exists. A chat an older build ran through it still opens, read-only: sending, regenerating, and any other action that would start a turn is refused with a notice that the chat ran on a remote crew. To continue that work, open the crew's own session through Remote crew sessions.
kirocrew config set instances.enabled true
The Gateway reads instances.enabled at startup, so restart it after changing the value. On restart it creates the instance registry and starts managing eligible connections.
Use a hostname, user@host, or SSH config alias. Authentication must work without an interactive prompt, normally through ssh-agent or a configured identity file.
Use an EC2 instance ID or SSM managed-instance ID with an AWS profile and region. The local machine needs the AWS CLI and Session Manager plugin, and the remote instance needs SSM Agent plus the required IAM permissions. The connection uses port forwarding and requires no inbound SSH rule.
Fargate is intended for disposable remote workers. The remote crew runs as an ECS task and is reached through an SSM forward to its turn API, with no public listener or embedded remote dashboard. Configure the Fargate placement, digest-pinned image, and secret references in the optional fargate block of cloud.json; the Cloud surface shows the connection and task state.
A task lives for six hours by default. Set fargate.task_ttl_seconds to choose a different positive lifetime. The runtime admits at most ten Fargate crew tasks at once; this safety ceiling is fixed rather than a cloud.json setting.
The laptop that connects needs the AWS CLI and Session Manager plugin. Fargate uses the same owner-only remote-crew controls as the other connection methods.
With instances.enabled on, the built-in EC2 launch form under Settings → Remote Crew accepts an optional Subnet ID. Supplying one places the crew in that subnet and its VPC. Kiro Crew checks for a NAT or internet-gateway route before launch and stops with the missing route named when the subnet has no internet egress.
┌── Hub gateway (this host) ──┐ │ /instances page (React) │ │ ├─ tab strip: Home · A · B │ │ ├─ warm <iframe>s per tab │ http://127.0.0.1:<local_port>/?token=… │ └─ Manage panel │ add / connect / diagnose / restart / remove │ │ │ instances/ package │ │ ├─ registry │ ~/.kiro/crew/instances.json │ ├─ port_allocator │ loopback ports from base 7778 │ ├─ token_mint │ ssh <host> kirocrew token → JWT (never logged) │ ├─ ssh_tunnel_manager │ supervised ssh -N -L, probe, self-heal, refresh │ └─ diagnostics │ ssh → remote-dashboard → local-forward ladder └──────────────────────────────┘ │ ssh -N -L 127.0.0.1:<local>:127.0.0.1:<remote> ▼ ┌── Remote gateway ────────────┐ │ kirocrew gateway bound to │ │ 127.0.0.1:<remote_port> │ └──────────────────────────────┘
Each instance you add produces a supervised ssh -N -L child. The tunnel forwards 127.0.0.1:<local> → remote 127.0.0.1:<remote>. The hub mints a dashboard token on the remote over SSH and embeds the remote dashboard in an iframe at the loopback URL.
POST /api/instances/{id}/connect allocates a loopback port, mints a token on the remote over SSH, starts ssh -N -L, waits until the local forward accepts a connection, then returns the live statusinstances.warm_set_cap to a positive value for a fixed limit. Warm instances keep a live tunnel + WebSocket. Connecting beyond the cap lazily evicts the least-recently-used?diagnose=1 runs a failure-probe ladder on demand; POST .../restart restarts the remote gateway over SSHOpen Settings → Remote Crew, select Add remote crew, and choose the connection method.
| Method | Target fields |
|---|---|
| SSH | Hostname, user@host, or SSH config alias, plus the remote Gateway port |
| AWS SSM | EC2 instance ID (i-*) or managed-instance ID (mi-*), AWS profile, region, and optional run-as user |
| Fargate | The registered ECS task target created by the Cloud launch flow, plus AWS profile and region |
All methods take a display name. Long-lived SSH and SSM Gateways also use a dashboard-token lifetime. A value beginning with - or a target that does not match its method is refused before a child process starts.
Use your SSH config alias or user@hostname. Authentication must succeed non-interactively through ssh-agent or configured keys.
Configure an SSH alias on the hub, then use that alias as the SSH target:
Host my-ec2 HostName ec2-1-2-3-4.compute-1.amazonaws.com User ec2-user IdentityFile ~/.ssh/my-key.pem ProxyJump bastion-host
For an EC2 instance with no inbound SSH port, choose AWS SSM directly or use an SSM ProxyCommand in the SSH alias.
Prerequisites on the hub:
ssh-agent holding it (BatchMode won't prompt)kirocrew installed and a gateway running on the EC2 instance's loopback port| Need | Status | How |
|---|---|---|
| Custom login user | ✅ | user@host or ssh-config User |
| FQDN / IP | ✅ | direct ssh_host value |
Identity file (-i) | ⚠️ via ssh config only | IdentityFile in a Host block |
| Non-22 SSH port | ⚠️ via ssh config only | Port in a Host block |
| Bastion / ProxyJump | ⚠️ via ssh config only | ProxyJump / ProxyCommand |
| SSM-only instances | ⚠️ via ssh config only | ProxyCommand with aws ssm start-session |
A remote dashboard can expose remote crews of its own. When you connect one inside that pane, your local dashboard adopts it as a top-level switcher tab. The tab is indented under the parent crew whose SSH tunnel carries it, and becomes unavailable when that parent is down.
A chain supports two hops, from your dashboard through one SSH-connected crew to another. One parent can carry at most eight chained crews. Removing the parent removes its chained entries from your dashboard.
The Manage panel gives you these per-instance actions:
Every route in /api/instances/* is gated by _guard():
request["user"] (authenticated dashboard session)instances.enabled: trueBeyond the guard:
ssh -N -L 127.0.0.1:<local>:127.0.0.1:<remote>ssh is always invoked with an argv list; ssh_host cannot inject local shell syntaxssh_host and remote_bin rejected if they contain shell metacharacters or lead with -event.origin against the exact http://127.0.0.1:<port> of a currently-warm tunnel before trusting an unread-count messageFor instances.enabled and the other multi-instance settings, see Configuration.
| Symptom | Fix |
|---|---|
/instances shows "multi-instance management is off" | instances.enabled is false — set it and restart |
| Iframe is blank | CSP frame-src relaxation only applies to active tunnel ports; ensure the instance is connected |
| Connect fails with SSH auth error | Refresh your SSH credentials (re-add key to ssh-agent); tunnels self-heal once SSH is restored |
| Connect fails | Use Diagnose — the ladder reports the first broken link (ssh_unreachable, remote_down, or tunnel_down) |
| Instance keeps dropping | Health probe + 2-tier self-heal retry over ~2 min; if it gives up, diagnosis runs automatically. Check the remote gateway and SSH stability |
| An instance silently disappeared from the warm set | LRU-evicted (warm set full). Raise instances.warm_set_cap or reconnect on demand |
Multi-instance