Build Server & Connection Setup
The build server is a public, stateless HTTP service. It issues build permissions, brokers WebRTC connection setup, and assigns jobs to build machines. It does not see the contents of any build, and does not have the ability to read user data flowing over WebRTC channels.
Endpoints
The build server exposes a small REST surface:
| Method | Path | Purpose |
|---|---|---|
POST | /grant/request | Request a build permission. |
POST | /signaling/{job_id}/offer | Submit a WebRTC SDP offer. |
POST | /signaling/{job_id}/candidate | Submit an ICE candidate. |
GET | /signaling/{job_id}/events | Live updates stream of the build machine’s answer and candidates. |
GET | /health | Liveness check. |
GET | /metrics | Prometheus metrics. |
There are also /agents/* and /services/* endpoints used by build
machines and operators, but those aren’t part of the user-facing build flow.
A permission request in detail
POST /grant/request
{
"peer_fingerprint": "sha-256:XX:XX:...",
"required_channels": ["espctl", "pty", "firmware"],
"cpu_cores": 2.0,
"memory_mb": 1024,
"timeout_secs": 600
}
| Field | Meaning |
|---|---|
peer_fingerprint | The SHA-256 certificate fingerprint of the requesting peer. The build server embeds this in the permission token so the build machine can later verify “the peer I’m talking to is the same peer the permission was issued to”. |
required_channels | The exact list of WebRTC data channels the requester needs. The build machine will refuse to open any channel not in this list. |
cpu_cores / memory_mb | Resource requirements used to schedule the job to a capable build machine. |
timeout_secs | How long the session is allowed to live. Capped at the operator’s policy (typically 5-30 seconds for permission tokens, longer for build sessions specifically). |
The build server responds with:
{
"job_id": "01H...uuid",
"grant": "<signed permission token>",
"candidates": ["agent-id-1", "agent-id-2"],
"ice_servers": [
{ "urls": "stun:stun.example.com:3478" },
{
"urls": "turn:stun.example.com:3478?transport=tcp",
"username": "...",
"credential": "..."
}
],
"expires_at": 1712340060
}
grant is a signed permission token (a digital signature over an
encoded body) that names the user, the allowed channels, the
bandwidth/rate limits, and the expiration time. The build machine verifies
this signature locally before honoring the permission – see
Permissions & Security.
Connection setup – what the build server does and doesn’t see
The build server relays the SDP offer/answer and ICE candidates between
the two peers. It does not parse the contents – it stores the body as
raw bytes, broadcasts them on the live updates stream for the matching
job_id, and discards the state after the session ends (default 60 seconds
TTL).
What the build server sees:
- That a session was requested at time T by peer with fingerprint X.
- That an SDP offer of N bytes was posted.
- That an SDP answer of M bytes was relayed.
- A handful of ICE candidates with their addresses (the candidate addresses are the only network metadata exposed; this is fundamental to how WebRTC works).
What the build server does not see:
- The build request body (it’s encrypted on the data channel).
- The build logs (data channel).
- The firmware binary (data channel).
- The contents of the user’s project files (data channel).
Job assignment
When a build permission is issued, the build server runs a small scheduler to pick which build machine should run the job. Inputs to the scheduler:
- Liveness – build machines send heartbeats every few seconds; only live machines are candidates.
- Capacity –
cpu_coresandmemory_mbfrom the permission request. - Capabilities – does this build machine have the requested IDF version installed? Does it support the requested chip target?
- Past performance – a small “learning engine” prefers build machines that recently ran similar jobs successfully.
The chosen build machine receives the permission via live updates and is then responsible for opening its half of the WebRTC peer connection.
Operator concerns
If you’re running your own build server:
- CORS: the
/grant/*and/signaling/*endpoints are called from a browser, so CORS must allow the relevant origins. EditALLOWED_ORIGINSin/etc/aegis/control.envand restart the service. - TLS: the public endpoint should be behind HTTPS in production. esphome.cloud uses Caddy as a reverse proxy with automatic Let’s Encrypt certificates; the IP-mode setup uses plain HTTP on port 80 (development only).
- Relay ports: the fallback relay needs UDP ports 49152-49231 open for peer traffic, and TCP+UDP 3478 for the relay control channel.
- Health monitoring:
GET /healthreturns 200 + a small JSON blob;GET /metricsreturns Prometheus-format metrics.
See the aegis repo’s deploy/ directory for full systemd unit files,
Caddyfile templates, and the provision-store.sh script.
See also
- WebRTC Build Machine & Data Channels – what happens once the data channels are open.
- Permissions & Security – what’s actually signed and verified.