Agent Uplink

DEVELOPMENT DOCUMENTATION / JOIN A RELAY

Connect your computer to a relay someone already runs.

For a tester whose operator already runs a relay, such as the project's alpha relay at agentuplink-relay.fly.dev. You install the device half on Linux or macOS, get a certificate, connect, and call the synthetic echo. You do not run a relay, Redis or an identity issuer.

To run your own relay, follow Setup instead. Windows cannot create or import device credentials in this alpha.

What you exchange with the operator

WhenFromWhat
Before you startOperatorThe device URL, for example wss://agentuplink-relay.fly.dev:9443/v1/tunnel/control, and the consumer URL, for example https://agentuplink-relay.fly.dev.
Before you startOperatorThe relay CA certificate, relay-ca.pem. The relay uses a private CA.
Before you startOperatorYour device UUID, your echo service UUID, and the release tag to install.
Step 3YouYour certificate signing request, device.csr. Never the private key.
Step 4OperatorYour signed device certificate, once your user, device, service and grant are in the relay catalog.
Step 6OperatorA consumer token, or access to their issuer, with your user's sub and echo:invoke in its scope.

1. Download and verify

Download the release your operator names from GitHub releases. On an Apple silicon Mac:

mkdir -p ~/agentuplink && cd ~/agentuplink
gh release download TAG -R andymac4182/agentuplink -p '*aarch64-apple-darwin*'
shasum -a 256 -c agentuplink-*.tar.gz.sha256
mkdir release && tar -xzf agentuplink-*.tar.gz -C release && cd release
cat release.json
export PATH="$PWD/bin:$PATH"
tunnel-client --version

On Linux use the x86_64-unknown-linux-gnu archive and sha256sum -c. The checksum proves integrity, not authorship. Releases built after the release-hardening change also carry a build attestation: gh attestation verify agentuplink-*.tar.gz -R andymac4182/agentuplink --signer-workflow andymac4182/agentuplink/.github/workflows/release.yml --source-ref refs/heads/main checks that the file was built by this repository's release workflow on main (older releases answer HTTP 404); see Downloads for what that does and does not prove. The archive has no top-level folder, which is why it is unpacked into release/; archives built after M6-C50 also carry docs/. README.txt in newer archives says to start with docs/operator.md: that guide is for running a relay, so follow this page instead. release.json names the source commit; archives built after M6-C102 carry every example their documents name; older ones hold only m1-client.toml and m1-relay.toml. On macOS, a browser download is quarantined: after the checksum passes, run xattr -dr com.apple.quarantine bin. Keep the three binaries together. The --help heading of releases up to f9f7abf is stale ("M1 connector"); in those releases a subcommand's --help exits 2; releases built after the D6 fix exit 0.

2. Write the device profile

mkdir -m 700 ~/agentuplink-device
cp examples/m1-client.toml ~/agentuplink-device/client.toml
cp /path/to/relay-ca.pem ~/agentuplink-device/relay-ca.pem

Edit client.toml and change three values, keeping the rest of the example:

device_id = "DEVICE_UUID"
relay_url = "wss://RELAY_HOST:9443/v1/tunnel/control"

[exports."SERVICE_UUID"]
type = "echo"
device_canary = "my-device"

The device and service UUIDs must be exactly the operator's. device_canary is any short string the echo returns before your payload. Then run tunnel-client config check --config ~/agentuplink-device/client.toml and expect Runtime client configuration is valid.

3. Create your key and request

tunnel-client credentials create --config ~/agentuplink-device/client.toml --csr-out device.csr

Paths resolve from the profile's directory, so this writes ~/agentuplink-device/device.csr and an owner-only key at ~/agentuplink-device/credentials/device-key.pem. Send the operator device.csr only.

4. Import your certificate

Save the certificate you receive as ~/agentuplink-device/device-cert.pem:

tunnel-client credentials import --config ~/agentuplink-device/client.toml --certificate device-cert.pem --server-ca relay-ca.pem
tunnel-client doctor --config ~/agentuplink-device/client.toml

Expect Imported 1 client certificate(s) and 1 server CA certificate(s). and a healthy local doctor, exit 0. Its second line, Supervisor IPC: not implemented in this operations slice., is expected, not an error. doctor does not contact the relay. An import refusal (X.509 v1, no device SAN, another device, or a key mismatch) needs the operator to sign again.

5. Connect

tunnel-client connect --config ~/agentuplink-device/client.toml --json

It runs in the foreground and reports "state":"ready", then "phase":"active". The device uses the operator's configured outbound port, 9443 in this example. Default data rotation is every 300 seconds; network loss triggers reconnect with backoff. Ctrl-C requests an orderly stop. Exit 7 OWNER_BUSY can mean another connector or a retained owner lease; exit 3 CREDENTIAL_ERROR requires checking enrollment. Investigate exit 1 PROTOCOL_ERROR with the operator rather than blindly retrying. Older releases through f9f7abf could misclassify one missing-ACK timeout this way; current main treats that narrow case as retryable transport loss. Reconnect never proves an interrupted operation did not run.

6. Call the echo

From any machine with relay-ca.pem and a token. read -rs keeps the token out of history and off screen:

read -rs TOKEN
curl --cacert ~/agentuplink-device/relay-ca.pem \
  -H "Authorization: Bearer $TOKEN" --data-binary hello \
  https://RELAY_HOST/v1/devices/DEVICE_UUID/services/SERVICE_UUID/echo
AnswerMeaning
200, my-devicehelloThe tunnel works: the device canary followed by your payload.
503 DEVICE_OFFLINE, not dispatchedThe device is not connected.
401No token, or a token not accepted: expired, wrong issuer, audience or key, or an unknown user.
403A valid token without echo:invoke. Relays built before this was split out, including the one serving agentuplink-relay.fly.dev on 2026-09-25, answer 401 instead.
404 SERVICE_NOT_FOUNDWrong service UUID, or no grant for your user.
503 RESOURCE_EXHAUSTED, not dispatchedSeen for about half a second at a data-socket rotation. The request did not run; repeat it.
503 REVERSE_CHANNEL_INTERRUPTED, unknownThe connection broke mid-request. Repeating an echo is harmless; check side-effecting services first.
curl error 60--cacert is missing or names the wrong CA.

When you are done

Stop connect and ask the operator to revoke your device. A revoked device cannot be reactivated. Delete ~/agentuplink-device to remove the private key.

Source & review

Reviewed against cb94dc3ccbf4 on 2026-09-25. Based on docs/join-relay.md, scripts/package_release.py, crates/tunnel-client/src/main.rs. Historical test evidence is not a new test run.