remote.DocumentationHome

Development

This document explains how to build, test, and run exchange locally in this repository. Deployment steps, protocol field tables, and platform installation details remain in the dedicated documents linked at the end.

Toolchain

The workspace uses Rust 2021 and requires Rust 1.88 or newer. The version is recorded in the rust-version field of the root Cargo.toml.

Workspace and binaries

The Rust workspace has nine members: eight under crates/ and the extension SDK under sdk/extend/rust. Protocol types are defined only in crates/proto; exchange, agent, and cli depend on this crate. Do not duplicate message structures in individual consumers.

crate Path Output
proto crates/proto Library; control messages, binary frames, and shared data structures live only here
exchange crates/exchange exchange binary (same package and binary name)
agent crates/agent remote-agent binary
cli crates/cli remote-cli binary
updater crates/updater Self-update library for remote-agent and remote-cli
telemetry crates/telemetry Shared exception and log reporting library for all three components
tls-client crates/tls-client Shared rustls client TLS policy and optional proxy connections
worker-proto crates/worker-proto Protocol validation and credential hashing for Workers Exchange
remote-extend-sdk sdk/extend/rust Library; extension provider framing and contracts

The entry point for exchange is crates/exchange/src/main.rs, without a separate [[bin]] table, so cargo build -p exchange produces exchange. The agent and cli manifests (Cargo.toml) explicitly name their binaries remote-agent and remote-cli.

The connection tables are split by responsibility:

  • crates/exchange/src/hub.rs manages online devices, reconnect replacement, and epochs.
  • crates/exchange/src/hub/session_table.rs translates operator-side and device-side IDs.
  • crates/exchange/src/hub/outbound.rs manages the bounded send queue for each connection.

Build

Run these commands from the repository root.

The release profile (opt-level = 2, thin LTO, unwind retained) builds the server, CLI, and Agent:

cargo build --release

Binaries are located at:

  • target/release/exchange
  • target/release/remote-cli
  • target/release/remote-agent

release retains unwinding because exchange is a persistent process with multiple connections. If a connection task panics, unwinding lets tokio stop that task without taking down sessions for other devices in the same process.

The device Agent also has a size-optimized profile (opt-level = "z", LTO, panic = "abort", strip, codegen-units = 1):

cargo build -p agent --no-default-features --profile release-small

The output is target/release-small/remote-agent. Because procd, systemd, or Task Scheduler restarts the Agent after a crash, this profile can trade panic = "abort" for a smaller binary.

The database is not required for compilation. crates/exchange/src/db.rs uses sqlx runtime queries rather than query! macros, so cargo build works without a database.

Tests

cargo test --workspace

Windows skips tests requiring Unix /bin/sh (or /bin/ash on OpenWrt) and PTYs. The test module in crates/agent/src/pty.rs is guarded by #[cfg(all(test, unix))] and is not compiled on non-Unix platforms. The PTY implementation tries /bin/ash and then /bin/sh. Process-group and Unix signal paths in exec.rs also compile only on Unix; Windows command tests use PowerShell without invoking /bin/sh. After changing Unix paths in exec.rs or pty.rs, run cargo test --workspace on Linux as well; Windows results alone are insufficient.

Narrow the scope to one crate when useful, for example cargo test -p proto or cargo test -p exchange -- hub::.

Run exchange locally

Prepare PostgreSQL and put its connection string in DATABASE_URL. This variable maps to the exchange --database-url option (see crates/exchange/src/config.rs). At startup, db::connect runs migrations embedded at compile time:

sqlx::migrate!("../../migrations")

Migration files live in the repository-root migrations/ directory and run automatically at startup; sqlx-cli is unnecessary.

Audit operations also require an audit encryption key. The examples below generate a local test key. Store it securely and reuse the same key on subsequent starts; generating a new key each time prevents decryption of existing audit details.

export DATABASE_URL='postgres://luci:luci@127.0.0.1:5432/luci'
export AUDIT_ENCRYPTION_KEY="$(python -c 'import base64, secrets; print(base64.b64encode(secrets.token_bytes(32)).decode())')"
cargo run -p exchange -- --listen 127.0.0.1:8080

In PowerShell, set the environment variable first:

$env:DATABASE_URL = "postgres://luci:luci@127.0.0.1:5432/luci"
$env:AUDIT_ENCRYPTION_KEY = python -c "import base64, secrets; print(base64.b64encode(secrets.token_bytes(32)).decode())"
cargo run -p exchange -- --listen 127.0.0.1:8080

If the release binary is already built, replace cargo run -p exchange -- above with ./target/release/exchange. The default listen address is 0.0.0.0:8080; explicitly bind to 127.0.0.1 for local debugging.

When the database has no enabled operator account, startup bootstraps an administrator and prints its token in that startup log only. The plaintext appears once. Complete deployment, reverse-proxy, and account instructions are in repository docs/deployment.md. That page contains server addresses and is not published on this site.

Pre-commit checks

Run these checks from the repository root. For Rust changes, start with focused crate tests, then complete workspace checks. If formatting checks find unrelated existing drift, format only changed files instead of reformatting other code. Android, the browser extension and the mobile console also need their own builds and tests; Rust tests do not cover those surfaces.

cargo fmt --all -- --check
cargo clippy --workspace --all-targets -- -D warnings
cargo test --workspace

Maintain documentation

Verify implementation behavior before updating repository guides in docs/. Website topics have a reference source at website/docs/<topic>.md and nine authored translations at website/docs/content/<locale>/<topic>.md. Keep commands, fields, code examples and heading levels aligned. Generate pages and search indexes with the script rather than editing HTML alone. Run the following from the repository root with Python 3.11+, markdown-it-py and Node.js installed:

python -m pip install markdown-it-py
python scripts/build-docs.py
python scripts/build-docs.py --check
python scripts/test-docs.py
python scripts/test-docs-markdown.py
node scripts/test-docs-locale.js
python scripts/test-build-docs.py
python scripts/test-site-seo.py

The generation check also compares every generated file byte for byte, failing on missing or stale output without writing files. CI runs it before any rebuild.

The checks validate translation completeness, code examples, heading structure, page links, anchors and language routing. Commit generated pages with their sources. Building documentation changes local files only; website deployment and download publication are separate operations.

Related documents