Repository Structure and Workspace Layout
This page maps every top-level directory and configuration file in the Portal repository so you can navigate confidently from day one. Portal is a large, multi-language monorepo: the bulk of the code is Rust organized as a Cargo workspace, with C/C++ for Wayfire plugins and streaming, Python/shell for deployment automation, and a Unity project for the AR glasses receiver. Understanding the layout before touching code prevents the most common beginner mistakes — building the wrong crate, editing a generated file, or looking for Rust where there is only C.
Root-Level Project Metadata
Section titled “Root-Level Project Metadata”The repository root holds the workspace manifest, toolchain pins, and the legal/governance files that apply to the entire project. Every crate and subsystem inherits from these settings.
[workspace]resolver = "2"members = [ "portal/spatial", "portal/spatial-plugin", ... ]The root Cargo.toml defines 30 workspace members grouped into six subsystems: core compositor, shared utilities, input/streaming, the PCP (Portal Capability Protocol) subsystem, the context/LLM layer, and the NPU runtime. It also declares a shared dependency pool under [workspace.dependencies] so that every crate uses identical versions of tokio, serde, tracing, and dozens of other crates — this prevents version drift and keeps the lockfile clean.
The [workspace.package] block sets project-wide metadata: the Demain Source License V2.2, edition = "2021", rust-version = "1.97.0", and publish = false (no crates are published to crates.io). The [workspace.lints.clippy] section is unusually thorough — it promotes correctness, suspicious, style, complexity, perf, and pedantic all to deny, and additionally denies unwrap_used, expect_used, and panic_in_result_fn in production code. Each crate opts in by adding lints.workspace = true in its [package] section.
Sources: Cargo.toml#L1-L45, Cargo.toml#L145-L172, Cargo.toml#L200-L272
Toolchain and Cross-Compilation Pins
Section titled “Toolchain and Cross-Compilation Pins”| File | Purpose |
|---|---|
rust-toolchain.toml |
Pins Rust 1.97.0 with rustfmt, clippy, and rust-src components |
.cargo/config.toml |
Sets the aarch64-linux-gnu-gcc linker for ARM64 cross-compilation |
Cross.toml |
Configures cross-rs Docker images for aarch64 and armv7 targets |
clippy.toml |
MSRV = 1.97.0, excessive-nesting threshold = 250, too-many-lines threshold = 250 |
deny.toml |
cargo-deny configuration: license allowlist, advisory ignores, supply-chain rules |
The primary target is aarch64-unknown-linux-gnu because the Spaceboard runs a Snapdragon X Elite (ARM64). The .cargo/config.toml wires the GNU cross-linker, while Cross.toml provides Docker-based cross-compilation images for CI environments that lack the native toolchain.
Sources: rust-toolchain.toml#L1-L4, .cargo/config.toml#L1-L17, Cross.toml#L1-L13, clippy.toml#L1-L26, deny.toml#L1-L21
Workspace Crate Map
Section titled “Workspace Crate Map”The following Mermaid diagram shows how the 30 workspace crates relate to each other, organized by functional subsystem. Arrows point from a crate toward the crates it depends on (import direction). Read the diagram top-to-bottom: the top row is the compositor surface, the middle row is application services, and the bottom row is shared infrastructure.
flowchart TD
subgraph Compositor
SPATIAL[portal-spatial]
PLUGIN[portal-spatial-plugin]
WARP[portal-warp]
WM[portal-wm]
end
subgraph Input & Streaming
INPUT[portal-input]
STREAM[portal-stream]
end
subgraph Application Framework
SHELL[portal-shell]
STYLE[portal-style]
LAUNCHER[portal-launcher]
LAUNCHERD[portal-launcherd]
CONTEXT[portal-context]
end
subgraph PCP Subsystem
PCP_CORE[pcp-core]
PCP_REG[pcp-registry]
PCP_DAEMON[pcp-daemon]
PCP_IPC[pcp-ipc]
PCP_CLI[pcp-cli]
end
subgraph Voice & AI
VOICE[portal-voice]
LLM[portal-llm]
NPU[portal-npu-runtime]
end
subgraph Shared
COMMON[portal-common]
end
PLUGIN --> SPATIAL
SPATIAL --> COMMON
WARP --> SPATIAL
WM --> COMMON
INPUT --> COMMON
STREAM --> COMMON
PCP_DAEMON --> PCP_CORE
PCP_DAEMON --> PCP_REG
PCP_DAEMON --> PCP_IPC
PCP_CLI --> PCP_IPC
PCP_REG --> PCP_CORE
VOICE --> NPU
CONTEXT --> LLM
SHELL --> STYLE
LAUNCHER --> COMMON
The crate catalog below lists every workspace member by name, path, crate type, and role:
| Crate Name | Path | Type | Role |
|---|---|---|---|
portal-spatial |
portal/spatial |
rlib + cdylib |
Spatial domain model: zones, dimensions, homography math |
portal-spatial-plugin |
portal/spatial-plugin |
cdylib |
Wayfire plugin entry point (loads into compositor process) |
portal-warp |
portal/warp |
cdylib |
Homography-based head-tracking warp renderer |
portal-wm |
portal/wm |
bin |
Window manager daemon — tracks Wayland toplevels via IPC |
portal-common |
portal/common |
rlib |
Shared error types, utilities, test infrastructure |
portal-input |
portal/input |
rlib |
Fusion engine: touch tracking, intent classification, virtual input |
portal-stream |
portal/stream |
rlib |
RTP packetization and HMAC-SHA256 stream authentication |
portal-launcher |
portal/launcher |
rlib |
Universal launcher: action registry, fuzzy search, desktop entry parsing |
portal-launcherd |
portal/launcherd |
bin |
Launcher daemon process |
portal-shell |
portal/shell |
rlib |
Shell interface for spatial application management |
portal-style |
portal/style |
rlib |
Design token system and theming (highest minor version at 0.4.0) |
portal-voice |
portal/voice |
rlib |
Voice pipeline: VAD, STT, NLU, TTS (highest minor version at 0.5.0) |
rnnoise-sys |
portal/voice/rnnoise-sys |
rlib |
FFI bindings to RNNoise denoiser C library |
qnn-sys |
portal/voice/qnn-sys |
rlib |
FFI bindings to Qualcomm Neural Network (QNN) SDK |
pcp-core |
portal/pcp/core |
rlib |
Core types, traits, error definitions for Portal Capability Protocol |
pcp-registry |
portal/pcp/registry |
rlib |
Capability registration and discovery |
pcp-ipc |
portal/pcp/ipc |
rlib |
IPC framing protocol for PCP daemon communication |
pcp-daemon |
portal/pcp/daemon |
bin |
PCP daemon process |
pcp-cli |
portal/pcp/cli |
bin |
Command-line interface to the PCP daemon |
portal-context |
portal/context |
rlib |
Context engine: event ingestion, decision making, template synthesis |
portal-llm |
portal/llm |
rlib |
On-device LLM integration layer |
portal-npu-runtime |
portal/npu-runtime |
rlib |
Safe Rust wrappers for Qualcomm Hexagon FastRPC |
portal-benches |
benches |
bench |
Criterion benchmarks for hot paths (IPC, RTP, TTS) |
The PCP subsystem alone contains 16 crates spanning core types, registry, AT-SPI2 integration, native bindings, push notifications, stream relay, platform abstraction, Wine interop, a simulator, CLI, IPC, daemon, learning, recovery, coordination, and inspection — each in its own subdirectory under portal/pcp/.
Sources: Cargo.toml#L3-L45, Cargo.toml#L47-L83, portal/spatial/Cargo.toml#L1-L12, portal/pcp/core/Cargo.toml#L1-L8, portal/common/Cargo.toml#L1-L25
The portal/ Directory — Runtime Code
Section titled “The portal/ Directory — Runtime Code”The portal/ directory is the heart of the repository. It contains all Rust workspace crates alongside the C/C++ streaming pipeline, Wayfire plugins, deployment artifacts, and operational configuration. The following diagram shows the top-level organization:
portal/├── spatial/ # Spatial domain model (Rust cdylib + rlib)├── spatial-plugin/ # Wayfire spatial plugin (Rust cdylib)├── warp/ # Spatial warp rendering (Rust cdylib)├── wm/ # Window manager daemon (Rust binary)├── common/ # Shared utilities (Rust rlib)├── input/ # Input handling, fusion engine (Rust rlib)├── stream/ # RTP/streaming pipeline (Rust rlib)├── launcher/ # App launcher library (Rust rlib)├── launcherd/ # Launcher daemon (Rust binary)├── shell/ # Shell interface (Rust rlib)├── style/ # Design token system (Rust rlib)├── voice/ # Voice pipeline: VAD/STT/NLU/TTS (Rust rlib)├── pcp/ # Portal Capability Protocol (16 Rust crates)│ ├── core/ registry/ ipc/ daemon/ cli/│ ├── atspi2/ native/ push/ stream/│ ├── platform/ wine/ simulator/│ ├── learning/ recovery/ coordination/ inspection/├── context/ # Context engine (Rust rlib)├── llm/ # On-device LLM integration (Rust rlib)├── npu-runtime/ # Hexagon FastRPC wrappers (Rust rlib)├── streaming/ # Main streaming pipeline (C / GStreamer)├── wayfire-plugins/ # C++ shims for Wayfire compositor (CMake)├── compositor/ # Compositor configuration files├── protocols/ # Wayland protocol XML definitions├── network/ # hostapd / dnsmasq / NetworkManager configs├── systemd/ # Systemd service unit files (.service)├── apparmor/ # AppArmor security profiles├── scripts/ # Build and deployment shell scripts├── bin/ # Executable helper scripts├── config/ # Example configuration files├── android/ # Android receiver application source└── docs/ # Architecture and operations documentationEach Rust crate directory follows the standard Cargo layout: src/ for source code, tests/ for integration tests, benches/ for Criterion benchmarks, fuzz/ for cargo-fuzz targets (in select crates), and a Cargo.toml manifest. Every manifest inherits workspace settings via .workspace = true for license, edition, Rust version, authors, and lints.
Sources: README.md#L92-L127, portal/common/Cargo.toml#L1-L25
Non-Rust Code Inside portal/
Section titled “Non-Rust Code Inside portal/”Three directories contain compiled code that is not part of the Cargo workspace:
The portal/streaming/ directory holds the primary streaming pipeline written in C — portal_stream.c is the entry point, with HMAC utilities in hmac_util.c/h, protocol headers generated from Wayland XML (wlr-export-dmabuf-protocol.c, xdg-shell-protocol.c), and a Makefile + build.sh for compilation. This C pipeline runs separately from the Rust streaming crate and handles DMA-BUF capture through GStreamer to hardware-encoded H.265 over RTP/UDP.
The portal/wayfire-plugins/ directory contains C++ CMake projects — thin shim plugins (portal-spatial, portal-spatial-warp, portal-virtual-output) that load into the Wayfire compositor process and call into the Rust cdylib crates via FFI. The CMakeLists.txt orchestrates the build, and portal-spatial-config.h defines compile-time configuration constants.
Sources: justfile#L22-L34, README.md#L22-L33
Deployment and Operations Artifacts
Section titled “Deployment and Operations Artifacts”Portal ships as a systemd service tree on the Spaceboard. The deployment artifacts live in three directories under portal/:
The portal/systemd/ directory contains 23 unit files and configuration templates. Key services include portal.service (the umbrella unit), portal-wm.service (window manager daemon), portal-input.service (input handler), portal-stream.service (streaming pipeline), portal-voice.service (voice pipeline), portal-llm.service (on-device LLM), portal-launcher.service (app launcher), and portal-pcp.service (capability protocol daemon). Environment variable templates are provided as .env.example files.
The portal/network/ directory contains the Wi-Fi access point configuration — hostapd-portal.conf (5 GHz AP for AR glasses connectivity), dnsmasq-portal.conf.tmpl (DHCP/DNS), NetworkManager.conf, and a udev rule for the uap0 interface. These create the dedicated network between the Spaceboard and the INMO AIR3 glasses.
The portal/compositor/ directory holds Wayfire configuration — wayfire.ini (production), wayfire-portal.ini, and spatial-warp.toml (warp renderer parameters). The portal/protocols/ directory contains portal-spatial-v1.xml, the custom Wayland protocol extension that defines the spatial compositor’s IPC interface.
Sources: portal/systemd/, portal/network/, portal/compositor/
Supporting Infrastructure
Section titled “Supporting Infrastructure”Fuzz Workspace
Section titled “Fuzz Workspace”The fuzz/ directory is a separate, standalone Cargo workspace (note its own [workspace] declaration in fuzz/Cargo.toml) that provides five libFuzzer targets targeting security-critical parsing code:
| Fuzz Target | Input Domain | Protects Against |
|---|---|---|
ipc_framing |
PCP IPC wire frames | Malformed protocol messages |
desktop_parser |
.desktop file entries |
Launcher injection via desktop entries |
rtp_depacketizer |
RTP packet sequences | Streaming pipeline crashes |
toml_config |
TOML configuration files | Config parsing panics |
gstreamer_argv |
Shell argument strings | GStreamer pipeline injection |
Each target has a corresponding corpus directory under fuzz/corpus/ for seed inputs.
Sources: fuzz/Cargo.toml#L1-L55, fuzz/
Benchmarks
Section titled “Benchmarks”The benches/ directory is a workspace member (portal-benches) containing three Criterion benchmarks: spatial_ipc (IPC throughput), rtp_encode (RTP packetization performance), and voice_tts (TTS latency). These measure the hot paths identified in the latency budget and are run via cargo bench -p portal-benches.
Sources: benches/Cargo.toml#L1-L34, Cargo.toml#L43-L44
Supply Chain Security
Section titled “Supply Chain Security”The supply-chain/ directory implements cargo-vet integration: audits.toml records per-crate security audits, imports.lock tracks audits imported from trusted third parties, config.toml configures the vet policy, and audit-priorities.md documents the triage order. Combined with deny.toml (cargo-deny), this provides a two-layer dependency governance framework — cargo-deny blocks vulnerable/unlicensed crates at build time, while cargo-vet records the human review that approved each dependency.
Sources: supply-chain/, deny.toml#L1-L21
Specification-Driven Development
Section titled “Specification-Driven Development”The openspec/ directory implements a spec-driven change management workflow. The specs/ subtree holds architecture specifications organized by subsystem (elara, input, launcher, pcp, style, terminal), while changes/ tracks proposed modifications as self-contained proposals (e.g., add-pcp-app-launch, add-pcp-cli-daemon-ipc). The config.yaml file configures the openspec tooling. Companion .claude/ and .opencode/ directories provide AI-assisted commands and skills for exploring, proposing, applying, and archiving these specifications.
Sources: openspec/, openspec/config.yaml#L1-L21
Build Scripts and Deployment Tooling
Section titled “Build Scripts and Deployment Tooling”The top-level scripts/ directory contains Python and shell automation for cross-cutting concerns that do not belong to a single crate:
| Script | Language | Purpose |
|---|---|---|
deploy/deploy.sh |
Shell | End-to-end deployment to the Spaceboard |
generate_sbom.sh |
Shell | Software Bill of Materials generation |
generate-manifest.py |
Python | Build manifest generation |
sign_release.sh |
Shell | Release artifact signing (cosign/minisign) |
verify_release.sh |
Shell | Release signature verification |
install-voice-models.sh |
Shell | Download and install STT/TTS/NLU model files |
hp_smoke_test.sh |
Shell | Smoke test suite for the Spaceboard |
scan_async_mutex.py |
Python | Static analysis for async mutex deadlocks |
The scripts/deploy/ subdirectory additionally contains aarch64-toolchain.cmake (CMake toolchain file for ARM64 cross-compilation) and ensure-hdmi-a2-resolution.sh (display configuration fixup).
Sources: scripts/, justfile#L1-L67
Cross-Language Components
Section titled “Cross-Language Components”Two directories live outside portal/ for components that are fundamentally separate from the Rust workspace:
The unity/ directory contains the Unity project for the INMO AIR3 glasses receiver — SpatialPortal_3DoF/ is the main project with 3DoF head tracking, and builds/ holds pre-built technical documentation. The .gitignore carefully excludes Unity build artifacts (Library/, Temp/, obj/) while tracking source assets.
The inmo_app_analysis/ directory contains reverse-engineering artifacts from the INMO AIR3 Android application — AndroidManifest.xml, decompiled Java sources, and resource files. This is a reference-only directory, not built code.
Sources: .gitignore#L70-L99, README.md#L122-L127
CI/CD and Quality Enforcement
Section titled “CI/CD and Quality Enforcement”The .github/workflows/ directory contains 14 GitHub Actions workflows that enforce quality gates on every push and pull request:
flowchart LR
PUSH[Push / PR] --> CHECK[check.yml<br/>fmt + clippy + docs + coverage]
PUSH --> TEST[test.yml<br/>unit + integration tests]
PUSH --> CROSS[cross-compile.yml<br/>aarch64 ARM64 build]
PUSH --> MIRI[miri.yml<br/>unsafe memory validation]
PUSH --> GUARD[guard-main.yml<br/>main branch protection]
PUSH --> SEMVER[semver.yml<br/>version compatibility]
PUSH --> GITLEAKS[gitleaks-scan.yml<br/>secret detection]
PUSH --> TRUFFLE[trufflehog-scan.yml<br/>verified secret scan]
RELEASE[Release Tag] --> REL[release.yml<br/>build + sign + publish]
RELEASE --> NOTIFY[notify-release.yml<br/>release notifications]
REL --> SBOM[SBOM generation]
REL --> SIGN[cosign signing]
SCHEDULED[Daily / Weekly] --> TR_DAILY[trufflehog-daily-subsystem.yml]
SCHEDULED --> TR_WEEKLY[trufflehog-weekly-repo.yml]
SCHEDULED --> SECURITY[security.yml]
The check.yml workflow runs four parallel jobs: fmt (formatting), clippy (two-tier linting — strict for --lib --bins, permissive for --tests), docs (cargo doc with -D warnings), and coverage (cargo-llvm-cov with an 85% line coverage floor). The test workflow (test.yml) runs cargo test in three phases: library tests, integration tests, and doc tests.
Locally, the justfile provides shorthand commands: just build-rust (full workspace build for ARM64), just build-cpp (Wayfire plugins), just test (workspace tests), and just clean (all artifacts). The .pre-commit-config.yaml adds two-layer secret scanning — Gitleaks (fast pattern-matching on staged changes) and TruffleHog (deep verification against live services) — both running before any commit enters history.
Sources: .github/workflows/check.yml#L1-L87, .github/workflows/test.yml#L1-L26, justfile#L1-L67, .pre-commit-config.yaml#L1-L35
File and Directory Quick Reference
Section titled “File and Directory Quick Reference”| Directory | What Lives Here | Build System |
|---|---|---|
portal/*/ |
Rust workspace crates (30 members) | Cargo (workspace) |
portal/streaming/ |
C streaming pipeline (GStreamer, H.265) | Makefile + shell |
portal/wayfire-plugins/ |
C++ Wayfire plugin shims | CMake |
portal/systemd/ |
Systemd service units | N/A (deployed as-is) |
portal/network/ |
Network configuration (hostapd, dnsmasq) | N/A (deployed as-is) |
portal/compositor/ |
Wayfire config files | N/A (deployed as-is) |
portal/protocols/ |
Wayland protocol XML | wayland-scanner |
portal/apparmor/ |
AppArmor profiles | N/A (deployed as-is) |
portal/scripts/ |
Build/deploy shell scripts | N/A |
portal/android/ |
Android receiver app | Gradle |
fuzz/ |
libFuzzer targets (5) | Cargo (standalone workspace) |
benches/ |
Criterion benchmarks (3) | Cargo (workspace member) |
supply-chain/ |
cargo-vet audits + config | N/A |
scripts/ |
Python/shell deployment automation | N/A |
unity/ |
Unity 3DoF receiver project | Unity Editor |
openspec/ |
Spec-driven change proposals | openspec tooling |
.github/workflows/ |
CI/CD pipeline definitions (14) | GitHub Actions |
Sources: README.md#L92-L127, Cargo.toml#L3-L45, fuzz/Cargo.toml#L1-L55
Where to Go Next
Section titled “Where to Go Next”Now that you understand the layout, these pages will help you take the next steps:
- Quick Start: Building and Deploying on the Spaceboard — hands-on guide to building the workspace and deploying to hardware
- Development Workflow: Local Coding to Remote Deployment — the code → SSH build → deploy loop
- ARM64 Sandbox Environment Setup — setting up a local ARM64 Docker sandbox for quick testing
- CI/CD Pipeline: Lint Enforcement, Cross-Compilation, and Release Signing — deep dive into the 14 GitHub Actions workflows
- Testing Strategy: Unit Tests, Property Tests, Fuzzing, and Benchmarks — how tests, fuzz targets, and benchmarks are organized