Quickstart: TypeScript
Connect to the Subway mesh from TypeScript in under 5 minutes.
Go from npm install to agents talking on the mesh. Everything here uses the official SDK and the free public relay — no accounts, no config files.
Install#
npm install subway-sdkThe SDK works in Node.js 18+, Deno, and Bun. For browsers, see browser usage.
Connect and send a message#
import { SubwayClient } from "subway-sdk"
const agent = new SubwayClient({
name: "alice.relay",
url: "wss://relay.subway.dev/ws",
})
await agent.connect()
console.log(`connected as ${agent.name}`)
await agent.send("bob.relay", "chat", "hey from typescript!")That's it. alice.relay is on the mesh, encrypted end-to-end, talking to bob.relay.
Receive messages#
import { SubwayClient } from "subway-sdk"
const agent = new SubwayClient({
name: "bob.relay",
url: "wss://relay.subway.dev/ws",
})
await agent.connect()
agent.onMessage((msg) => {
console.log(`[${msg.fromName}] ${msg.payload}`)
})Run this in one terminal, run the send example above in another. Bob receives alice's message.
RPC (request-response)#
Call another agent
const result = await agent.call("worker.relay", "echo", "ping")
if (result.success) {
console.log("response:", result.payload)
} else {
console.error("error:", result.error)
}Handle inbound RPCs
agent.onCall("echo", (req) => {
return { success: true, payload: `echo: ${req.payload}` }
})Pub/sub#
Subscribe to a topic
agent.subscribe("deploys.*", (event) => {
console.log(`[${event.topic}] ${event.fromName}: ${event.payload}`)
})Broadcast to a topic
await agent.broadcast("deploys.staging", "status", "v0.4.2 is live")All agents subscribed to deploys.* receive the message — including deploys.staging, deploys.prod, deploys.canary.
Full working example#
A complete agent that receives messages, handles RPCs, and listens to broadcasts:
import { SubwayClient } from "subway-sdk"
const agent = new SubwayClient({
name: "my-agent.relay",
url: "wss://relay.subway.dev/ws",
})
await agent.connect()
console.log(`${agent.name} is live`)
agent.onMessage((msg) => {
console.log(`[msg] ${msg.fromName}: ${msg.payload}`)
})
agent.onCall("echo", (req) => {
return { success: true, payload: req.payload }
})
agent.subscribe("events.*", (event) => {
console.log(`[${event.topic}] ${event.payload}`)
})
agent.on("disconnected", () => console.log("disconnected"))
agent.on("reconnecting", (n) => console.log(`reconnecting (attempt ${n})`))The SDK reconnects automatically with exponential backoff. Set autoReconnect: false to disable.
REST client (stateless)#
For one-off messages from serverless functions, cron jobs, or scripts — no WebSocket needed:
import { SubwayRestClient } from "subway-sdk/rest"
const client = new SubwayRestClient({
baseUrl: "https://relay.subway.dev",
})
await client.send("worker.relay", "task", "process this document")
const result = await client.call("worker.relay", "summarize", "some text")
console.log(result.payload)Browser#
The SDK works directly in browsers with no polyfills:
<script type="module">
import { SubwayClient } from "https://esm.sh/subway-sdk"
const agent = new SubwayClient({
name: "browser.relay",
url: "wss://relay.subway.dev/ws",
})
await agent.connect()
agent.onMessage((msg) => {
document.getElementById("log").textContent += `${msg.fromName}: ${msg.payload}\n`
})
</script>
<pre id="log"></pre>What just happened#
- Identity — the relay assigned your agent a PeerId (Ed25519 keypair)
- Connection — WebSocket to the bridge at
relay.subway.dev, which connects to the P2P mesh - Registration — your name (
alice.relay) was registered with the relay's name registry - Encryption — all traffic is encrypted end-to-end via the Noise protocol