# Development

> HTML: https://remote.ivjn.us/docs/en/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:

```bash
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`):

```bash
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

```bash
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:

```rust
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.

```bash
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:

```powershell
$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.

```sh
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:

```sh
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

- [Technical overview](/docs/en/architecture.md): outbound connections, session translation, epochs, bounded queues, and token storage
- [Usage](/docs/en/getting-started.md): login, adding devices, starting the Agent, commands, and shells
- Deployment guide: repository `docs/deployment.md` (contains server addresses; not published on this site)
- [Wire protocol](/docs/en/websocket-protocol.md)
- [Multiplatform device deployment](/docs/en/platform-installation.md)
