Contributing

August 19, 2026 ยท View on GitHub

Thanks for helping improve basic-webserver. Questions and early design discussions are welcome in the Roc Zulip chat.

Before making a change

Read design.md first. It is the authoritative description of the platform's intended architecture, ownership boundaries, supported scope, and non-goals. The implementation is still moving toward that design, so existing code is not evidence that a conflicting architecture is intentional.

Keep application policy in Roc, durable mutable state in SQLite or an external service, and bounded transport or operating-system resources in typed host subsystems. If a request conflicts with the design, call out the conflict instead of adding a workaround or silently expanding the platform's scope.

design.md records enduring what and why. Put implementation plans, migration status, and temporary constraints in code, focused documentation, issues, or pull requests.

Prefer simple solutions and a single source of truth where practical. Keep repository automation in scripts/ and write it in portable Python; extend an existing entry point when the responsibility fits, and add a new script only for a distinct reusable workflow.

Prerequisites

  • A recent Zig-based roc compiler on PATH. Build it from a Roc source checkout with zig build roc. The old Rust-based compiler is unsupported.
  • The Rust toolchain declared in rust-toolchain.toml. Rustup selects it automatically.
  • Python 3. Repository automation uses only the Python standard library.
  • Native build tools for your operating system. Windows host builds require MSVC and the Windows SDK.

The exact Roc nightly used for development, CI, and releases is recorded in .roc-version, and every examples/ manifest repeats it in its roc: entry. The CI action in .github/actions/setup-roc installs that version, and python scripts/test.py --operation validate fails when a manifest drifts from it. Move both to another nightly with:

python scripts/update_roc_version.py nightly-2026-08-13-2fdd90e

A scheduled workflow, update_roc_nightly.yml, runs that script for the latest nightly, pushes the result to the update-roc-nightly branch, runs CI with that nightly, and opens a pull request reporting whether it passed.

Build and run locally

Build the native host library:

python scripts/build.py

Then run an example:

roc examples/hello-web.roc

The server listens on http://127.0.0.1:8000 by default. Build one explicit target with python scripts/build.py --target TARGET, or every target buildable from the current host with python scripts/build.py --all. Linux builds the two musl targets; macOS builds the macOS and musl targets. The x64win host inputs must be built on Windows.

Verify a change

The normal local checks are:

cargo fmt --all -- --check
cargo test --locked
python scripts/test.py

Run python scripts/build.py first whenever the Rust host has changed or the native host library is absent.

scripts/test.py validates its own harness, formats and checks Roc sources, runs Roc tests, builds every active examples/*.roc application, and executes the cases in scripts/test_spec.json. Normal server cases use the real HTTP listener, including HTTP/2 coverage. The runner uses only Python's standard library and applies the same expectations on Linux, macOS, and Windows.

Files ending in .todoroc are intentionally skipped migration backlog. Active .roc examples must pass the suite on every supported operating system.

For a focused application build after building the host:

roc build examples/hello-web.roc

CI also checks every Linux example under Valgrind Memcheck. On x86-64 Linux with Valgrind installed, run the same lane with:

python scripts/test.py --operation memcheck

Add or change examples

Keep examples realistic. Prefer adding a case to an existing useful example, or add a new example that demonstrates a real platform use case instead of a test-only Roc application.

Every active examples/*.roc application must have exactly one entry in scripts/test_spec.json. Platform-specific skips are exceptional: each skip must include a concrete reason and a GitHub tracking issue URL. The validator rejects platform-specific expected results.

Generated Rust glue

Changes to the hosted or provides blocks in platform/main.roc require regenerating src/roc_platform_abi.rs. The compiler and RustGlue.roc must come from the matching revision recorded in scripts/regenerate_glue.py.

ROC_SRC=/path/to/roc python scripts/regenerate_glue.py
ROC_SRC=/path/to/roc python scripts/regenerate_glue.py --check

ROC_GLUE_SPEC=/path/to/RustGlue.roc can be used instead of ROC_SRC.

Generate API documentation

Generate and serve the same platform API documentation published for main:

roc docs platform/main.roc --serve

Open the local URL printed by Roc. The default output directory without --serve is generated-docs/.

Generated documentation is not committed. Each release attaches a docs.tar.gz asset holding the documentation for that version, and the published site is rebuilt from those assets plus freshly generated main documentation:

python scripts/restore_release_docs.py temp_docs

This needs an authenticated GitHub CLI (gh auth login).

The www directory holds documentation for releases up to 0.16.0, which predate the release asset and cannot be changed retroactively. It is copied into the published site alongside the restored archives, and is scheduled for deletion once 0.20.0 is released.

Benchmarking

Use the unified benchmark entrypoint for substituted-transport server invariants, allocation reports, real HTTP/SSE/SQLite load, process measurements, and before/after comparisons:

python scripts/benchmark.py check
python scripts/benchmark.py measure \
  --suite http \
  --label before \
  --output target/benchmarks/before.jsonl

See docs/benchmarking.md for the real-versus-simulated evidence boundary, thousand-stream SSE runs, scenario controls, recorded metrics, and comparison workflow. Local timing and memory values are indicative and are never CI thresholds.

Release validation

Source validation without building runtime artifacts is available separately:

python scripts/test.py --operation validate

Release CI builds all target host inputs, creates one platform bundle, has each compiler host cross-build every target from that bundle, and runs every independently produced artifact set on its native target.

After all declared target inputs have been assembled under platform/targets, create a release-format package with:

python scripts/bundle.py --output-dir dist

The bundler validates target completeness and Roc's transitive dependency size limit, then includes the required notices and exact Rust dependency licenses. The release workflow assembles Windows, macOS, and Linux inputs before invoking it.