Skip to content

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.

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

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

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 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 documentation

Each 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

Three directories contain compiled code that is not part of the Cargo workspace:

The portal/streaming/ directory holds the primary streaming pipeline written in Cportal_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

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 configurationhostapd-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 configurationwayfire.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/

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/

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

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

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

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

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

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

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

Now that you understand the layout, these pages will help you take the next steps: