Files
start-os/CLAUDE.md
Aiden McClelland 4e638fb58e feat: implement preferred port allocation and per-address enable/disable
- Add AvailablePorts::try_alloc() with SSL tracking (BTreeMap<u16, bool>)
- Add DerivedAddressInfo on BindInfo with private_disabled/public_enabled/possible sets
- Add Bindings wrapper with Map impl for patchdb indexed access
- Flatten HostAddress from single-variant enum to struct
- Replace set-gateway-enabled RPC with set-address-enabled
- Remove hostname_info from Host; computed addresses now in BindInfo.addresses.possible
- Compute possible addresses inline in NetServiceData::update()
- Update DB migration, SDK types, frontend, and container-runtime
2026-02-10 17:38:51 -07:00

7.2 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Project Overview

StartOS is an open-source Linux distribution for running personal servers. It manages discovery, installation, network configuration, backups, and health monitoring of self-hosted services.

Tech Stack:

  • Backend: Rust (async/Tokio, Axum web framework)
  • Frontend: Angular 20 + TypeScript + TaigaUI
  • Container runtime: Node.js/TypeScript with LXC
  • Database/State: Patch-DB (git submodule) - storage layer with reactive frontend sync
  • API: JSON-RPC via rpc-toolkit (see agents/rpc-toolkit.md)
  • Auth: Password + session cookie, public/private key signatures, local authcookie (see core/src/middleware/auth/)

Build & Development

See CONTRIBUTING.md for:

  • Environment setup and requirements
  • Build commands and make targets
  • Testing and formatting commands
  • Environment variables

Quick reference:

. ./devmode.sh                            # Enable dev mode
make update-startbox REMOTE=start9@<ip>   # Fastest iteration (binary + UI)
make test-core                            # Run Rust tests

Verifying code changes

When making changes across multiple layers (Rust, SDK, web, container-runtime), verify in this order:

  1. Rust: cargo check -p start-os — verifies core compiles
  2. TS bindings: make ts-bindings — regenerates TypeScript types from Rust #[ts(export)] structs
    • Runs ./core/build/build-ts.sh to export ts-rs types to core/bindings/
    • Syncs core/bindings/sdk/base/lib/osBindings/ via rsync
    • If you manually edit files in sdk/base/lib/osBindings/, you must still rebuild the SDK (step 3)
  3. SDK bundle: cd sdk && make baseDist dist — compiles SDK source into packages
    • baseDist/ is consumed by /web (via @start9labs/start-sdk-base)
    • dist/ is consumed by /container-runtime (via @start9labs/start-sdk)
    • Web and container-runtime reference the built SDK, not source files
  4. Web type check: cd web && npm run check — type-checks all Angular projects
  5. Container runtime type check: cd container-runtime && npm run check — type-checks the runtime

Important: Editing sdk/base/lib/osBindings/*.ts alone is NOT sufficient — you must rebuild the SDK bundle (step 3) before web/container-runtime can see the changes.

Architecture

Core (/core)

The Rust backend daemon. Main binaries:

  • startbox - Main daemon (runs as startd)
  • start-cli - CLI interface
  • start-container - Runs inside LXC containers; communicates with host and manages subcontainers
  • registrybox - Registry daemon
  • tunnelbox - VPN/tunnel daemon

Key modules:

  • src/context/ - Context types (RpcContext, CliContext, InitContext, DiagnosticContext)
  • src/service/ - Service lifecycle management with actor pattern (service_actor.rs)
  • src/db/model/ - Patch-DB models (public.rs synced to frontend, private.rs backend-only)
  • src/net/ - Networking (DNS, ACME, WiFi, Tor via Arti, WireGuard)
  • src/s9pk/ - S9PK package format (merkle archive)
  • src/registry/ - Package registry management

RPC Pattern: See agents/rpc-toolkit.md

Web (/web)

Angular projects sharing common code:

  • projects/ui/ - Main admin interface
  • projects/setup-wizard/ - Initial setup
  • projects/start-tunnel/ - VPN management UI
  • projects/shared/ - Common library (API clients, components)
  • projects/marketplace/ - Service discovery

Development:

cd web
npm ci
npm run start:ui        # Dev server with mocks
npm run build:ui        # Production build
npm run check           # Type check all projects

Container Runtime (/container-runtime)

Node.js runtime that manages service containers via RPC. See RPCSpec.md for protocol.

Container Architecture:

LXC Container (uniform base for all services)
└── systemd
    └── container-runtime.service
        └── Loads /usr/lib/startos/package/index.js (from s9pk javascript.squashfs)
            └── Package JS launches subcontainers (from images in s9pk)

The container runtime communicates with the host via JSON-RPC over Unix socket. Package JavaScript must export functions conforming to the ABI type defined in sdk/base/lib/types.ts.

/media/startos/ directory (mounted by host into container):

Path Description
volumes/<name>/ Package data volumes (id-mapped, persistent)
assets/ Read-only assets from s9pk assets.squashfs
images/<name>/ Container images (squashfs, used for subcontainers)
images/<name>.env Environment variables for image
images/<name>.json Image metadata
backup/ Backup mount point (mounted during backup operations)
rpc/service.sock RPC socket (container runtime listens here)
rpc/host.sock Host RPC socket (for effects callbacks to host)

S9PK Structure: See agents/s9pk-structure.md

SDK (/sdk)

TypeScript SDK for packaging services (@start9labs/start-sdk).

  • base/ - Core types, ABI definitions, effects interface (@start9labs/start-sdk-base)
  • package/ - Full SDK for package developers, re-exports base

Patch-DB (/patch-db)

Git submodule providing diff-based state synchronization. Changes to db/model/public.rs automatically sync to the frontend.

Key patterns:

  • db.peek().await - Get a read-only snapshot of the database state
  • db.mutate(|db| { ... }).await - Apply mutations atomically, returns MutateResult
  • #[derive(HasModel)] - Derive macro for types stored in the database, generates typed accessors

Generated accessor types (from HasModel derive):

  • as_field() - Immutable reference: &Model<T>
  • as_field_mut() - Mutable reference: &mut Model<T>
  • into_field() - Owned value: Model<T>

Model<T> APIs (from db/prelude.rs):

  • .de() - Deserialize to T
  • .ser(&value) - Serialize from T
  • .mutate(|v| ...) - Deserialize, mutate, reserialize
  • For maps: .keys(), .as_idx(&key), .as_idx_mut(&key), .insert(), .remove(), .contains_key()

Supplementary Documentation

The agents/ directory contains detailed documentation for AI assistants:

  • TODO.md - Pending tasks for AI agents (check this first, remove items when completed)
  • USER.md - Current user identifier (gitignored, see below)
  • rpc-toolkit.md - JSON-RPC patterns and handler configuration
  • core-rust-patterns.md - Common utilities and patterns for Rust code in /core (guard pattern, mount guards, etc.)
  • s9pk-structure.md - S9PK package format structure
  • i18n-patterns.md - Internationalization key conventions and usage in /core

Session Startup

On startup:

  1. Check for agents/USER.md - If it doesn't exist, prompt the user for their name/identifier and create it. This file is gitignored since it varies per developer.

  2. Check agents/TODO.md for relevant tasks - Show TODOs that either:

    • Have no @username tag (relevant to everyone)
    • Are tagged with the current user's identifier

    Skip TODOs tagged with a different user.

  3. Ask "What would you like to do today?" - Offer options for each relevant TODO item, plus "Something else" for other requests.