Thank you for your interest in contributing to NeMo Relay. This guide covers the development workflow, coding standards, and pull request process.
This section collects the setup steps needed before building, testing, or contributing changes.
If you are consuming NeMo Relay rather than developing this repository, install the published package for your language:
- Rust crate --
cargo add nemo-relay - Python package --
uv add nemo-relayorpip install nemo-relay - Node.js package --
npm install nemo-relay-node
Go, WebAssembly, and the raw FFI surface are currently experimental and remain source-first.
Install these tools before you start:
- Rust (stable toolchain) -- install with rustup
- Python >= 3.11 -- use uv for environment management
- just --
cargo install just --locked - Go >= 1.21
- Node.js (LTS)
- wasm-pack --
cargo install wasm-pack - cargo-deny --
cargo install cargo-deny
When you work in Go, WebAssembly, or the raw FFI surface, build and validate those bindings from source in the same branch.
Clone the repository and build the workspace:
git clone <repo-url> && cd NeMo-Relay
uv sync
cargo install just --locked
uv run pre-commit install
just build-rust
just build-python
just build-nodeValidate the source builds for the experimental bindings when you touch them:
# Go binding (requires the release FFI library)
cd go/nemo_relay
CGO_LDFLAGS="-L../../target/release" LD_LIBRARY_PATH="${LD_LIBRARY_PATH:+${LD_LIBRARY_PATH}:}../../target/release" go test -v ./...
cd ../..
# WebAssembly binding
wasm-pack test --node crates/wasmVerify everything works by running the test suites (see Testing Requirements below).
Use the following prefixes for branch names:
| Prefix | Purpose |
|---|---|
feat/ |
New features or capabilities |
fix/ |
Bug fixes |
docs/ |
Documentation-only changes |
test/ |
Test additions or modifications |
refactor/ |
Code restructuring without behavior changes |
Examples: feat/scope-context-managers, fix/node-wasm-silent-failures, docs/api-reference-update.
Versioned release tags must use raw Rust-compatible SemVer without a leading
v.
- Use
0.1.0for stable releases. - Use
0.1.0-rc.1for prereleases. - Do not create tags such as
v0.1.0orv0.1.0-rc.1.
This keeps release tags aligned with Cargo package versions and avoids
per-registry tag translation during packaging. CI rejects v-prefixed release
tags.
For the maintainer release process and release-notes policy, see
RELEASING.md.
These style requirements keep contributions consistent across Rust, Python, Go, and general repository files.
Use these Rust commands and conventions when changing the core runtime or Rust-facing API surface.
- Formatting:
cargo fmt(rustfmt defaults) - Linting:
cargo clippy -- -D warnings-- all warnings are treated as errors - Dependency auditing:
cargo deny check-- configured indeny.toml
Use these Python commands and conventions when changing the wrapper package, tests, or docs tooling.
- Linting: Ruff with rule sets
E,F,W,I - Formatting: Ruff formatter (line length 120, double quotes)
- Type checking: ty
Use these Go commands and conventions when changing the experimental Go binding.
- Formatting:
gofmt - Static analysis:
go vet ./...
These general conventions apply across files and language surfaces.
- Use the naming conventions appropriate to each language: Rust
snake_case, C FFI exports prefixednemo_relay_, GoPascalCase, Node.jscamelCase, Pythonsnake_case.
Pre-commit hooks are configured in .pre-commit-config.yaml and run automatically on every git commit. Install them after cloning:
uv run pre-commit installThe hooks enforce:
- General: trailing whitespace removal, end-of-file fixup, YAML/TOML/JSON validity, merge conflict marker detection, large file check (500 KB max)
- Docs: Fern validation for
docs/andfern/, plus external Markdown/MDX link checking forREADME.md,CONTRIBUTING.md, anddocs/vialychee - Python: Ruff linting and formatting, ty type checking
- Rust: FFI header sync for
crates/ffi/nemo_relay.hthrough Cargo/build.rs,cargo fmtformatting check,cargo clippylints,cargo denyauditing - Go:
gofmtformatting,go vetstatic analysis
To run all hooks manually against the entire codebase:
uv run pre-commit run --all-filesRun tests for every language affected by your changes. If your change touches the core Rust crate, run tests across all bindings since they all depend on it.
Run the affected test targets directly through the repository justfile:
just test-rust
just ci=true test-rust
just test-python
just test-go
just test-node
just test-wasmThose target recipes are the primary entrypoints for targeted reruns as well:
# Rust
just test-rust
# Python
just test-python
# Go (requires FFI lib built with --release)
just test-go
# Node.js (requires native addon built)
just test-node
# WebAssembly (unit tests)
just test-wasmWhen adding new functionality, include tests in the appropriate test files for each affected language binding. Tests are organized by topic: types, scope, tools, LLM, deregister, context isolation, and scope-local.
If your change affects public behavior, bindings, examples, or workspace structure, update the corresponding docs in the same branch.
Before opening a PR, check the following:
README.mdstill reflects the current workspace members and top-level docs.- The relevant reference docs are updated for any public API change.
- The relevant crate or package README is updated when that surface changed.
- Embedded documentation snippets, patch docs, and binding-support notes are updated if examples or supported bindings changed.
- For docs site changes, run
just docs(or./scripts/build-docs.sh htmlas a compatibility wrapper) — it regenerates ignored Fern API reference pages before validation.
For documentation-heavy changes, prefer small targeted commits so the history shows entry-point changes, reference changes, examples, and maintenance updates separately.
Every commit in a pull request must include a Developer Certificate of Origin sign-off.
Use git commit -s when you create commits, or add a Signed-off-by: trailer manually if you are fixing an older commit before review.
This section describes how to prepare and submit changes for review.
Complete these checks before opening or updating a pull request.
- Ensure all pre-commit hooks pass.
- Run the relevant test suites and confirm they pass.
- Verify your changes compile cleanly with the relevant
./scripts/build*.shentrypoint. - Update the relevant documentation entry points and references.
- Rebase your branch on the latest
mainto avoid merge conflicts.
Include the following in your pull request description:
- What: A concise summary of the change.
- Why: The motivation or issue being addressed.
- How: Key implementation details, especially for non-obvious design decisions.
- Testing: Which test suites you ran and any new tests added.
- Breaking changes: Note any API changes that affect existing users.
These expectations describe how review is handled before changes are merged.
- All PRs require at least one approving review before merge.
- Reviewers may request changes for code quality, test coverage, documentation, or architectural concerns.
- Address review feedback by pushing additional commits (do not force-push during review).
- CI must pass before merging.
- Scrutinize any
SONAR_IGNORE_START/SONAR_IGNORE_ENDmarkers and require reviewer sign-off plus a written justification before approving them.
Use SONAR_IGNORE_START / SONAR_IGNORE_END only for documented false
positives that cannot be resolved in code or by improving the analyzer
configuration. Keep the ignored block as small as possible, add a brief comment
explaining why the suppression is needed, and call it out in the PR description
so reviewers can explicitly sign off on it.
Use the following format for commit messages:
type: short description of the change
Optional longer description explaining the motivation and context.
Valid types:
| Type | Purpose |
|---|---|
feat |
New feature or capability |
fix |
Bug fix |
docs |
Documentation changes |
test |
Test additions or modifications |
refactor |
Code restructuring without behavior changes |
chore |
Build, CI, or tooling changes |
perf |
Performance improvements |
Examples:
feat: add scope context managers for automatic cleanup in Go, Node.js, and WebAssembly
fix: propagate JS callback errors instead of silent null fallback
docs: update API reference for typed wrapper methods
test: add context isolation tests for concurrent scope stacks
Keep the first line under 72 characters. Use the body for additional context when the change is not self-explanatory.
All source files must include an SPDX license header. Use the appropriate comment syntax for the file type:
Rust / Go / JavaScript / TypeScript:
// SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
Python:
# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
HTML / Markdown:
<!--
SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
SPDX-License-Identifier: Apache-2.0
-->
TOML:
# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
The pre-commit hooks do not currently enforce SPDX headers automatically, but reviewers will check for them during PR review.
Before making significant changes, read through the documentation in
docs/, especially:
- Architecture Overview -- runtime model and data flow
- Scopes -- scopes, handles, events, and runtime ownership
- Middleware -- execution ordering and middleware behavior
- API Reference -- public surfaces across Rust, Python, and Node.js
The codebase follows a layered architecture: Core (Rust) provides the runtime, with bindings through FFI (C, used by Go through CGo), PyO3 (Python), NAPI (Node.js), and wasm-bindgen (WebAssembly). Each binding mirrors the full API surface.