The development host is Linux x86_64; `flutter build windows` cannot
be cross-compiled and requires a Windows host with Visual Studio
2022's C++ Desktop workload. This commit adds the instructions for
producing the Windows v0.2.0-beta.1 Internal Beta build on an Azure
VM, plus a PowerShell helper that automates the build itself.
docs/release/windows-build.md (v0.1.0):
- Toolchain pin table (Windows Server 2022, VS 2022 Build Tools
+ C++ workload, Flutter 3.41.9, Rust 1.95 stable, FRB 2.12.0,
CMake, audiopus build dependency).
- Azure VM provisioning recipe: Standard_D4s_v5 (4 vCPU / 16 GiB),
Premium SSD 128 GiB, RDP locked to caller IP, auto-shutdown,
cost estimate (<USD 1 per build session).
- Step-by-step PowerShell to install VS 2022 Build Tools with the
required components, Git for Windows, rustup, Flutter SDK,
flutter_rust_bridge_codegen, and CMake.
- Two upload paths for source: temporary git remote OR zip archive
over RDP clipboard.
- flutter create --platforms=windows to scaffold the
windows/ platform folder (the product Flutter app was created
with only linux + android).
- cargo build -p chanora_bridge to produce chanora_bridge.dll.
- flutter build windows --release to produce chanora_flutter.exe
and the bundle.
- Drop the DLL next to the EXE so dart:ffi loads it.
- Smoke-test instructions including the cn.teamspeak.app UDP 9987
egress gotcha for some Azure regions.
- Packaging into chanora-v0.2.0-beta.1-windows-x64.zip.
- Known-issue / caveat table.
- Reproducibility note (build is not bit-reproducible in this Beta).
tools/build-windows.ps1:
- Parameter switches: -SkipRustBuild, -RegenerateBindings, -Version.
- Verifies flutter, cargo, rustc, cmake, git on PATH.
- Adds the x86_64-pc-windows-msvc target via rustup if missing.
- Runs flutter create --platforms=windows if the windows/ folder
is absent in apps/chanora_flutter/.
- Optionally regenerates FRB bindings.
- cargo build --release -p chanora_bridge --target x86_64-pc-windows-msvc.
- flutter pub get + flutter build windows --release.
- Copies the DLL into the Release bundle.
- Compress-Archive into chanora-<version>-windows-x64.zip.
- Prints final artefact paths.
This is documentation + helper only; no actual Windows binaries are
produced by this commit. To produce the binaries, follow §3-§13 of
docs/release/windows-build.md on a Windows host.
17 KiB
Building Chanora v0.2.0-beta.1 on Windows via Azure VM
Document type: Release / Platform Build Instructions
Version: 0.1.0
Status: Draft
Language: English
Product: Chanora
Repo path: docs/release/windows-build.md
1. Purpose
This document gives exact step-by-step instructions for building the
Chanora v0.2.0-beta.1 Internal Beta on a Windows host, using an
Azure Virtual Machine. It exists because the development host is
Linux; flutter build windows requires a Windows host with Visual
Studio 2022 and cannot be cross-compiled.
After following this doc you will have:
chanora_flutter.exe— the Windows desktop binary.chanora_bridge.dll— the Rust cdylib loaded by the .exe via FFI.- A complete release bundle directory the app needs at runtime.
The doc assumes you have:
- An Azure subscription with permission to create VMs.
- An RDP client (Windows: built-in; macOS: Microsoft Remote Desktop;
Linux:
remminaorrdesktop). - The Chanora source code locally (this repository).
2. Toolchain versions pinned for this build
| Tool | Version | Why |
|---|---|---|
| Windows Server | 2022 Datacenter | LTS, plenty of Azure images, ships PowerShell 5.1 + Server Core APIs |
| Visual Studio 2022 | Build Tools (Community works too) | Required by flutter build windows; provides MSVC, Windows SDK, CMake, link.exe |
| Visual Studio workload | "Desktop development with C++" | Provides MSVC v143, Windows 10/11 SDK, CMake tools, C++ ATL |
| Flutter SDK | 3.41.9 stable | Matches the Linux/Beta build; bridge codegen is version-sensitive |
| Dart SDK | 3.11.5 | Comes with Flutter 3.41.9 |
| Rust toolchain | stable 1.95 (or newer) | Workspace rust-version = "1.95" |
| Rust target | x86_64-pc-windows-msvc |
Native Windows ABI; produces MSVC-compatible .dll |
cargo-ndk |
N/A | Not needed for Windows; only Android needs it |
| Opus library | bundled via audiopus_sys build script + cmake |
On Windows there is no system libopus by default; we build it from source |
| Git for Windows | latest | Convenient git + the bundled Bash for running flutter if needed |
| 7-Zip (optional) | latest | Convenient for unpacking the source bundle if you uploaded it as zip |
3. Azure VM provisioning
3.1 Recommended SKU
| Setting | Value | Rationale |
|---|---|---|
| Image | Windows Server 2022 Datacenter — x64 Gen2 |
Most common LTS Windows image |
| Size | Standard_D4s_v5 (4 vCPU, 16 GiB) or larger |
The Cargo cold build pulls hundreds of crates; 4 cores keeps the build under 30 minutes |
| OS disk | Premium SSD 128 GiB | Default 128 GiB is enough for VS 2022 (~10 GiB), Flutter SDK (~2 GiB), Rust toolchain (~1 GiB), source + build artefacts (~15 GiB) |
| Auth | Password (you'll RDP in) | Sufficient for a short-lived build VM |
| Inbound port rules | Allow RDP (3389) only from your IP | Lock RDP to your IP via NSG; never open to 0.0.0.0/0 |
| Networking | New VNet + public IP | Standard for an ad-hoc build host |
| Auto-shutdown | Enable, 19:00 local time | Stops you forgetting and burning hours |
3.2 Estimated cost
Standard_D4s_v5 in West US 2 is about USD $0.20/hour for the
compute plus ~USD $0.01/hour for the disk. A first cold build
(provision → install everything → compile → archive) is typically
2–3 hours. Total: under USD $1 per build session if you
deallocate after.
Cost gotcha: Stopped (deallocated) VMs do not bill for compute; Stopped (in the OS shutdown sense, but not deallocated in Azure) still bills compute. Always check the Azure portal shows the VM as Stopped (deallocated).
3.3 Provisioning checklist
- Azure Portal → Create a resource → Windows Server 2022 Datacenter (x64 Gen2).
- Choose region near you.
- Size:
Standard_D4s_v5. - Administrator account: pick a username (e.g.
chanora) + a strong password. - Inbound ports: RDP only, restricted to your public IP.
- Disks: keep Premium SSD 128 GiB.
- Networking: defaults; ensure auto-assign public IP.
- Management → Auto-shutdown: On, time = end of your work day, notification = email.
- Review + create.
3.4 First RDP login
- Get the VM's public IP from the Azure portal.
- RDP in as
chanora/ your password. - (Optional) Open Server Manager → Local Server → IE Enhanced Security Configuration → turn Off for Administrators so the downloads in §4 don't get blocked.
4. Install the toolchain (one-time per VM)
Run all of these in an elevated PowerShell (right-click PowerShell → Run as Administrator).
4.1 Visual Studio 2022 Build Tools + workload
# Download the VS 2022 Build Tools bootstrapper
Invoke-WebRequest -Uri 'https://aka.ms/vs/17/release/vs_buildtools.exe' `
-OutFile "$env:TEMP\vs_buildtools.exe"
# Install with the C++ desktop workload (this is what Flutter requires).
# --quiet suppresses UI; --wait blocks until done; takes 10-15 minutes.
& "$env:TEMP\vs_buildtools.exe" --quiet --wait --norestart `
--add Microsoft.VisualStudio.Workload.VCTools `
--add Microsoft.VisualStudio.Component.VC.Tools.x86.x64 `
--add Microsoft.VisualStudio.Component.Windows11SDK.22621 `
--add Microsoft.VisualStudio.Component.VC.CMake.Project `
--add Microsoft.VisualStudio.Component.VC.ATL `
--includeRecommended
When this completes, reboot the VM (Visual Studio Build Tools needs it for environment variables).
4.2 Git for Windows
winget install --id Git.Git --silent --accept-source-agreements --accept-package-agreements
4.3 Rust (stable, with the MSVC target)
# Download rustup-init for Windows.
Invoke-WebRequest -Uri 'https://win.rustup.rs/x86_64' `
-OutFile "$env:TEMP\rustup-init.exe"
# Install the default toolchain (stable-x86_64-pc-windows-msvc).
& "$env:TEMP\rustup-init.exe" -y --default-toolchain stable --profile minimal
# Open a NEW PowerShell so PATH picks up cargo.
# Then verify:
cargo --version
rustc --version
Expected output: cargo 1.95.0 (or newer), rustc 1.95.0. The
default target on Windows is x86_64-pc-windows-msvc — this is what
we want.
4.4 Flutter SDK (pinned to 3.41.9 stable)
# Download Flutter SDK
$flutter = 'https://storage.googleapis.com/flutter_infra_release/releases/stable/windows/flutter_windows_3.41.9-stable.zip'
Invoke-WebRequest -Uri $flutter -OutFile "$env:TEMP\flutter.zip"
Expand-Archive "$env:TEMP\flutter.zip" -DestinationPath 'C:\'
# Add to PATH for the current user (persists across sessions).
[Environment]::SetEnvironmentVariable(
'Path',
[Environment]::GetEnvironmentVariable('Path', 'User') + ';C:\flutter\bin',
'User'
)
# Open a NEW PowerShell so PATH applies, then:
flutter --version
flutter doctor
flutter doctor should show green on:
- ✓ Flutter (Channel stable, 3.41.9, on Microsoft Windows …)
- ✓ Windows Version (Windows … 10.0.20348 or later)
- ✓ Visual Studio - develop Windows apps (Visual Studio Build Tools 2022 …)
Yellow on Android Studio / Chrome is fine — we're only building Windows.
4.5 flutter_rust_bridge_codegen and Dart deps
cargo install flutter_rust_bridge_codegen --version "=2.12.0"
Takes ~5 minutes (compiles flutter_rust_bridge_codegen from source).
4.6 Opus build dependency
The audiopus Rust crate wraps the C Opus library. On Windows there
is no system Opus, so the audiopus_sys build script will attempt to
build Opus from source via CMake (which the VS workload already
provided). No extra action is required — but verify CMake is on
PATH:
cmake --version
Expected: cmake version 3.x (any 3.x ≥ 3.18 is fine).
If CMake is not found, install it explicitly:
winget install --id Kitware.CMake --silent
5. Get the source onto the VM
Two options. Pick whichever is easier for you.
Option A — Push the repo to a temporary git remote
On the Linux host where the repo lives:
# E.g. create a private GitHub repo and push:
git remote add origin https://github.com/<your-user>/chanora.git
git push --all
git push --tags
Then on the Windows VM:
cd C:\
git clone https://github.com/<your-user>/chanora.git
cd chanora
git checkout v0.2.0-beta.1
Option B — Upload the source as a zip via RDP clipboard
On Linux:
cd /home/milkice
git -C chanora archive --format=zip --output=/tmp/chanora-v0.2.0-beta.1.zip v0.2.0-beta.1
Copy /tmp/chanora-v0.2.0-beta.1.zip into the RDP session
(clipboard or shared drive), unzip into C:\chanora\.
Either way, you end up with the working tree at C:\chanora\,
checked out at the v0.2.0-beta.1 tag.
6. Add the Windows platform to the Flutter app
The product Flutter app was created with --platforms=linux,android,
so the windows/ platform folder does not yet exist. Add it once on
the build VM:
cd C:\chanora\apps\chanora_flutter
flutter create --platforms=windows .
This creates apps/chanora_flutter/windows/ containing:
windows/
CMakeLists.txt
flutter/
CMakeLists.txt
generated_plugin_registrant.cc
...
runner/
main.cpp
Runner.rc
chanora_flutter.exe.manifest
...
Important: Do not commit this
windows/folder back to the repo from the VM unless you also do the corresponding edits the source-of-truth host uses (icon, manifest, etc.). For Beta it is fine to keep it VM-only.
7. Build the Rust cdylib for Windows
cd C:\chanora
cargo build --release -p chanora_bridge
This builds the workspace, including:
- The whole
tsclientlibgit dependency tree (slow first time — expect ~5–10 minutes for the first compile ofaudiopus_sys+tsprotoetc.). chanora_bridgeas acdylib+staticlib+rlib.
Expected output location:
C:\chanora\target\release\chanora_bridge.dll
(Note: no lib prefix on Windows MSVC.)
Verify:
Get-Item C:\chanora\target\release\chanora_bridge.dll | Format-List Name, Length, LastWriteTime
The DLL is typically ~16–18 MiB.
8. Regenerate the FRB bindings (optional — only if you changed the bridge API)
The Linux build already produced lib/src/rust/*.dart and
crates/chanora_bridge/src/frb_generated.rs. The bindings are
platform-agnostic Dart + Rust source, so they do not need
regenerating on Windows. Skip §8 unless you modified
crates/chanora_bridge/src/api.rs after v0.2.0-beta.1.
If you do need to regenerate:
cd C:\chanora
flutter_rust_bridge_codegen generate
Takes ~5–10 minutes the first time on Windows.
9. Build the Flutter Windows binary
cd C:\chanora\apps\chanora_flutter
flutter pub get
flutter build windows --release
flutter build windows:
- Invokes CMake on
apps/chanora_flutter/windows/. - Builds
chanora_flutter.exevia MSVC. - Bundles the Flutter engine DLL (
flutter_windows.dll), ICU data, Dart-AOT-compiledapp.so(delivered asdata\app.so), the Flutterdata\directory, and platform-specific resources.
Build time: ~3–5 minutes on Standard_D4s_v5 once dependencies are
cached.
Expected output location:
C:\chanora\apps\chanora_flutter\build\windows\x64\runner\Release\
Contents:
| File | Size (approx) | Notes |
|---|---|---|
chanora_flutter.exe |
~10 MiB | The launcher .exe |
flutter_windows.dll |
~17 MiB | Flutter engine |
data\icudtl.dat |
~10 MiB | Unicode tables |
data\app.so |
varies | AOT-compiled Dart code |
data\flutter_assets\ |
varies | Images, fonts, ARB-derived .json |
10. Add the Rust DLL to the bundle
The Flutter app loads chanora_bridge.dll at runtime via dart:ffi.
Drop the DLL next to the .exe (the same directory):
$src = 'C:\chanora\target\release\chanora_bridge.dll'
$dst = 'C:\chanora\apps\chanora_flutter\build\windows\x64\runner\Release\'
Copy-Item $src $dst -Force
Verify everything is in place:
Get-ChildItem $dst -Recurse | Select-Object FullName, Length |
Format-Table -AutoSize
You should see:
C:\chanora\apps\chanora_flutter\build\windows\x64\runner\Release\chanora_flutter.exe
C:\chanora\apps\chanora_flutter\build\windows\x64\runner\Release\chanora_bridge.dll
C:\chanora\apps\chanora_flutter\build\windows\x64\runner\Release\flutter_windows.dll
C:\chanora\apps\chanora_flutter\build\windows\x64\runner\Release\data\app.so
C:\chanora\apps\chanora_flutter\build\windows\x64\runner\Release\data\icudtl.dat
C:\chanora\apps\chanora_flutter\build\windows\x64\runner\Release\data\flutter_assets\...
11. Smoke test on the VM
Double-click chanora_flutter.exe (or & '...\chanora_flutter.exe'
in PowerShell). The Beta UI should appear:
- Banner: "Beta build — voice in/out wired; not production ready."
- Form:
cn.teamspeak.app+ChanoraBetapre-populated. - Connect button.
Press Connect → after a few seconds you should see the channel
tree of cn.teamspeak.app (Vigorous Pro). Press Start audio →
PTT button appears. Hold it to transmit (a Windows mic permission
prompt may appear).
Network gotcha: Some Azure regions block UDP 9987 outbound by default. If Connect times out, check the VM's NSG (outbound rules) and the regional firewall.
12. Package the release artefact
$src = 'C:\chanora\apps\chanora_flutter\build\windows\x64\runner\Release'
$out = 'C:\chanora-v0.2.0-beta.1-windows-x64.zip'
Compress-Archive -Path "$src\*" -DestinationPath $out -Force
Get-Item $out | Format-List Name, Length
Expected size: ~50–80 MiB.
13. Download to your local host
From your local machine (Linux/macOS/Windows):
# Use Azure CLI to copy via SAS, or just RDP the file out.
# Simplest: in the RDP session enable clipboard file transfer
# (mstsc /control or Remmina's "share local folder"), then drag the
# zip out.
Or use Azure Blob Storage if you want versioned hosting.
14. Final artefact paths (what you actually produce)
Inside the VM, after a successful build:
| Artefact | Path on the VM |
|---|---|
| Bridge DLL | C:\chanora\target\release\chanora_bridge.dll |
| Flutter EXE | C:\chanora\apps\chanora_flutter\build\windows\x64\runner\Release\chanora_flutter.exe |
| Complete runtime bundle | C:\chanora\apps\chanora_flutter\build\windows\x64\runner\Release\ |
| Release zip | C:\chanora-v0.2.0-beta.1-windows-x64.zip |
15. After the build — deallocate the VM
# In the Azure CLI on your local host, OR via the portal:
az vm deallocate --resource-group <rg> --name <vm-name>
Verify the portal says Stopped (deallocated).
16. Known issues and caveats
| Issue | Workaround |
|---|---|
audiopus_sys build fails with "cmake not found" |
Re-run §4.6's winget install Kitware.CMake and open a new PowerShell. |
flutter build windows complains about Long Paths |
Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem' -Name LongPathsEnabled -Value 1, then reboot. |
Microsoft Defender quarantines chanora_bridge.dll |
Add C:\chanora\target\release\ to Defender exclusions before the cargo build. |
The build VM cannot reach cn.teamspeak.app on UDP 9987 |
Confirm Azure regional firewall allows outbound UDP. The Linux dev host's verification doesn't translate to Azure VMs automatically. |
First cargo build takes > 30 minutes |
Normal. Subsequent builds reuse target/ and are ~1 minute. |
flutter pub get fails behind a corporate proxy |
Set HTTPS_PROXY and HTTP_PROXY environment variables before running. |
The windows/ folder created by flutter create is missing icons |
Replace windows\runner\resources\app_icon.ico with a Chanora icon. The default Flutter icon ships otherwise. Cosmetic only for Beta. |
17. Reproducibility note
This build process is not bit-reproducible on Windows in the current state:
- The
audiopus_sysbuild script picks the CMake-discovered MSVC toolchain, which embeds its version into the DLL. - Dart AOT compilation embeds a Dart kernel hash.
- Windows resource compilation embeds a build timestamp.
If true reproducibility is required for a future External Beta or MVP Public release, that is Beta+ work; the build above is sufficient for an Internal Beta milestone per DEC-001.
18. Change History
| Version | Date | Description |
|---|---|---|
| 0.1.0 | 2026-05-14 | Initial Windows build instructions for Chanora v0.2.0-beta.1. Targets Azure VM Standard_D4s_v5 running Windows Server 2022, Visual Studio 2022 Build Tools with the C++ desktop workload, Rust 1.95 stable (x86_64-pc-windows-msvc), Flutter 3.41.9 stable, flutter_rust_bridge 2.12.0. Produces chanora_flutter.exe + chanora_bridge.dll packaged into chanora-v0.2.0-beta.1-windows-x64.zip. |