Skip to content

Latest commit

 

History

History
255 lines (178 loc) · 16.1 KB

File metadata and controls

255 lines (178 loc) · 16.1 KB

Contributing

This guide is for contributing to the Start9 monorepo (StartOS and the other products that live here). If you are interested in packaging a service for StartOS, visit the packaging guide. If you are interested in promoting, providing technical support, creating tutorials, or helping in other ways, please visit the Start9 website.

This file covers what is common to the whole monorepo — the shared toolchain, branch policy, the cross-cutting test/format entry points, and code/commit conventions. Per-product system dependencies, build targets, deploy steps, and release procedure live in that product's own scope — its AGENTS.md (e.g. projects/start-sdk/AGENTS.md), or its CONTRIBUTING.md in scopes not yet migrated (e.g. projects/start-os/CONTRIBUTING.md for building the StartOS OS image). See AGENTS.md for that migration and the reasoning behind it.

Documentation

The repo root's docs split across four files:

  • README.md — what this is
  • ARCHITECTURE.md — how it's built (the monorepo layout)
  • CONTRIBUTING.md — this file; how to contribute
  • AGENTS.md — AI-developer/agent operating rules (CLAUDE.md is a one-line @AGENTS.md import)

These docs must be kept up to date. When you change project structure, conventions, build process, or product context, update the relevant file(s) in the same change — do not defer. Each product and shared library keeps its own README.md/ARCHITECTURE.md/AGENTS.md for what is specific to it (most still carry a CONTRIBUTING.md too, which is being folded into that scope's AGENTS.md — see AGENTS.md) — see projects/*/, shared-libs/crates/start-core/, shared-libs/ts-modules/, and projects/start-os/container-runtime/.

The user-facing books, and live-docs

Each product's end-user documentation is an mdBook under projects/<product>/docs/, and a user-visible change updates it in the same PR (see AGENTS.md). Those edits go to master like any other change — but master's books describe what is next, not what is published.

docs.start9.com serves the live-docs branch. A book goes live when its product is tagged: the tag sync copies that tag's docs/ tree, plus the shared projects/start-docs/ site infrastructure, onto live-docs and redeploys. So the site always matches released software, and docs for an unreleased version can sit in master indefinitely without leaking.

To fix something that is already published — a typo, a broken link, a wrong instruction on the live site — PR it against live-docs directly. It deploys on merge, and is then pushed to master automatically so the next tag doesn't revert it. That automatic backport is direct-to-master and needs no second review; only a conflict with master falls back to a PR, and that PR is yours to resolve.

Collaboration

Environment Setup

Debian/Ubuntu is the only officially supported build environment. MacOS has limited build capabilities and Windows requires WSL2.

The shared toolchain below is enough to build the Rust bins and the web apps. Individual products need more — most notably the StartOS OS image, which adds multi-arch emulation and image-packaging tooling. See each product's CONTRIBUTING.md for its additional system dependencies.

Web-UI work skips most of this. The Angular front ends build and run standalone against mock data — they need only Node 24 and Make, no Rust, Docker, or OS-image tooling. See shared-libs/ts-modules/CONTRIBUTING.md.

# Common build tooling
sudo apt update
sudo apt install -y ca-certificates curl gpg build-essential git \
  sed grep gawk jq gzip brotli rsync

# Container backend (Docker) — used by .s9pk packaging and OS image builds
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo "deb [arch=$(dpkg-architecture -q DEB_HOST_ARCH) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian bookworm stable" | sudo tee /etc/apt/sources.list.d/docker.list
sudo apt update
sudo apt install -y containerd.io docker-ce docker-ce-cli docker-compose-plugin
sudo usermod -aG docker $USER
sudo su $USER

# Rust (stable; rustfmt runs in a pinned-nightly container — see Formatting)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh # proceed with default installation

# Node.js 24 (required by Angular 22's CLI)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/master/install.sh | bash
source ~/.bashrc
nvm install 24
nvm use 24
nvm alias default 24 # this prevents your machine from reverting back to another version

Cloning

git clone https://github.com/Start9Labs/start-technologies.git
cd start-technologies

Branches

master is always shippable. Nothing lands on it until it is finished and tested — every push to master deploys straight to the alpha channel (see Channels). Target it directly with a PR when your change is ready to ship on its own.

integration/<topic> is for work that isn't. When a change is too large or too risky to land piecemeal — a refactor spanning several products, a feature that only makes sense once all its parts exist — open an integration/ branch named for the work (integration/tunnel-v2), land the pieces there as ordinary PRs, and soak it as a unit. The PR that merges it into master is the last gate before it ships, so CI builds the full set of flashable images on that PR specifically, whatever paths it touched. Delete the branch once it merges. If you are unsure whether your change needs one, ask a maintainer.

The old next/patch | next/minor | next/major branches are retired; CI no longer builds them.

live-docs is what docs.start9.com serves. It is not a development branch — see The user-facing books, and live-docs.

Channels

Alpha is continuously deployed from master; nothing else is. Releases are cut deliberately from a merged master with scripts/manage-release.sh (see AGENTS.md).

Product What a master push publishes
StartOS images to S3, indexed into the alpha registry
start-cli, StartTunnel, start-registry .debs into the alpha apt suite (apt/start9-alpha.list)
StartWRT images into the alpha registry, only once STARTWRT_ALPHA_REGISTRY is set
Start SDK nothing — npm versions are permanent, so the SDK publishes only on release

The alpha apt suite is signed with a CI-only key, not the production release key, and carries its own keyring (signed-by=/usr/share/keyrings/start9-alpha.gpg). Trusting alpha therefore never implies trusting it to publish stable; the two suites share a bucket but nothing else.

Alpha builds are release-equivalentENVIRONMENT is empty on master, not dev. Alpha is the source registry that beta and then production are promoted from, and promotion copies each asset's URL and signed commitment through verbatim, so the bytes a master push produces are the bytes that eventually ship. Builds from PRs and integration/* branches still default to dev (password SSH before setup, uncompressed frontends), since those are for testing rather than promotion.

To put a CI build on a server without building locally:

make start-os-update-from-gha REMOTE=start9@<ip>              # latest master build
make start-os-update-from-gha REMOTE=start9@<ip> RUN_ID=<id>  # a specific run

Building

This is a monorepo: one root Cargo workspace and one Angular workspace, both rooted at the repo root. The root Makefile is a thin orchestrator (it includes each product's build.mk) — run make with no target to print a help summary; there is no default target. Run build commands from the repo root.

  • A single Rust bin: cargo build -p <crate> --bin <bin> — crates are start-os (startbox / start-container), start-cli, start-registry (registrybox), start-tunnel (tunnelbox), and startwrt-core (startwrt).
  • A whole product (bins + UI + packaging) has its own make targets and build instructions in its CONTRIBUTING.md:
Product Primary build target Build & deploy docs
StartOS (OS image, UIs, device deploy) make start-os projects/start-os/CONTRIBUTING.md
start-cli make start-cli projects/start-cli/CONTRIBUTING.md
start-registry make start-registry projects/start-registry/CONTRIBUTING.md
StartTunnel make start-tunnel projects/start-tunnel/CONTRIBUTING.md
StartWRT make start-wrt (make start-wrt-image for the full OpenWrt image — hours, fetches the pinned OpenWrt tree) projects/start-wrt/CONTRIBUTING.md
Start SDK make bundle (from projects/start-sdk) projects/start-sdk/AGENTS.md
Web (shared libs + app UIs) npm run build:ui shared-libs/ts-modules/CONTRIBUTING.md

make start-core-ts-bindings regenerates the TypeScript bindings from the Rust types, and make clean removes all compiled artifacts. Cross-layer changes (Rust → bindings → SDK → web/runtime) are described in ARCHITECTURE.md.

Build configuration

Builds are parameterized by environment variables shared across all products:

Variable Description
PLATFORM Target platform (e.g. x86_64, aarch64, riscv64). For non-OS products it only derives ARCH.
ENVIRONMENT Hyphen-separated feature flags; the available options depend on the product.
PROFILE Build profile: release (default) or dev.
GIT_BRANCH_AS_HASH Set to 1 to use the git branch name as the version hash (avoids rebuilds).

Each product's CONTRIBUTING.md documents the PLATFORM values and ENVIRONMENT flags it actually supports.

Testing

make test                    # all tests
make start-core-test               # Rust (shared-libs/crates/start-core)
make start-sdk-test                # SDK
make container-runtime-test  # container runtime
make start-wrt-test           # StartWRT Rust crates

# Run a specific Rust test
cd shared-libs/crates/start-core && cargo test <test_name> --features=test

Each product's CONTRIBUTING.md covers its own scoped tests.

Formatting

Three tools, one config each at the repo root: rustfmt (rustfmt.toml) for Rust, prettier (.prettierrc.json) for TS/JS/HTML/SCSS/Markdown/YAML/JSON, and taplo (taplo.toml) for TOML.

make format          # format the whole repo
make format-check    # read-only check (what CI runs)

rustfmt uses options that are still nightly-only, so to keep output identical for everyone it runs in a pinned-nightly container — build/fmt/fmtenv.Dockerfile, which adds the pinned nightly to start9/cargo-zigbuild (the same image the Rust build uses) and is built on first use. prettier and taplo are pinned via npm devDependencies and run natively. To bump a version, edit the Dockerfile's RUSTFMT_TOOLCHAIN (rustfmt) or package.json (prettier / @taplo/cli).

If you already have the pinned nightly installed and want to skip Docker:

FMT_NATIVE=1 make format

Or scope Rust formatting to one crate (still through the container):

make start-core-format   # shared Rust crates
make start-cli-format    # also start-registry-format / start-tunnel-format / start-os-format / start-wrt-format
make web-format          # prettier over the whole repo

Run the formatters before committing. A git pre-commit hook (husky + lint-staged) auto-runs prettier on staged files once you've run npm ci, so a missed format won't reach CI; it no-ops when dependencies aren't installed. CI enforces formatting regardless: a fast prettier --check gate runs on every pull request (including docs-only ones) and blocks the slower jobs, with make format-check as the source of truth.

Code Style Guidelines

Documentation & Comments

Rust:

  • Add doc comments (///) to public APIs, structs, and non-obvious functions
  • Use // comments sparingly for complex logic that isn't self-evident
  • Comments should be shorthand, not prose. Most comments can say what they need to in a single line.

TypeScript:

  • Document exported functions and complex types with JSDoc
  • Keep comments focused on "why" rather than "what"

General:

  • Don't add comments that just restate the code
  • Update or remove comments when code changes
  • TODOs should include context: // TODO(username): reason

Commits / PRs

Use Conventional Commits:

<type>(<scope>): <description>

[optional body]

[optional footer]

Types:

  • feat - New feature
  • fix - Bug fix
  • docs - Documentation only
  • style - Formatting, no code change
  • refactor - Code change that neither fixes a bug nor adds a feature
  • test - Adding or updating tests
  • chore - Build process, dependencies, etc.

Examples:

feat(web): add dark mode toggle
fix(core): resolve race condition in service startup
docs: update CONTRIBUTING.md with style guidelines
refactor(sdk): simplify package validation logic

Licensing

This repository is MIT. By contributing you agree your work is licensed under the MIT License in LICENSE, and that you have the right to license it that way.

If a change vendors third-party code into the tree, it must keep the upstream copyright notice and be added to NOTICE.md in the same PR. Prefer a build-time dependency over a checked-in copy.

Don't link GPL or AGPL code into a StartOS binary: the combined work would have to be conveyed under those terms. LGPL and MPL-2.0 code may be linked — both permit it — but each carries obligations we then owe downstream, so raise it before adding one. deny.toml encodes which licenses are accepted.