Embed in your app
Bundle the beamd binary into a host app, drive it with a dedicated config, and reconcile tunnels on startup.
This is for building Beamd into a host app — for example an AI task manager that exposes each agent's dev server for remote review. You bundle the binary, point it at your edge with a dedicated config, and manage the bring-up / tear-down lifecycle yourself.
Mental model
- One edge runs on a public box and owns a domain.
- Your app machine runs the client:
beamd open <port> -dhands a tunnel to a background agent that holds it and returns immediately. - Your app is the source of truth for what should be exposed. The agent doesn't persist registrations across reboots — on startup, re-open what you want (cheap and idempotent).
1. Bundle the binary
Add Beamd as a dependency — npm installs only the host-platform binary (~4 MB):
npm i @beamd/cliSpawn it via the package bin (node_modules/.bin/beamd) or
require.resolve("@beamd/cli/bin/beamd.cjs"). Everything below is just
beamd <args>.
2. Configure with --config
Pass a dedicated config file to every command. It bypasses Beamd's account
store entirely, so your app keeps its own {server, token} out of the user's
$HOME and never collides with their interactive beamd accounts. The token
is a workspace API key (hosted) or an operator-issued token (self-host):
# my-app-beamd.yaml — referenced via --config my-app-beamd.yaml
server: tunnel.example.com:443
token: <workspace API key>
agent_socket: /path/to/your-app/beamd-agent.sock # pin a dedicated socket
# insecure_skip_verify: true # ONLY for a self-signed dev edgePin a dedicated
agent_socket. The agent is keyed by its socket path. Without one, your app shares the default socket with the user's ownbeamd, and an agent already running there keeps serving its original credentials even after you changeserver/token. A per-app socket isolates you — and if you rotate creds, runbeamd reload --config <path>to restart the agent with them.
3. Bring a tunnel up (detached + JSON)
beamd open 3000 --as my-app -d --json
# { "url": "https://my-app.<base>", "name": "my-app", "port": 3000, "slug": "", "baseDomain": "<base>" }-d detaches; --json prints exactly one object. Parse url, embed or open
it — and always trust url rather than assembling the host (it's correct
whether the edge is flat or namespaced).
4. Tear down
beamd close my-app --json # { "name": "my-app", "removed": true|false }Idempotent: exit 0 whether or not it existed. Never spawns an agent.
5. Reconcile on startup
After a reboot the agent isn't running and holds nothing. Reconcile against your own desired state:
beamd status --json # { "agentRunning": bool, "server": ..., "slug": ..., "scope": ..., "healthy": bool }
beamd list --json # [ { "name", "url", "port", "healthy" }, … ]For anything you want exposed that isn't listed, beamd open … -d again (the
first detached open spawns the agent automatically).
Two constraints to design around
- Names are one label.
--as <name>must be a single RFC 1123 label — encode structure with hyphens, not dots (proj-ab12-api, nota.b). See Naming. - There's a tunnel cap. The edge caps concurrent tunnels per token
(
max_tunnels_per_token, default 25). If you expose many short-lived previews,closethe ones you no longer need.
For the raw socket contract under these commands, see Agent local API.