- 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
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:
- Rust:
cargo check -p start-os— verifies core compiles - TS bindings:
make ts-bindings— regenerates TypeScript types from Rust#[ts(export)]structs- Runs
./core/build/build-ts.shto export ts-rs types tocore/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)
- Runs
- SDK bundle:
cd sdk && make baseDist dist— compiles SDK source into packagesbaseDist/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
- Web type check:
cd web && npm run check— type-checks all Angular projects - 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 asstartd)start-cli- CLI interfacestart-container- Runs inside LXC containers; communicates with host and manages subcontainersregistrybox- Registry daemontunnelbox- 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.rssynced to frontend,private.rsbackend-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 interfaceprojects/setup-wizard/- Initial setupprojects/start-tunnel/- VPN management UIprojects/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 statedb.mutate(|db| { ... }).await- Apply mutations atomically, returnsMutateResult#[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 toT.ser(&value)- Serialize fromT.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 configurationcore-rust-patterns.md- Common utilities and patterns for Rust code in/core(guard pattern, mount guards, etc.)s9pk-structure.md- S9PK package format structurei18n-patterns.md- Internationalization key conventions and usage in/core
Session Startup
On startup:
-
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. -
Check
agents/TODO.mdfor relevant tasks - Show TODOs that either:- Have no
@usernametag (relevant to everyone) - Are tagged with the current user's identifier
Skip TODOs tagged with a different user.
- Have no
-
Ask "What would you like to do today?" - Offer options for each relevant TODO item, plus "Something else" for other requests.