# Backend —  Application Entry Point & Build System

Location: src-tauri/src/ and src/

# main.rs — Binary Entry Point

<span style="white-space: pre-wrap;">The binary entry point is intentionally minimal. It simply calls </span>`<span class="editor-theme-code">base_station_lib::run()</span>`<span style="white-space: pre-wrap;">, which lives in </span>`<span class="editor-theme-code">lib.rs</span>`<span style="white-space: pre-wrap;">. The only thing of note is the </span>`<span class="editor-theme-code">windows_subsystem = "windows"</span>`<span style="white-space: pre-wrap;"> attribute on the first line — this suppresses the extra console window that would otherwise appear when launching the app on Windows in release builds. Do not remove it.</span>

# lib.rs — Application Bootstrap

`<span class="editor-theme-code">lib.rs</span>`<span style="white-space: pre-wrap;"> is where the entire Tauri application is configured and started. It does the following in order:</span>

### Managed state registration

<span style="white-space: pre-wrap;">Three pieces of state are registered with Tauri's state manager so they can be injected into any command via </span>`<span class="editor-theme-code">State<'_></span>`:

- `<span class="editor-theme-code">RoverState</span>`<span style="white-space: pre-wrap;"> — the three rover mode booleans (</span>`<span class="editor-theme-code">drive_manual_mode</span>`<span style="white-space: pre-wrap;">, </span>`<span class="editor-theme-code">arm_manual_mode</span>`<span style="white-space: pre-wrap;">, </span>`<span class="editor-theme-code">pickup_mode</span>`<span style="white-space: pre-wrap;">), all wrapped in </span>`<span class="editor-theme-code">Mutex</span>`<span style="white-space: pre-wrap;"> so they are safe to read and write from async commands</span>
- `<span class="editor-theme-code">DummyStreamHandle</span>`<span style="white-space: pre-wrap;"> — holds an optional cancellation flag for the dummy simulator</span>
- `<span class="editor-theme-code">RoverAddress</span>`<span style="white-space: pre-wrap;"> — holds the port the Rover is sending to</span>

<p class="callout warning">`<span class="editor-theme-code">RoverState</span>`<span style="white-space: pre-wrap;"> is subject to change, because the rover might become able to drive and move the arm at the same time</span></p>

### Plugin registration

Three official Tauri plugins are loaded:

<table id="bkmrk-pluginpurposetauri-p"><colgroup><col></col><col></col></colgroup><tbody><tr><th>**Plugin**

</th><th>**Purpose**

</th></tr><tr><td>`<span class="editor-theme-code">tauri-plugin-fs</span>`

</td><td>File system access from the frontend

</td></tr><tr><td>`<span class="editor-theme-code">tauri-plugin-opener</span>`

</td><td>Open files/URLs in the OS default application

</td></tr><tr><td>`<span class="editor-theme-code">tauri-plugin-dialog</span>`

</td><td>Native file picker and dialog boxes

</td></tr></tbody></table>

<span style="white-space: pre-wrap;">All plugins must be registered in </span>`<span class="editor-theme-code">lib.rs</span>`<span style="white-space: pre-wrap;">, </span>`<span class="editor-theme-code">Cargo.toml</span>`<span style="white-space: pre-wrap;"> and if they require access to anything in </span>`<span class="editor-theme-code">src-tauri/capabilities/default.json</span>`

<p class="callout warning"><span style="white-space: pre-wrap;">AI will often try to get you to add them to </span>`<span class="editor-theme-code">tauri.conf.json</span>`<span style="white-space: pre-wrap;"> but that is, as far as I have encountered if, incorrect</span></p>

### Command registration

<span style="white-space: pre-wrap;">All Tauri commands are registered here via </span>`<span class="editor-theme-code">tauri::generate_handler!</span>`<span style="white-space: pre-wrap;">. This is the complete list of commands callable from the frontend via </span>`<span class="editor-theme-code">invoke()</span>`<span style="white-space: pre-wrap;">. If you add a new command in any </span>`<span class="editor-theme-code">commands/</span>`<span style="white-space: pre-wrap;"> file, it must also be added here or it will not be accessible from the frontend.</span>

### Setup (startup sequence)

<span style="white-space: pre-wrap;">The </span>`<span class="editor-theme-code">.setup()</span>`<span style="white-space: pre-wrap;"> closure runs once at launch, before any window is shown. It performs these steps in order:</span>

**a) GStreamer plugin path**<span style="white-space: pre-wrap;"> Sets the </span>`<span class="editor-theme-code">GST_PLUGIN_PATH</span>`<span style="white-space: pre-wrap;"> environment variable so GStreamer can find its plugins on Windows. It will look for them at </span>`<span class="editor-theme-code">C:\gstreamer\1.0\msvc_x86_64\bin</span>`. On Linux it can find the plugins automatically.

**b) Storage directory creation**<span style="white-space: pre-wrap;"> Calls </span>`<span class="editor-theme-code">ensure_storage_dirs_internal()</span>`<span style="white-space: pre-wrap;"> to create the </span>`<span class="editor-theme-code">tasks/</span>`<span style="white-space: pre-wrap;">, </span>`<span class="editor-theme-code">images/</span>`<span style="white-space: pre-wrap;">, and </span>`<span class="editor-theme-code">maps/</span>`<span style="white-space: pre-wrap;"> subdirectories under the app data directory if they don't already exist.</span>

**c) Cache clearing**<span style="white-space: pre-wrap;"> Calls </span>`<span class="editor-theme-code">clear_cache_on_startup()</span>`<span style="white-space: pre-wrap;"> to wipe any stale cached files from the previous session.</span>

**d) GStreamer streaming server**<span style="white-space: pre-wrap;"> Spawns an async task that runs </span>`<span class="editor-theme-code">commands::gstreamer::stream()</span>`<span style="white-space: pre-wrap;"> for the lifetime of the app. This starts the three GStreamer pipelines and their corresponding MJPEG HTTP servers.</span>

**e) UDP service**<span style="white-space: pre-wrap;"> Creates </span>`<span class="editor-theme-code">UdpService</span>`<span style="white-space: pre-wrap;"> (binding </span>`<span class="editor-theme-code">0.0.0.0:9000</span>`<span style="white-space: pre-wrap;">) synchronously using </span>`<span class="editor-theme-code">block_on</span>`. The socket is extracted before the service is moved into Tauri's state manager, so it can be passed to the listener independently.

**f) UDP listener**<span style="white-space: pre-wrap;"> Spawns an async task running </span>`<span class="editor-theme-code">network::listener::run_listener()</span>`<span style="white-space: pre-wrap;"> with the shared socket. This is the loop that receives, decodes, and forwards all incoming rover packets to the frontend.</span>

**g) Controller listener**<span style="white-space: pre-wrap;"> Calls </span>`<span class="editor-theme-code">commands::controller::start_controller_listener()</span>`, which spawns an OS thread to poll for gamepad events.

# proto.rs — Protobuf Module

<span style="white-space: pre-wrap;">This file simply includes the generated Rust code for the </span>`<span class="editor-theme-code">packets</span>`<span style="white-space: pre-wrap;"> protobuf module:</span>

rust

```rust
pub mod packets {
    include!(concat!(env!("OUT_DIR"), "/packets.rs"));
}
```

<span style="white-space: pre-wrap;">The actual </span>`<span class="editor-theme-code">.proto</span>`<span style="white-space: pre-wrap;"> source files live in </span>`<span class="editor-theme-code">src-tauri/proto/</span>`<span style="white-space: pre-wrap;">. They are compiled at build time by </span>`<span class="editor-theme-code">build.rs</span>`<span style="white-space: pre-wrap;"> into </span>`<span class="editor-theme-code">packets.rs</span>`<span style="white-space: pre-wrap;"> in the Cargo </span>`<span class="editor-theme-code">OUT_DIR</span>`<span style="white-space: pre-wrap;">. Importing </span>`<span class="editor-theme-code">crate::proto::packets::*</span>`<span style="white-space: pre-wrap;"> in any Rust file gives access to all the generated message structs.</span>

# build.rs — Protobuf Compilation

<span style="white-space: pre-wrap;">The build script runs before the Rust compiler and is responsible for compiling all </span>`<span class="editor-theme-code">.proto</span>`<span style="white-space: pre-wrap;"> files into Rust code. It does this in several steps:</span>

**1. Collect proto files**<span style="white-space: pre-wrap;"> Recursively scans the </span>`<span class="editor-theme-code">src-tauri/proto/</span>`<span style="white-space: pre-wrap;"> directory for </span>`<span class="editor-theme-code">.proto</span>`<span style="white-space: pre-wrap;"> files. Only files that are inside a </span>`<span class="editor-theme-code">components/</span>`<span style="white-space: pre-wrap;"> subdirectory are included — this is a deliberate filter to exclude top-level or organisational proto files.</span>

**2. Patch proto files**<span style="white-space: pre-wrap;"> Each </span>`<span class="editor-theme-code">.proto</span>`<span style="white-space: pre-wrap;"> file is copied to inside </span>`<span class="editor-theme-code">src-tauri/generated_proto</span>`<span style="white-space: pre-wrap;"> and patched to inject </span>`<span class="editor-theme-code">package packets;</span>`<span style="white-space: pre-wrap;"> after the line </span>`<span class="editor-theme-code">syntax = "proto3"</span>`<span style="color: rgb(204, 204, 204);">;</span><span style="white-space: pre-wrap;">. This ensures all generated types end up in a single </span>`<span class="editor-theme-code">packets</span>`<span style="white-space: pre-wrap;"> Rust module, regardless of how the source </span>`<span class="editor-theme-code">.proto</span>`<span style="white-space: pre-wrap;"> files are organised. The patching is idempotent — it won't inject the package line twice if it is already present.</span>

<p class="callout danger">This means that the basestation **protobufers are**<span style="white-space: pre-wrap;"> slightly </span>**different**<span style="white-space: pre-wrap;"> from the ones embeded and jonny boi (the jestson) uses. Keep this in mind for debugging</span></p>

**3. Compile**

- <span style="white-space: pre-wrap;">Using </span>**prost**<span style="white-space: pre-wrap;"> for backend. The patched files are compiled for backend (stored in </span>`<span class="editor-theme-code">src-tauri/target/debug/build/base_station-0f99f7e026ffb091/out/packets.rs</span>`<span style="white-space: pre-wrap;">) using </span>`<span class="editor-theme-code">prost_build</span>`. A type attribute is applied globally:

```rust
config.type_attribute(".", "#[derive(serde::Serialize)]");
```

- <span style="white-space: pre-wrap;">Using the plugin </span>**protoc-gen-ts**<span style="white-space: pre-wrap;"> for frontend. The patched files are compiled for frontend (stored in </span>`<span class="editor-theme-code">src/lib/proto</span>`)

<span style="white-space: pre-wrap;">This means every generated message struct and enum automatically derives </span>`<span class="editor-theme-code">serde::Serialize</span>`, so they can be passed directly as Tauri event payloads without any manual wrapper types.

**4. protoc**<span style="white-space: pre-wrap;"> The </span>`<span class="editor-theme-code">protoc</span>`<span style="white-space: pre-wrap;"> compiler binary is sourced from </span>`<span class="editor-theme-code">protoc-bin-vendored</span>`<span style="white-space: pre-wrap;">, so no system installation of </span>`<span class="editor-theme-code">protoc</span>`<span style="white-space: pre-wrap;"> is required.</span>

<p class="callout info"><span style="white-space: pre-wrap;">If you add new </span>`<span class="editor-theme-code">.proto</span>`<span style="white-space: pre-wrap;"> files, place them inside a </span>`<span class="editor-theme-code">components/</span>`<span style="white-space: pre-wrap;"> subdirectory under </span>`<span class="editor-theme-code">src-tauri/proto/</span>`<span style="white-space: pre-wrap;"> and they will be picked up automatically on the next build.</span></p>

<p class="callout warning"><span style="white-space: pre-wrap;">If you add new protobufers you must explicitly commit and push them into the github submodule, they will </span>**NOT** sync automatically when you sync the repo.</p>

# tauri.conf.json — Application & Security Configuration

### Window

<span style="white-space: pre-wrap;">The app opens a single window titled </span>`<span class="editor-theme-code">base_station</span>`<span style="white-space: pre-wrap;"> at 800×600. </span>`<span class="editor-theme-code">devtools</span>`<span style="white-space: pre-wrap;"> is enabled, meaning the browser DevTools can be opened in development builds.</span>

### Content Security Policy

The CSP is configured to be strict by default while allowing the specific localhost ports needed for video:

<table id="bkmrk-directivewhat-it-all"><colgroup><col></col><col></col></colgroup><tbody><tr><th>Directive

</th><th>What it allows

</th></tr><tr><td>`<span class="editor-theme-code">default-src</span>`

</td><td>Only the app itself and Tauri's custom protocol

</td></tr><tr><td>`<span class="editor-theme-code">script-src</span>`

</td><td>Self + inline scripts (required by SvelteKit)

</td></tr><tr><td>`<span class="editor-theme-code">img-src</span>`

</td><td>App assets + the three MJPEG stream ports (5000, 5001, 5002)

</td></tr><tr><td>`<span class="editor-theme-code">media-src</span>`

</td><td>The three MJPEG stream ports

</td></tr><tr><td>`<span class="editor-theme-code">connect-src</span>`

</td><td><span style="white-space: pre-wrap;">All localhost ports (for dev server, WebSockets) + </span>

`<span class="editor-theme-code">api.ipify.org</span>`

</td></tr></tbody></table>

<span style="white-space: pre-wrap;">The asset protocol is enabled with scope </span>`<span class="editor-theme-code">$APPDATA/**</span>`<span style="white-space: pre-wrap;">, which allows the frontend to read files from the app data directory (e.g. saved maps and images) using the </span>`<span class="editor-theme-code">asset://</span>`<span style="white-space: pre-wrap;"> protocol.</span>

<p class="callout warning">**For future developers:**<span style="white-space: pre-wrap;"> Security gives a lot of problems (and they vary between devices and platforms), if something isn't loading always check if it is because of permissions. Thus far setting security to unrestricted does not fix those issues.</span></p>

### Bundled resources

<span style="white-space: pre-wrap;">The </span>`<span class="editor-theme-code">models/*</span>`<span style="white-space: pre-wrap;"> glob in </span>`<span class="editor-theme-code">bundle.resources</span>`<span style="white-space: pre-wrap;"> ensures all 3D model files in </span>`<span class="editor-theme-code">src-tauri/models/</span>`<span style="white-space: pre-wrap;"> are included in the packaged application. This is what </span>`<span class="editor-theme-code">load_model.rs</span>`<span style="white-space: pre-wrap;"> reads from in release builds.</span>

# Cargo.toml — Key Dependencies

<table id="bkmrk-cratepurposetaurides"><colgroup><col></col><col></col></colgroup><tbody><tr><th>**Crate**

</th><th>**Purpose**

</th></tr><tr><td>`<span class="editor-theme-code">tauri</span>`

</td><td><span style="white-space: pre-wrap;">Desktop app framework, with </span>`<span class="editor-theme-code">protocol-asset</span>`<span style="white-space: pre-wrap;"> and </span>`<span class="editor-theme-code">devtools</span>`<span style="white-space: pre-wrap;"> features</span>

</td></tr><tr><td>`<span class="editor-theme-code">tokio</span>`<span style="white-space: pre-wrap;"> (full)</span>

</td><td>Async runtime for all network and I/O tasks

</td></tr><tr><td>`<span class="editor-theme-code">prost</span>`

</td><td>Protobuf encode/decode

</td></tr><tr><td>`<span class="editor-theme-code">gstreamer</span>`<span style="white-space: pre-wrap;"> / </span>`<span class="editor-theme-code">gstreamer-app</span>`

</td><td>Video pipeline

</td></tr><tr><td>`<span class="editor-theme-code">warp</span>`

</td><td>MJPEG HTTP server

</td></tr><tr><td>`<span class="editor-theme-code">gilrs</span>`

</td><td>Gamepad/controller input

</td></tr><tr><td>`<span class="editor-theme-code">reqwest</span>`<span style="white-space: pre-wrap;"> (blocking)</span>

</td><td>HTTP client used to capture video snapshots

</td></tr><tr><td>`<span class="editor-theme-code">serde</span>`<span style="white-space: pre-wrap;"> / </span>`<span class="editor-theme-code"> serde_json</span>`

</td><td>Serialisation for Tauri events and commands

</td></tr><tr><td>`<span class="editor-theme-code">anyhow</span>`

</td><td>Ergonomic error handling across async code

</td></tr><tr><td>`<span class="editor-theme-code">dirs</span>`

</td><td>Cross-platform system directory paths (cache dir)

</td></tr><tr><td>`<span class="editor-theme-code">chrono</span>`

</td><td>Date/time (available for timestamps)

</td></tr><tr><td>`<span class="editor-theme-code">bytes</span>`

</td><td>Zero-copy byte buffer for GStreamer frame sharing

</td></tr><tr><td>`<span class="editor-theme-code">once_cell</span>`

</td><td>Handling threads of dummy data

</td></tr><tr><td>`<span class="editor-theme-code">rand</span>`

</td><td>For generating random numbers (for dummy data)

</td></tr><tr><td>`<span class="editor-theme-code">socket2</span>`

</td><td>For creating a socket with custom options

</td></tr><tr><td>`<span class="editor-theme-code">tobj</span>`

</td><td>For handling maps with .obj format

</td></tr><tr><td>`<span class="editor-theme-code">las</span>`

</td><td>For handling maps with .las format

</td></tr><tr><td>`<span class="editor-theme-code">nalgebra</span>`

</td><td>For handling the computing the height map

</td></tr></tbody></table>