Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 33 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,17 +219,18 @@ What RustKVM focuses on:
The full walkthrough, including flashing and wiring, is in
[docs/getting-started.md](docs/getting-started.md).

1. **Build** the frontend and the device binary (next section), or use `dev_deploy.sh`.
2. **Deploy** to the device:
1. **Flash** a firmware image to a microSD card ([docs/quickstart.md](docs/quickstart.md)),
from a release or built yourself (next section). It already runs RustKVM.
2. **Open** `https://<device_ip>/` (or `https://rustkvm.local/`) and accept the
self-signed certificate.
3. **Finish the setup wizard**, then click the video view to take control.
4. **Deploy your own build** (optional): enable SSH under Settings → Security → SSH access
and Settings → System → Developer mode, then

```bash
./dev_deploy.sh -r <device_ip> -u root
./dev_deploy.sh -r <device_ip> # builds in the devkit unless you have stage2
```

3. **Open** `https://<device_ip>/` (or `https://rustkvm.local/`) and accept the
self-signed certificate.
4. **Finish the setup wizard**, then click the video view to take control.

Useful runtime flags (all also settable via `RUSTKVM_*` environment variables, see
`rustkvm_app --help`):

Expand All @@ -251,9 +252,11 @@ scripts/devkit.sh run scripts/build-app.sh # → out/app/rustkvm_app-aarch64-u
scripts/devkit.sh firmware # → out/firmware/ SD and eMMC images
```

Pinned versions, caches, proxies and the firmware build are covered in
[`docs/building.md`](docs/building.md). The manual setup below is what the containers
automate.
Only Docker and git are needed; the images are public and pulled without a login.
Pinned versions, disk space, caches, proxies and the firmware build are covered in
[`docs/building.md`](docs/building.md); renaming and rebranding your build in
[`docs/customizing.md`](docs/customizing.md). The manual setup below is what the
containers automate, and you only need it to work without Docker.

### 1. Frontend (any host)

Expand All @@ -279,8 +282,10 @@ cargo +nightly test --workspace

### 3. Device build (aarch64 / RK3588)

The production binary uses the Buildroot RK3588 host toolchain and a self-built Rust
`stage2` toolchain with `-Z build-std`.
Without Docker, the binary needs the Buildroot RK3588 host toolchain and a nightly Rust
with `rust-src` (`-Z build-std`). The reference workbench uses a self-built `stage2`
toolchain, which `rust-toolchain.toml` names; with the pinned nightly instead, set
`RUSTUP_TOOLCHAIN=nightly-2026-06-14` (this is what the devkit does).

<details>
<summary><b>Step-by-step cross-compilation setup</b></summary>
Expand All @@ -302,21 +307,25 @@ sudo apt install pkg-config gcc-arm-linux-gnueabihf clang llvm-dev libclang-dev
python-is-python3 texinfo kmod cmake
```

**Buildroot host toolchain.** Extract the RK3588 Buildroot SDK on a Linux filesystem
(extracting on Windows breaks its symlinks) and build it:
**Buildroot host toolchain.** Build the firmware once (`scripts/build-firmware.sh`, on a
Linux filesystem; a Windows drive breaks the SDK's symlinks) and copy its host dir:

```bash
mkdir -p buildroot-rk3588-20250527 && tar -xf buildroot-rk3588-20250527.tar -C buildroot-rk3588-20250527
cd buildroot-rk3588-20250527
./build.sh rk3588.mk
sudo mkdir -p /opt/rk3588-buildkit
sudo cp -r "$PWD"/buildroot/output/rockchip_rk3588/host/* /opt/rk3588-buildkit/
sudo cp -r ~/rustkvm-sdk/rk3588/buildroot/output/rockchip_rk3588/host/* /opt/rk3588-buildkit/
```

Or copy it out of the published image:

```bash
docker create --name bk ghcr.io/rust-kvm/rk3588-buildkit:20250527
sudo docker cp bk:/opt/rk3588-buildkit /opt/ && docker rm bk
```

The [`buildkit`](.github/workflows/buildkit.yml) workflow can also build this toolchain
and publish it as the `ghcr.io/<owner>/rk3588-buildkit` container image.

**Rust stage2 toolchain.** In the `rust/` submodule, create `bootstrap.toml`:
**Rust stage2 toolchain** (optional; skip it to use the pinned nightly). In the `rust/` submodule, create `bootstrap.toml`:

```toml
[build]
Expand Down Expand Up @@ -344,6 +353,8 @@ profile sets `panic = "abort"`):
cargo +stage2 build -Z build-std=std,panic_abort \
--target aarch64-unknown-linux-gnu -p rustkvm --bin rustkvm_app --release
# → target/aarch64-unknown-linux-gnu/release/rustkvm_app
# without stage2: rustup toolchain install nightly-2026-06-14 --component rust-src \
# --target aarch64-unknown-linux-gnu, then replace +stage2 with +nightly-2026-06-14
```

## API overview
Expand Down Expand Up @@ -379,7 +390,10 @@ claude mcp add --transport http rustkvm https://rustkvm.local/mcp \

## Documentation

- [`docs/quickstart.md`](docs/quickstart.md): flash a release image and log in
- [`docs/getting-started.md`](docs/getting-started.md): flash, deploy, wire up and log in on an RK3588 board
- [`docs/building.md`](docs/building.md): Docker devkit, application and firmware builds
- [`docs/customizing.md`](docs/customizing.md): your own hostname, name, USB IDs and firmware version
- [`docs/api.md`](docs/api.md): HTTP, JSON-RPC, MCP, HID-RPC and metrics reference
- [`AGENTS.md`](AGENTS.md): architecture, module tree, coding conventions and gotchas
- [`CONTRIBUTING.md`](CONTRIBUTING.md): development workflow and quality gates
Expand Down
22 changes: 17 additions & 5 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,13 +79,25 @@ RustKVM 的侧重点:RK3588 的 8 核算力与 H.264 / H.265 硬件编码;

完整步骤(刷机、接线、首次登录)见 [docs/getting-started.md](docs/getting-started.md)。

1. 构建前端与设备程序(见下节),或直接使用 `dev_deploy.sh`
2. 部署到设备:`./dev_deploy.sh -r <设备IP> -u root`
3. 浏览器打开 `https://<设备IP>/` 或 `https://rustkvm.local/`,接受自签名证书
4. 完成设置向导,点击画面即可开始控制
1. 把固件镜像写入 microSD 卡([docs/quickstart.md](docs/quickstart.md)),可用 Release 下载的,也可自己构建(见下节);固件已自带 RustKVM
2. 浏览器打开 `https://<设备IP>/` 或 `https://rustkvm.local/`,接受自签名证书
3. 完成设置向导,点击画面即可开始控制
4. 部署自己构建的程序(可选):在 设置 → 安全 → SSH 访问 填入公钥,在 设置 → 系统 → 开发者 打开开发者模式,然后执行 `./dev_deploy.sh -r <设备IP>`(没有 stage2 工具链时自动在 devkit 容器里编译)

## 从源码构建

**最快路径:任意装了 Docker 的 Linux 主机(含 WSL2)**,无需登录 GitHub 或 `docker login`,镜像都是公开的:

```bash
git clone https://github.com/Rust-KVM/rustkvm && cd rustkvm
scripts/devkit.sh run scripts/build-app.sh # → out/app/rustkvm_app-aarch64-unknown-linux-gnu,约 7 分钟
scripts/devkit.sh firmware # → out/firmware/ SD 卡与 eMMC 镜像,首次需约 50 GB 空闲磁盘
```

版本固定、磁盘占用、代理与固件构建见 [docs/building.md](docs/building.md);改主机名、界面名称、USB 标识和固件版本见 [docs/customizing.md](docs/customizing.md)。WSL2 下 Docker 和 Linux 文件都在 C 盘的虚拟磁盘里,可用空间以 C 盘为准。

不用 Docker 时的手动步骤:

```bash
# 1. 前端(任意主机,stable Rust)
rustup target add wasm32-unknown-unknown && cargo install trunk
Expand All @@ -95,7 +107,7 @@ cd crates/rkvm-web && trunk build --release && cd -
sudo apt install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev
cargo +nightly test --workspace

# 3. aarch64 设备构建(需 Buildroot RK3588 工具链 + 自建 stage2 Rust 工具链)
# 3. aarch64 设备构建(需 /opt/rk3588-buildkit 工具链;stage2 是参考工作站自建的,也可换成 +nightly-2026-06-14)
cargo +stage2 build -Z build-std=std,panic_abort \
--target aarch64-unknown-linux-gnu -p rustkvm --bin rustkvm_app --release
```
Expand Down
2 changes: 1 addition & 1 deletion app/src/mqtt/discovery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -520,7 +520,7 @@ impl MqttManager {
state_topic: Some(self.topic(&["update", "state"])),
device_class: Some("firmware".to_string()),
entity_category: Some("config".to_string()),
release_url: Some("https://github.com/foxxcn/rustkvm/releases".to_string()),
release_url: Some("https://github.com/Rust-KVM/rustkvm/releases".to_string()),
availability_topic: avail_topic.clone(),
availability_template: avail_template.to_string(),
device: Some(device.clone()),
Expand Down
12 changes: 9 additions & 3 deletions dev_deploy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,15 @@ if [ -z "${REMOTE_HOST}" ]; then
exit 1
fi

# 1. Build application
# 1. Build application: the self-built stage2 toolchain if installed, else the Docker devkit
echo "🔨 Building application..."
cargo +stage2 build -Z build-std=std,panic_abort --target aarch64-unknown-linux-gnu -p rustkvm --bin rustkvm_app --release
if rustup toolchain list 2>/dev/null | grep -q '^stage2'; then
cargo +stage2 build -Z build-std=std,panic_abort --target aarch64-unknown-linux-gnu -p rustkvm --bin rustkvm_app --release
APP_BIN=target/aarch64-unknown-linux-gnu/release/rustkvm_app
else
"$(dirname "$0")/scripts/devkit.sh" run scripts/build-app.sh
APP_BIN=out/app/rustkvm_app-aarch64-unknown-linux-gnu
fi

# 2. Stop existing process gracefully, then clean up.
# rustkvm_app arms the hardware watchdog and only disarms it during graceful
Expand All @@ -62,7 +68,7 @@ ssh "${REMOTE_USER}@${REMOTE_HOST}" '
echo "📦 Deploying to device..."
ssh "${REMOTE_USER}@${REMOTE_HOST}" 'mkdir -p /userdata/rustkvm/bin'
ssh "${REMOTE_USER}@${REMOTE_HOST}" 'mkdir -p /userdata/rustkvm/log'
ssh "${REMOTE_USER}@${REMOTE_HOST}" "cat > /userdata/rustkvm/bin/rustkvm_app" < target/aarch64-unknown-linux-gnu/release/rustkvm_app
ssh "${REMOTE_USER}@${REMOTE_HOST}" "cat > /userdata/rustkvm/bin/rustkvm_app" < "${APP_BIN}"
ssh "${REMOTE_USER}@${REMOTE_HOST}" 'chmod +x /userdata/rustkvm/bin/rustkvm_app'

# # 4. Start application
Expand Down
1 change: 1 addition & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
| [Getting started on RK3588](getting-started.md) | Flashing, deploying, wiring and first login |
| [API reference](api.md) | HTTP routes, JSON-RPC, MCP tools, HID-RPC, MQTT, Prometheus metrics |
| [Building](building.md) | Docker devkit, application package and firmware image, pinned versions |
| [Customizing your build](customizing.md) | Your own hostname, UI name, USB IDs, firmware version and build images |
| [Releasing and shipping firmware](firmware-release.md) | Versioning, release checklist, flashing, factory state, acceptance test |
| [Building from source manually](../README.md#building-from-source) | Frontend, x86_64 host build and the aarch64 cross toolchain without containers |
| [Architecture and conventions](../AGENTS.md) | Module tree, coding rules, gotchas |
Expand Down
34 changes: 29 additions & 5 deletions docs/building.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,22 @@ only Docker installed:
The scripts also run directly on a host that has the tools installed; the containers
just pin those tools.

Getting started from nothing:

```bash
git clone https://github.com/Rust-KVM/rustkvm && cd rustkvm
scripts/devkit.sh run scripts/build-app.sh
```

No GitHub account or `docker login` is needed: the repository and the `ghcr.io/rust-kvm`
images are public. Your user must be able to run `docker` (member of the `docker`
group, or Docker Desktop's WSL integration).

> [!NOTE]
> **WSL2:** Docker and the Linux filesystem live in a virtual disk on the Windows system
> drive, so free space on `C:` is the real limit, whatever `df /` says. Clone and build
> on the Linux filesystem (`~/...`), not under `/mnt/c`.

## Pinned versions

| What | Version | Where it is pinned |
Expand All @@ -33,8 +49,9 @@ scripts/devkit.sh run scripts/build-app.sh
```

The first run pulls the published devkit image,
`ghcr.io/rust-kvm/rustkvm-devkit:nightly-2026-06-14` (about 3 GB unpacked). `out/app/` then
holds `rustkvm_app-aarch64-unknown-linux-gnu`, its `.sha256` (the same pair the OTA
`ghcr.io/rust-kvm/rustkvm-devkit:nightly-2026-06-14` (about 3 GB unpacked) and takes
about 7 minutes on 16 threads; it needs about 5 GB of disk in all (image, `target/` and
the cargo cache in `~/.cache/rustkvm`). `out/app/` then holds `rustkvm_app-aarch64-unknown-linux-gnu`, its `.sha256` (the same pair the OTA
updater installs) and `BUILD_INFO`.

The devkit image sits on `ghcr.io/rust-kvm/rk3588-buildkit:20250527`, the cross gcc and
Expand Down Expand Up @@ -66,11 +83,11 @@ Deploy to a board on the network as before: `./dev_deploy.sh -r <device_ip>`.

## 2. Firmware image

Costs of the first build (the disk figure is the reference workbench's SDK tree; network and time are estimates):
Costs of the first build (the disk figure is measured on the reference workbench; network and time are estimates):

| | |
| --- | --- |
| Disk | about 60 GB for the SDK checkout and build tree, plus about 10 GB of download and ccache under `~/.cache/rustkvm` |
| Disk | about 40 GB for the SDK checkout and build tree (38 GB measured with the headless defconfig), plus about 2 GB of downloads and ccache under `~/.cache/rustkvm` and 2 GB for the builder image; keep 50 GB free |
| Network | about 15 GB on the first sync (shallow clones plus Buildroot package sources) |
| Time | several hours on 16 threads; later builds reuse the tree and ccache |

Expand All @@ -79,6 +96,11 @@ scripts/devkit.sh run scripts/build-app.sh # the image embeds out/app/
scripts/devkit.sh firmware # sync, patch, build, collect
```

The builder image (`rustkvm/firmware-builder:local`, Ubuntu 22.04 with the SDK's host
packages) is not published; the first `firmware` run builds it from
`ci/firmware/Dockerfile`, pulling `ubuntu:22.04` from Docker Hub. Every pinned SDK
commit is fetched anonymously from `github.com/friendlyarm`.

Steps can be run separately: `scripts/devkit.sh firmware patch build collect` skips the
sync. The SDK checkout lives in `RUSTKVM_SDK_DIR` (default `~/rustkvm-sdk/rk3588`) and
belongs to the script: the patch step resets every patched project to its pinned commit.
Expand All @@ -96,7 +118,9 @@ belongs to the script: the patch step resets every patched project to its pinned
set `FW_VERSION` to override it for a release.

The firmware container runs `--privileged` because the SDK packs images through loop
devices. What the SDK changes are and how to edit them: [`os/rk3588/README.md`](../os/rk3588/README.md).
devices. An incremental rebuild after an application or overlay change
(`scripts/devkit.sh firmware patch build collect`) takes 7 to 40 minutes, depending on what changed. What the SDK changes are and how to edit them: [`os/rk3588/README.md`](../os/rk3588/README.md).
Your own hostname, name and version label: [customizing.md](customizing.md).

## Networks behind a proxy

Expand Down
81 changes: 81 additions & 0 deletions docs/customizing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# Customizing your build

How to ship RustKVM under your own name: device hostname, the name in the web UI, USB
descriptors, the firmware version label and your own build images. Build everything with
the devkit first ([building.md](building.md)); each change below is followed by the same
two commands:

```bash
scripts/devkit.sh run scripts/build-app.sh
FW_VERSION=<your version> scripts/devkit.sh firmware # only if you ship firmware
```

## Per device, no rebuild

`/userdata/rustkvm/config.toml` on the device overrides the built-in defaults. Edit it
over SSH (developer mode, see [getting-started.md](getting-started.md#3-deploy-rustkvm))
and restart with `/etc/init.d/S99rustkvm restart`.

```toml
[network_config]
hostname = "mykvm" # system hostname; mDNS keeps the --mdns-hostname name

[usb_config] # all five keys are required in this table
vendor_id = "0x1d6b"
product_id = "0x0104"
serial_number = ""
manufacturer = "MyKVM"
product = "MyKVM USB Emulation Device"
```

`rustkvm_app --help` lists every runtime flag; each also reads a `RUSTKVM_*` environment
variable (for example `--mdns-hostname` / `RUSTKVM_MDNS_HOSTNAME`). The firmware starts
the app from `/etc/init.d/S99rustkvm` without flags, so to change a flag for every unit
change the default in the source instead.

## Built-in defaults (rebuild the app)

| What | Where |
| --- | --- |
| mDNS name (`rustkvm.local`) and fallback hostname | `default_value` of `mdns_hostname` in `app/src/cli.rs` |
| Browser tab title | `<title>` in `crates/rkvm-web/index.html` |
| Name in the sidebar, login card and page header | `"rustkvm"` strings in `crates/rkvm-web/src/shell/sidebar.rs` and `crates/rkvm-web/src/app.rs` |
| USB manufacturer, product, VID/PID | `impl Default for UsbConfig` in `app/src/config/types.rs` |
| Virtual media SCSI inquiry string | `inquiry_string` in `app/src/hardware/usb/gadget.rs` |
| MQTT base topic and Home Assistant device | `app/src/mqtt/types.rs`, `app/src/mqtt/discovery.rs` |

Use a USB vendor and product ID you are entitled to before shipping hardware; the default
`0x1d6b:0x0104` is the Linux Foundation multifunction gadget ID.

The MCP `serverInfo.name` (`app/src/mcp.rs`) is asserted by `app/tests/mcp.rs`; change
both together if you rename it.

## Firmware

| What | How |
| --- | --- |
| Version label (`/etc/rustkvm-release`, Settings → About, image file names) | `FW_VERSION=...` on the `firmware` command |
| Root password on the serial console | `RUSTKVM_ROOT_PASSWORD='<crypt hash>'` (`mkpasswd -m sha-512`); unset disables root password login. Keep it out of the repository. |
| Files in the root filesystem | `os/rk3588/rootfs-overlay/` |
| Kernel, Buildroot or board changes | patches under `os/rk3588/patches/`, see [`os/rk3588/README.md`](../os/rk3588/README.md) |

The image files are always named `rustkvm-rk3588-<FW_VERSION>-*.img.gz`; rename them
after `collect` if you need another prefix.

## Your own build images

The devkit pulls `ghcr.io/rust-kvm/*` images anonymously. To build and host your own
(after changing `ci/devkit/Dockerfile` or the toolchain), point the scripts at your
registry and push:

```bash
export RUSTKVM_BUILDKIT_IMAGE=ghcr.io/<you>/rk3588-buildkit:<tag>
export RUSTKVM_DEVKIT_IMAGE=ghcr.io/<you>/rustkvm-devkit:<tag>
scripts/devkit.sh image # builds the devkit on top of the buildkit image
docker login ghcr.io # with your own token, never committed
scripts/devkit.sh push
```

In a fork, `buildkit.yml` already publishes under your own owner; `release.yml` names
`ghcr.io/rust-kvm/rustkvm-devkit` directly, so change it there if releases should use
your devkit.
Loading
Loading