This directory contains reusable composite actions and workflow definitions for CI/CD, testing, publishing, and automation within the NautilusTrader repository.
- attest-build-provenance-retry: wraps GitHub build provenance attestation with bounded retries.
- attest-sbom-retry: wraps Docker SBOM attestation with bounded retries.
- cargo-tool-install: installs cargo tools (cargo-deny, cargo-vet) with caching.
- common-setup: prepares the environment (OS packages, Rust toolchain, Rust cache, Python, prek, swap space).
- common-test-data: caches large test data under
tests/test_data/large. - common-wheel-build: builds and installs Python wheels across Linux, macOS, and Windows for multiple Python versions.
- generate-sbom-retry: wraps Docker SBOM generation with bounded retries.
- install-capnp: installs the Cap'n Proto compiler with caching across Linux, macOS, and Windows.
- publish-wheels: publishes built wheels to Cloudflare R2, manages old wheel cleanup and index generation.
- upload-artifact-wheel: uploads the latest wheel artifact to GitHub Actions.
- build.yml: main CI pipeline - plan, pre-commit, cargo-deny, Rust tests, Python tests, wheel builds, artifact uploads, release asset uploads, Trusted Publishing to PyPI and crates.io, release attestations, registry verification, release checksum publication, and final GitHub release publication and attestation verification. Uses Depot 8-core runners for Linux and Windows builds. Includes a plan step that skips builds on docs-only changes and skips Rust tests on Python-only changes.
- build-v2.yml: CI pipeline for the v2 Rust-native system. Runs Linux builds on the self-hosted
build-v2pool. - build-docs.yml: dispatches documentation build on
masterandnightlypushes. - cli-binaries.yml: builds and publishes CLI binaries for multiple platforms.
- codeql-analysis.yml: CodeQL security scans for Python and Rust on PRs to
master, pushes tonightly, and manual dispatch. - coverage.yml: coverage report generation, currently paused and runs only on
workflow_dispatch. - docker.yml: builds and pushes multi-platform Docker images (
nautilus_trader,jupyterlab) using Buildx and native ARM runners. - nightly-docs-features-check.yml: nightly docs.rs build checks and crate feature compatibility verification.
- nightly-merge.yml: auto-merges
developintonightlywhen CI succeeds. - nightly-tests.yml: extended test suites too slow for PR builds - turmoil network tests,
macOS, Windows, and Linux ARM build-and-test jobs, plus final Cargo publish-plan and dry-run
checks that run daily at 12:00 UTC to give early visibility on develop before
nightly-mergeat 14:00 UTC. - performance.yml: Rust/Python benchmarks on
nightly, reporting to CodSpeed. - security-audit.yml: nightly supply chain security checks (cargo-audit, cargo-deny, cargo-vet, pip-audit, osv-scanner, and Zizmor).
- openssf-scorecard.yml: OpenSSF Scorecard posture scan on weekly schedule and manual dispatch. The scheduled run publishes badge/API results; all runs upload SARIF to code scanning.
- CODEOWNERS: Critical infrastructure files (workflows, dependencies, build configs, scripts) require Core team review before merge.
- Branch and tag rulesets: Protected branches require signed commits and passing CI checks.
Release tags matching
v*are immutable after creation. External PRs must receive Core team approval before merge. - Least-privilege tokens: Workflows default
GITHUB_TOKENtocontents: read, actions: readand selectively elevate scopes only for jobs that need them. - Secret management: No secrets or credentials are stored in the repo. Credentials are provided via GitHub Secrets and injected at runtime.
- Dependency pinning: Key tools (prek, Python versions, Rust toolchain, cargo-nextest, uv) are
locked to fixed versions or SHAs. The uv version is pinned via
required-versioninpyproject.tomland extracted byscripts/uv-version.shfor CI, Docker, and local builds. Release and audit helper Python CLIs are pinned intools.toml. - Dependency cooldown: Python dependency resolution excludes packages published within the last
3 days (
exclude-newer = "3 days"in[tool.uv]). This gives the community time to detect and quarantine compromised releases before they enter the lockfile.
- cargo-deny: Rust dependency auditing for security advisories (RUSTSEC/GHSA), license
compliance, banned crates, and supply chain integrity. Configuration in
deny.toml. - Code scanning: CodeQL analyzes Python and Rust code on PRs to
master, pushes tonightly, and manual dispatch. Zizmor runs insecurity-audit.ymland uploads SARIF when token permissions allow it. - OpenSSF Scorecard:
openssf-scorecard.ymlpublishes repository posture results for the public badge/API and uploads SARIF to code scanning.
- Immutable action pinning: All third-party GitHub Actions are pinned to specific commit SHAs.
- Docker image pinning: Base images in Dockerfiles and service containers in workflows are pinned to SHA256 digests to prevent supply-chain attacks via tag mutation.
- Build attestations: Python wheels and sdists receive GitHub artifact attestations and PyPI
publish attestations. Docker images receive cosign signatures and SPDX SBOM attestations. Verify
Python artifacts via
gh attestation verifyand container images viacosign verify. - Release sequencing: Stable releases create a draft GitHub release first, attach wheel and
sdist assets, publish to package indexes (
packages.nautechsystems.io, PyPI, crates.io), verify registries, attach final integrity assets, then publish the GitHub release. This keeps the GitHub release as the anchor for downstream registry publishing while staying compatible with GitHub release immutability. - Release checksums: GitHub releases attach
SHA256SUMS, per-asset.sha256files,dist-manifest.json, and per-artifact.sigstore/.intoto.jsonlprovenance bundle siblings for Python artifacts. The release body also includes a generated artifact checksum table and provenance verification command. - PyPI Trusted Publishing:
publish-wheels-pypiandpublish-sdist-pypiupload to PyPI via OIDC trusted publishing rather than a long-lived API token. The trusted publisher on PyPI is bound to reponautechsystems/nautilus_trader, workflowbuild.yml, and environmentrelease;uv publish --trusted-publishing automaticmints a short-lived token at publish time. NoPYPI_*secret is required. - crates.io Trusted Publishing:
publish-cargo-cratespublishes Cargo crates via crates.io OIDC trusted publishing. The trusted publisher on crates.io must be configured per crate for reponautechsystems/nautilus_trader, workflowbuild.yml, and environmentrelease; the job uses a short-lived token fromrust-lang/crates-io-auth-actionand no long-lived cargo token. - Post-publish verification:
publish-release-integrityverifies PyPI files againstdist-manifest.json, verifies PyPI provenance publisher metadata, and verifies crates.io entries were trusted-published by this repository. These verifier calls retry transient Sigstore/Rekor/TUF lag, while provenance and identity mismatches fail fast. The job records whether each crate matches the release commit, was already published, or matched an explicitCRATES_IO_MANUAL_PUBLISH_EXCEPTIONScrate@versionentry for emergency token-publish recovery. Manual entries are recorded incrates-manifest.jsonwithrelease_status: "manual_token_publish". Malformed or unused exception entries fail the job. The job uploadscrates-manifest.json, attaches attestation siblings, and cleans up release workflow artifacts.publish-github-releasethen publishes the draft release and verifies GitHub's release attestation. - Caching: Rust target directory cache (
Swatinem/rust-cache), prek hook environments, and test data caches speed up workflows while preserving hermetic builds. Rust cache saves are restricted to push events to prevent PR cache pollution. - Concurrency: PR CI runs are cancelled when a new push arrives to the same PR. Push events to mainline branches are never cancelled.
- Runners: Linux and Windows builds use Depot 8-core runners (32 GB RAM, 150 GB SSD). macOS
builds use GitHub free runners. Lightweight jobs (plan, cargo-deny, cargo-vet, publish) use
GitHub free runners. Custom runner labels are declared in
.github/actionlint.yaml.
- Hardened runners: All workflows employ
step-security/harden-runnerto reduce attack surface and monitor outbound traffic. All workflows defaultegress-policytoblock. SetSTEP_SECURITY_EGRESS_POLICY=auditonly as a temporary rollback while expanding an allow list. Jobs that declare a GitHub Environment can override the repo or org value with an environment-scoped variable. The publish environments (r2-develop,r2-nightly,release) can use this override too. Security audit jobs read repo and org variables directly and run in audit mode for fork PRs when variables are absent. - Fork PR handling:
build.ymlfalls back toegress-policy: auditfor fork PRs. Forks cannot access repo or org variables, so the allow lists would be empty and block all network access. Fork PRs run with read-only permissions and no access to secrets, so audit mode is safe.
The security-gate-nightly job runs cargo audit and osv-scanner to catch vulnerabilities
before publishing. Occasionally, upstream events outside our control (transitive dependency
advisories, crate yanks for non-security reasons) can block the nightly pipeline with no
actionable fix on our side.
The repo-scoped variable SECURITY_GATE_OVERRIDE holds an ISO 8601 UTC timestamp
(e.g. 2026-03-28T02:00:00Z). When the current time is before the timestamp, the security
gate is skipped. When the timestamp passes, the gate re-enables automatically with no manual
reset. The variable will be left unset for normal operations.
A repo admin will thoroughly assess all flagged items before setting the timestamp, and will scope it to the minimum window needed for the blocked build to complete:
date -u -d '+2 hours' --iso-8601=seconds # e.g. 2 hour window
Modifying repo variables requires admin access. An attacker with that level of access can already disable workflows or push directly, so the override does not widen the attack surface.
cargo audit catches CVEs and unsound code advisories independent of yank status. A crate
yanked for non-security reasons (MSRV mistakes, broken builds, accidental publishes) produces
a warning but does not indicate a vulnerability.
The step-security/harden-runner action restricts network access to approved endpoints.
All three variables are stored in GitHub as single-line, space-delimited values. The pinned
step-security/harden-runner version does not enforce newline-delimited values correctly
in block mode.
All workflows read these GitHub variables:
STEP_SECURITY_EGRESS_POLICY: StepSecurity egress mode for the job. Workflows default toblock. Setauditonly as a temporary override while expanding an allow list.COMMON_ALLOWED_ENDPOINTS: Endpoints needed by every job (GitHub API, Ubuntu packages, tooling).CI_ALLOWED_ENDPOINTS: Extra endpoints shared by the main CI, nightly, docs, and release workflows.SECURITY_AUDIT_ALLOWED_ENDPOINTS: Extra endpoints needed by the security audit jobs.
Some workflows add job-specific endpoints inline (e.g., upload.pypi.org:443 for publishing,
auth.docker.io:443 and registry-1.docker.io:443 for Docker builds, and Scorecard publishing
plus lookup endpoints such as api.scorecard.dev:443, fulcio.sigstore.dev:443, and
tuf-repo-cdn.sigstore.dev:443).
Security audit jobs do not use deployment environments. They do not need environment secrets, and environment branch policies block same-repo contributor PRs before the audit steps can start.
api.github.com:443 # GitHub API
github.com:443 # GitHub main site
artifacts.githubusercontent.com:443 # GitHub Actions artifacts
codeload.github.com:443 # GitHub code downloads
raw.githubusercontent.com:443 # Raw file access
uploads.github.com:443 # GitHub uploads
objects.githubusercontent.com:443 # GitHub objects storage
pipelines.actions.githubusercontent.com:443 # Actions pipelines
tokens.actions.githubusercontent.com:443 # Actions tokens
github-cloud.githubusercontent.com:443 # GitHub cloud content
github-cloud.s3.amazonaws.com:443 # GitHub S3 storage
media.githubusercontent.com:443 # GitHub media content
archive.ubuntu.com:443 # Ubuntu package archives
security.ubuntu.com:443 # Ubuntu security updates
azure.archive.ubuntu.com:443 # Azure Ubuntu mirrors
ports.ubuntu.com:443 # Ubuntu ports archives
changelogs.ubuntu.com:443 # Ubuntu changelogs
esm.ubuntu.com:443 # Ubuntu ESM (extended security)
motd.ubuntu.com:443 # Ubuntu MOTD updates
astral.sh:443 # UV/Ruff tooling
proxy.golang.org:443 # Go module proxy (shfmt pre-commit hook)
sum.golang.org:443 # Go checksum database
storage.googleapis.com:443 # Go module downloads (via proxy)
registry.npmjs.org:443 # npm packages (actionlint hook)
api.snapcraft.io:443 # Ubuntu snap API (runner infra)
artifactcache.actions.githubusercontent.com:443 # Actions cache
github-releases.githubusercontent.com:443 # GitHub release downloads
launch.actions.githubusercontent.com:443 # Actions launch
results-receiver.actions.githubusercontent.com:443 # Actions results
release-assets.githubusercontent.com:443 # Release assets
hosted-compute-request-orchestrator-prod-iad-01.githubapp.com:443 # Runner orchestration
hosted-compute-request-orchestrator-prod-iad-02.githubapp.com:443 # Runner orchestration
hosted-compute-watchdog-prod-iad-01.githubapp.com:443 # Runner watchdog
hosted-compute-watchdog-prod-iad-02.githubapp.com:443 # Runner watchdog
packages.microsoft.com:443 # Microsoft packages
sh.rustup.rs:443 # Rust toolchain installer
static.rust-lang.org:443 # Rust toolchain downloads
crates.io:443 # Rust crate registry
index.crates.io:443 # Rust crate index
static.crates.io:443 # Rust crate downloads
pypi.org:443 # Python packages
files.pythonhosted.org:443 # Python package files
capnproto.org:443 # Cap'n Proto compiler
packages.nautechsystems.io:443 # Nautech packages
test-data.nautechsystems.io:443 # Nautech test data
formulae.brew.sh:443 # Homebrew formulae
community.chocolatey.org:443 # Chocolatey community
chocolatey.org:443 # Chocolatey packages
packages.chocolatey.org:443 # Chocolatey downloads
archive.ubuntu.com:80 # Ubuntu archives (HTTP)
security.ubuntu.com:80 # Ubuntu security (HTTP)
azure.archive.ubuntu.com:80 # Azure Ubuntu (HTTP)
ports.ubuntu.com:80 # Ubuntu ports (HTTP)
fulcio.sigstore.dev:443 # Sigstore certificate authority
rekor.sigstore.dev:443 # Sigstore transparency log
codspeed.io:443 # CodSpeed benchmarking
static.rust-lang.org:443 # Rust toolchain downloads
crates.io:443 # Rust crate registry
index.crates.io:443 # Rust crate index
static.crates.io:443 # Rust crate downloads
pypi.org:443 # Python packages
files.pythonhosted.org:443 # Python package files
api.osv.dev:443 # OSV vulnerability database
release-assets.githubusercontent.com:443 # GitHub release assets
GitHub-hosted runners contact Azure infrastructure at fixed IPs that are allowed by default at the VM level and do not need to be in the allow lists:
168.63.129.16:80-- Azure IMDS/wireserver (DHCP, DNS forwarding, health probes)168.63.129.16:53-- Azure DNS resolver
Action Update Policy: When updating GitHub Actions, only use versions that have been released for at least 2 weeks. This allows time for the community to identify potential issues while maintaining security through timely updates.
For updates or changes to actions or workflows, please adhere to the repository's CONTRIBUTING guidelines and maintain these security best practices.