# EdgeGuard managed Origin agent

The Agent is a small Rust binary for customer-hosted Origins. It uses the
existing portal account for device approval; there is no separate dashboard.

## Enrollment

```bash
sudo edgeguard-agent discover
sudo edgeguard-agent connect
```

The Agent displays a ten-minute user code. The customer enters that code in the
existing portal, reviews the detected hostname and selects the tenant. The
device and permanent secrets are generated locally. The API stores only their
SHA-256 hashes, and the permanent secret never crosses a browser session.

Credentials are stored as root-only
`/var/lib/edgeguard-agent/credentials.json`. The periodic report contains only
derived service facts: server type, configuration path, domains, listeners,
upstreams and webroots. It never includes complete nginx/Caddy configuration
contents or private keys. Revoking an Agent in the portal immediately rejects
future reports.

The current discovery MVP is read-only and understands nginx and Caddy. Config
generation, explicit customer selection, validation and rollback are separate
future steps; the Agent does not mutate a web server merely because it found
one.

## Outbound Origin tunnel

After enrollment and the first valid route sync, the package automatically
starts `edgeguard-agent-tunnel.service`. The Agent first opens an outbound
QUIC/UDP 443 connection to `tunnel.stgon.cloud`. If UDP or QUIC is blocked, it
automatically falls back to a multiplexed TLS/TCP 443 connection using ALPN
`edgeguard-tunnel-tcp/1`; customers do not need inbound firewall rules, a
public IP or tunnel endpoint configuration.

The machine channel requires TLS 1.3 with ML-KEM-1024. The default `metered`
profile keeps one connection warm, uses a 30-second transport keepalive and
rotates all current DNS addresses only after failure. There is no separate
application heartbeat. `redundant` keeps one connection per configured
endpoint warm. For development only, endpoints can be overridden with the
`EDGEGUARD_TUNNEL_ENDPOINTS` environment variable or command arguments.
An operator can pin the dial address without weakening certificate validation by using
`tunnel.stgon.cloud@192.0.2.10:443`; the name before `@` remains the TLS server name and the
address after it is used only for routing.

QUIC and TCP carry the same bounded protobuf heads and streaming bodies. TCP
uses HTTP/2 DATA streams solely as a bounded, flow-controlled multiplex layer;
normal browser HTTPS on the shared port remains selected by `h2` or
`http/1.1`. Both tunnel transports require the machine-only ML-KEM-1024 TLS
profile.

The local hop remains streaming and can use HTTP or HTTPS. HTTPS defaults to
public-CA verification (`strict`); `encrypt_only` is an explicit per-route
option for self-signed local Origins and never silently downgrades to plaintext.

## APT repository

`repo.stgon.cloud` is a static signed APT repository backed by EdgeGuard/S3. It
does not run an application or a second portal.

```text
deb [signed-by=/usr/share/keyrings/stageone-edgeguard.gpg] https://repo.stgon.cloud/edgeguard stable main
```

The repository publisher requires a dedicated OpenPGP signing key via
`EDGEGUARD_APT_SIGNING_KEY`. The private key is never placed in the repository
or S3. Publish the resulting `dist/apt-repository/edgeguard/` tree as immutable
artifacts; clients authenticate `InRelease` before trusting package hashes.

## Alpine Linux / OpenRC

The Agent is also built as a static musl binary. The Alpine bundle contains the binary, an OpenRC
service for the permanent tunnel, and a 15-minute discovery report installed through Alpine's
periodic scheduler. Build it with:

```bash
deploy/packages/build-agent-alpine.sh
```

Copy and unpack the resulting `edgeguard-agent_*_alpine_*.tar.gz` on the target, then run
`doas ./install.sh` (or `sudo`). Enroll before enabling the tunnel:

```bash
doas edgeguard-agent connect
doas rc-update add edgeguard-agent default
doas rc-service edgeguard-agent start
```

The target needs `openrc`, `ca-certificates`, and a running `crond`. It does not need glibc,
OpenSSL, Rust, Docker, or inbound firewall rules. Agent credentials and desired routes remain
root-only under `/var/lib/edgeguard-agent`.

Release artifacts are exposed through EdgeGuard itself. Versioned URLs are immutable; `stable`
tracks the currently recommended release:

```text
https://agent.eg.stgon.cloud/install.sh
https://agent.eg.stgon.cloud/v0.1.4/x86_64/edgeguard-agent.tar.gz
https://agent.eg.stgon.cloud/stable/x86_64/edgeguard-agent.tar.gz
https://agent.eg.stgon.cloud/stable/x86_64/SHA256SUMS
```

The preferred installation path detects the CPU architecture, Linux distribution and init system,
verifies the static release binary, and installs either hardened systemd units or OpenRC services:

```bash
curl -fsSL https://agent.eg.stgon.cloud/install.sh | sudo bash
```

The installer asks for the local Origin URL, starts the tenant-bound device enrollment, and starts
the service after approval. Public domain and route selection remain in the customer portal. For
non-interactive provisioning, pass the same value as `EDGEGUARD_AGENT_ORIGIN_URL`.

The static musl build is shared across distributions, avoiding glibc-version coupling. Existing
credentials and routes under `/var/lib/edgeguard-agent` are preserved during upgrades.

Run `deploy/packages/publish-agent-downloads.sh` after building to prepare the versioned and stable
object tree. The one-line installer is the preferred installation path. Bundles and standalone
binaries remain available for operators who manage deployment themselves.
