* feat(sdk): add TypeScript SDK (@nvidia/openshell-sdk) First native, per-language SDK for the OpenShell gateway: a thin, idiomatic TypeScript client over proto-generated gRPC stubs (connect-es), no FFI. Covers the v0.1 surface — sandbox lifecycle (create/get/list/delete + waitReady/ waitDeleted), health, and streamed exec. - sdk/typescript/: package, client/transport/errors, protoc + protoc-gen-es codegen (gen/ gitignored, absorbed into dist/ at build), committed lockfile. - tasks/typescript.toml: sdk:ts install/proto/typecheck/build/ci/publish; sdk:ts:typecheck wired into `check`; sdk-typescript job in branch-checks (typecheck, build, and a --dry-run publish that validates the release path). - Enforce SPDX headers on .ts/.tsx/.mts/.cts (skip node_modules and gen/); back-fill docs/_components/jsx.d.ts and fern/components/CustomFooter.tsx. - release.py gains an npm version format; release-tag.yml publishes to GitHub Packages on tag, stamping the version (0.0.0 placeholder in git); prerelease builds publish under the `next` dist-tag, not `latest`. Ships as @nvidia/openshell-sdk on GitHub Packages pre-GA; public npm (@openshell/sdk) follows at GA with an unchanged public API. Signed-off-by: Max Dubrinsky <mdubrinsky@nvidia.com> * chore(sdk): adopt TypeScript 6, tidy @types/node range - typescript ^5.7.2 -> ^6.0.3 (6.0 is now `latest`; the old caret capped at 5.x) - @types/node ^24.0.0 -> ^24 (same range, tidier) No source changes; codegen, typecheck, and build pass on 6.0.3. Verified the emitted d.ts still type-check for downstream consumers on TypeScript 5.0.4 through 5.9.3, so this does not raise the SDK's consumer TS floor. Signed-off-by: Max Dubrinsky <mdubrinsky@nvidia.com> * refactor(sdk): group operations under a composable SandboxClient Reshape the client from flat methods (createSandbox, listSandboxes, exec) to a scoped SandboxClient reached as `client.sandbox.create/get/list/delete/exec` (+ waitReady/waitDeleted), mirroring the CLI's noun-verb model and the Python SDK's SandboxClient. SandboxClient is also usable standalone via SandboxClient.connect(); OpenShellClient composes it over a single shared transport, so future service/provider clients reuse one connection. health() stays top-level as a gateway call. No behavior change; types are unchanged. Signed-off-by: Max Dubrinsky <mdubrinsky@nvidia.com> * chore(sdk): generate TypeScript SDK stubs with buf Replace the protoc gen.sh with `buf generate` + buf.gen.yaml. `buf` (@bufbuild/buf) is a package devDependency and self-compiles the protos, so the TS SDK no longer depends on the mise-pinned protoc; it drives the same connect-es plugin. Generation stays limited to the client-surface closure (openshell/sandbox/datamodel) via the input paths. Output is byte-identical to the previous protoc + protoc-gen-es pipeline. Lays the groundwork for a shared buf.yaml (lint/breaking/LSP) as a follow-up. Signed-off-by: Max Dubrinsky <mdubrinsky@nvidia.com> * build(proto): add repo-level buf module with lint Declare proto/ as a single buf v2 module in a root buf.yaml so buf generate, lint, breaking, and the editor LSP resolve imports the same way. Lint uses STANDARD with six documented exceptions for deviations the current protos intentionally make: the flat proto/ layout with nested packages (DIRECTORY_SAME_PACKAGE, PACKAGE_DIRECTORY_MATCH) and the established API shape with unsuffixed services and reused request/response messages (RPC_REQUEST_RESPONSE_UNIQUE, RPC_REQUEST_STANDARD_NAME, RPC_RESPONSE_STANDARD_NAME, SERVICE_SUFFIX). Every other STANDARD rule now enforces on future protos. Breaking uses FILE. Code generation stays package-scoped in sdk/typescript/buf.gen.yaml since it binds to that package's connect-es plugin and output dir; its inputs are unchanged and regeneration is byte-identical. Wire the check in via a proto:lint mise task that runs buf from the SDK devDependencies. It is a dependency of both sdk:ts:ci (so the TypeScript SDK CI job enforces it) and the top-level lint aggregate (so local pre-commit covers it). Signed-off-by: Max Dubrinsky <mdubrinsky@nvidia.com> * chore(sdk): publish as unscoped openshell-sdk on public npm Rename the package from @nvidia/openshell-sdk to the unscoped openshell-sdk and target public npm (registry.npmjs.org) instead of GitHub Packages. GitHub Packages requires a scope matching the owning org, and the @openshell scope is blocked by an unrelated existing package, so an unscoped name on public npm is the lowest-friction distribution path and needs no org approval. Rework the release-tag publish job to auth against registry.npmjs.org with NPM_TOKEN (the job now only needs packages: read to pull the CI image). Update the README install instructions and usage imports. Signed-off-by: Max Dubrinsky <mdubrinsky@nvidia.com> * chore(sdk): publish @nvidia/openshell-sdk to GitHub Packages Revert the unscoped-name switch. GitHub Packages only accepts scoped names matching the owning org, so shipping there first (which needs no external npm org or NPM_TOKEN, just the repo's GITHUB_TOKEN) requires the @nvidia scope. Keeping the @nvidia/openshell-sdk name also lets a later public-npm release use the same install specifier, so adding public npm becomes a second publish step rather than a rename. Restore the GitHub Packages publish auth in the release-tag job and the scoped install instructions in the README (keeping the buf codegen note). Signed-off-by: Max Dubrinsky <mdubrinsky@nvidia.com> * feat(sdk-ts): add streaming exec, forward, ssh, provider, and config methods Grow SandboxClient to the surface the first two consumers need. execStream yields stdout/stderr chunks as they arrive and exec now drains it, keeping its buffered ExecResult and signature unchanged. execInteractive is the TTY + stdin transport primitive (start-first framing, output/write/resize/close/done, no terminal glue). forward binds a local TCP listener that tunnels each accepted connection into the sandbox for the process lifetime, minting and revoking a per-socket SSH session token around a forwardTcp bidi. Adds createSshSession / revokeSshSession, attach/detach/listProviders, and getConfig / setPolicy / setSetting (sandbox-scoped, network-policy-only, with an optional wait poll). Signed-off-by: Max Dubrinsky <mdubrinsky@nvidia.com> * build(sdk-ts): add Biome and Vitest tooling The TypeScript SDK had no formatter or linter and no test runner. Add Biome (format + lint, generated src/gen excluded) enforcing 2-space indent, single quotes, semicolons, and a 120-column width, and reformat the existing hand-written sources accordingly. Add Vitest for unit tests. Wire sdk:ts:format, sdk:ts:lint, and sdk:ts:test mise tasks into the fmt/lint aggregates, the root test suite, and sdk:ts:ci so they run in CI. Signed-off-by: Max Dubrinsky <mdubrinsky@nvidia.com> * test(sdk-ts): cover the sandbox surface with in-memory transport tests Exercise SandboxClient against an in-memory OpenShell service built with createRouterTransport: request assembly and id resolution, u64/int64 rendered as strings, enum lowercasing, fromConnect code mapping, the exec/execStream drain plus a backward-compat check on exec, execInteractive start-first ordering and done resolution, and a forward() byte relay against a loopback echo with close() teardown. Signed-off-by: Max Dubrinsky <mdubrinsky@nvidia.com> * docs(sdk-ts): document the new surface and connect/upload/download boundaries Document execStream, execInteractive, forward, ssh sessions, providers, and config/policy in the SDK README, and record the intentional boundaries: interactive connect / PTY ownership, upload/download (no file-transfer RPC), and detached forwards stay out of scope. Note the Biome/Vitest dev commands. Signed-off-by: Max Dubrinsky <mdubrinsky@nvidia.com> * feat(sdk-ts): support mTLS client authentication Add clientCert and clientKey to ConnectOptions so the SDK can authenticate to the default local gateway, which uses mTLS user authentication. Without a client certificate and key the SDK could verify the server but never authenticate the caller, so it could not connect to the standard Docker, VM, Homebrew, or Linux-package gateway. Validate the pair as both-or-neither and pass cert and key through to the Node TLS options for https gateways. The h2c path is unchanged. Signed-off-by: Max Dubrinsky <mdubrinsky@nvidia.com> * chore(sdk-ts): drop the demo script and its tsx dependency Remove src/demo.ts, the demo npm script, the tsx devDependency, and the tsconfig build exclude for the demo. The demo was never part of the published package, and dropping it also removes the only place that logged part of an SSH session token. Signed-off-by: Max Dubrinsky <mdubrinsky@nvidia.com> * fix(sdk-ts)!: harden exec streaming, waits, SSH, and forwarding Address review feedback on the sandbox surface. - Make the streamed command exit code observable from idiomatic for-await: the terminal exit is now an in-band ExecStreamEvent ({ type: 'exit', exitCode }) rather than the async generator return value, which for-await discards. A stream that ends without an exit event now throws instead of reporting success. - Bound waitReady, waitDeleted, and the setPolicy wait by their timeout: each poll RPC carries a per-iteration deadline and the waits accept an AbortSignal, so a stalled call can no longer leave a wait pending forever. Add waitTimeoutSecs to SetPolicyOptions. - Validate the CreateSshSession response against the proto charset and range contract before returning it or using its token, since the values feed an OpenSSH ProxyCommand. - Respect socket backpressure when relaying forwarded responses: pause reading the gRPC stream when the local socket buffer is full and resume on drain so memory stays bounded. - Expose create-time sandbox policy: add policy and an advanced rawSpec passthrough to SandboxSpec so the safety boundary is expressible at creation and new spec fields do not require an SDK change. BREAKING CHANGE: execStream and the interactive exec output now yield a terminal { type: 'exit', exitCode } event; consumers iterating the stream must handle that arm. The exit code is no longer the async generator return value. Signed-off-by: Max Dubrinsky <mdubrinsky@nvidia.com> * feat(sdk-ts): export error contract, enum unions, and caller cancellation Address Tier-1 review feedback on the TypeScript SDK public surface (PR #2122). - Errors: export SdkError and SdkErrorCode so callers can use instanceof and exhaustively switch on .code. fromConnect preserves the originating ConnectError as .cause and its status as .connectCode, maps Aborted to a new 'aborted' code for optimistic-concurrency conflicts, and maps Canceled and DeadlineExceeded to 'canceled'. errorCode() behavior is unchanged. - Enums: replace the string-typed phase, status, scope, and policySource fields with lowercase literal unions (SandboxPhaseName, HealthStatus, SettingScopeName, PolicySourceName) backed by exhaustive Record maps. The unions are a hand-maintained mirror of the generated proto enums; a new drift test pins each literal to its generated member name. - Cancellation: accept an optional AbortSignal on exec, execInteractive, and forward, threaded into both sandbox resolution and the streaming RPC. forward tears down its local listener on abort. Signed-off-by: Max Dubrinsky <mdubrinsky@nvidia.com> * feat(sdk-ts): add raw escape hatch for uncurated gateway RPCs The curated sub-clients reduce proto messages to ergonomic subsets (for example get() drops created_at_ms, the full spec, conditions, runtime endpoints, and current_policy_version), and not every gateway RPC has a typed helper yet. Rather than ship methods that exist but throw, expose a generated client for the full surface. OpenShellClient.raw and SandboxClient.raw are generated clients covering every gateway RPC, returning the verbatim wire messages so proto distinctions the curated types smooth over are preserved. .transport exposes the shared connection for building extra clients over one socket. Generated request/response types are published at the new @nvidia/openshell-sdk/raw subpath. Curated methods stay the default; raw is the always-available floor. Signed-off-by: Max Dubrinsky <mdubrinsky@nvidia.com> * fix(sdk-ts): address review feedback on exec, forward, and auth transport Settle exec's `done` promise before yielding the exit event so a consumer that breaks on exit no longer leaves it pending forever, and give it a lone rejection handler plus a finally-settle so a stream error or early abandon can never surface as an unhandled rejection or a hang. Attach an 'error' listener to each accepted forward socket synchronously, before forwardConnection awaits CreateSshSession; a peer reset in that window previously emitted an unhandled 'error' and crashed the process. Reject ambiguous or unsafe transport configs at buildTransport: oidcToken and edgeToken together (silently OIDC-only), and any auth token sent over plaintext http:// to a non-loopback host unless allowInsecureAuth is set. Wrap versionPin so a non-u64 expectedResourceVersion raises SdkError('invalid_config') instead of a raw BigInt SyntaxError, and raise the Node engine floor to >=20.3 for AbortSignal.any(). Signed-off-by: Max Dubrinsky <mdubrinsky@nvidia.com> * fix(sdk-ts): address review feedback Signed-off-by: Drew Newberry <anewberry@nvidia.com> * docs(sdk-ts): defer published sdk guide Signed-off-by: Drew Newberry <anewberry@nvidia.com> --------- Signed-off-by: Max Dubrinsky <mdubrinsky@nvidia.com> Signed-off-by: Drew Newberry <anewberry@nvidia.com> Co-authored-by: Drew Newberry <anewberry@nvidia.com>
15 KiB
Build
This page records the stable build, CI, docs, and release architecture. It is
not a command reference. Contributor-facing workflow details live in
CONTRIBUTING.md, CI.md, and published docs.
Artifacts
OpenShell builds these main artifacts:
| Artifact | Source |
|---|---|
| Gateway binary | crates/openshell-server |
| CLI package and Python SDK | python/openshell plus Rust binaries where packaged |
| TypeScript SDK package | sdk/typescript |
| Gateway container image | deploy/docker/Dockerfile.gateway |
| Supervisor container image | deploy/docker/Dockerfile.supervisor |
| Helm chart | deploy/helm/openshell |
| VM driver/runtime assets | crates/openshell-driver-vm |
| Published docs site | docs/ rendered by Fern config in fern/ |
Sandbox community images are built outside this repository.
Build Features
Anonymous telemetry emission is gated behind a default-on telemetry Cargo
feature. It is defined in openshell-core (where the emission code, HTTP
client, and endpoint live) and forwarded by the binary crates that emit or
collect telemetry: openshell-server (gateway), openshell-sandbox
(supervisor), and openshell-driver-vm. Every crate depends on
openshell-core with default-features = false, so the binary crate's feature
is the single switch that enables openshell-core/telemetry for its build
graph. In-process drivers (docker, kubernetes, podman) inherit the
gateway's setting through feature unification and carry no passthrough.
Building a binary with --no-default-features compiles out telemetry entirely:
no endpoint, no telemetry HTTP client, and no emission code. With telemetry
compiled out, telemetry::enabled() is always false and the emit_* helpers
are no-ops, so the data-model types stay available and dependent crates compile
unchanged. The runtime OPENSHELL_TELEMETRY_ENABLED switch remains the way to
disable telemetry in a default (telemetry-enabled) build.
Supervisor upstream TLS root-store selection is controlled by the
bundled-ca-roots Cargo feature (on by default). Default builds use Mozilla
roots through webpki-roots plus locally-installed CAs from the system bundle.
Building without bundled-ca-roots switches to the platform trust store via
rustls-native-certs and excludes bundled Mozilla root crates such as
webpki-roots and webpki-root-certs from the dependency graph. The
system-ca-roots feature alias on openshell-sandbox includes all other
defaults (currently telemetry) except bundled-ca-roots, so Linux
distribution builds (e.g. RPM) can use
--no-default-features --features system-ca-roots without manually re-adding
unrelated defaults. Other Rustls clients use native roots directly because that
already satisfies Linux distribution trust-store policy.
The workspace uses z3 versions whose z3-sys dependency keeps downloader
HTTP/TLS support behind explicit build features, so default system-Z3 builds do
not reintroduce bundled Mozilla roots. Release builds that need bundled Z3
continue to opt in with bundled-z3.
Linux Runtime Environments
OpenShell uses different Linux libc environments for different host artifacts.
The standalone openshell CLI is built as a static musl binary so it can run on
a wide range of Linux distributions without depending on the host's glibc. Host
runtime binaries that use the GNU/Linux runtime environment are GNU-linked.
openshell-gateway and openshell-driver-vm are built with a glibc 2.28 floor.
The gateway bundles z3 into the release binary so Linux packages, standalone
tarballs, and gateway images do not depend on distro-specific z3 shared-library
SONAMEs.
The supervisor is the one binary whose libc is selectable, because it is the one
binary executed inside a userland OpenShell does not control. SUPERVISOR_LIBC
chooses between musl (default) and glibc-static. Both produce a fully static
binary; the choice does not change the runtime layout or the supervisor image base.
Static linkage is a hard requirement rather than a preference, so both variants
are verified by tasks/scripts/verify-static-binary.sh, which fails the build on
any PT_INTERP or DT_NEEDED entry.
The two variants differ only in build-time constraints:
musl (default) |
glibc-static |
|
|---|---|---|
| Cross-compiles | yes, via cargo zigbuild |
no — must build natively per architecture |
| Host requirement | zig + cargo-zigbuild | glibc static libraries (glibc-static on Fedora/RHEL, libc6-dev on Debian/Ubuntu) |
| libc license | MIT | LGPL-2.1-or-later, statically linked |
cargo zigbuild cannot produce the glibc-static variant: zig cc accepts
-static for *-linux-gnu targets and emits a dynamically linked binary
anyway. The staging script therefore refuses to cross-compile that variant
instead of silently degrading linkage.
Selecting glibc-static statically links LGPL glibc into a redistributed
binary, which carries relinking obligations that musl (MIT) does not. Treat the
default as the shipping configuration unless that has been reviewed.
Container Builds
The Docker image pipeline is a two-step flow: build the Rust binary natively
for the target architecture, then assemble the container image from the
prebuilt binary. The gateway image is built from deploy/docker/Dockerfile.gateway
and the supervisor image from deploy/docker/Dockerfile.supervisor. Neither
Dockerfile compiles Rust — both copy a staged binary out of
deploy/docker/.build/prebuilt-binaries/<arch>/ into the final image.
Binary staging is driven by tasks/scripts/stage-prebuilt-binaries.sh. Because
staging cross-compiles on the host, it sources tasks/scripts/build-env.sh and
raises the per-process open-file limit before invoking cargo zigbuild on
macOS — the static musl link opens hundreds of .rlib files at once and would
otherwise fail with ProcessFdQuotaExceeded under macOS's default soft limit of
256. The guard is a no-op on Linux and when cargo-zigbuild is absent. Gateway
binaries use cargo zigbuild with GNU targets pinned to glibc 2.28, including
native-architecture builds, so the gateway image, standalone tarballs, and Linux
packages share the same host portability floor. The gateway build enables
bundled-z3. Linux VM driver release artifacts use the same glibc floor so
package-managed VM support does not raise the package runtime requirement.
Gateway staging and release workflows set up the Zig C/C++ wrapper before
bundled Z3 builds and verify the maximum referenced GLIBC_* symbol version
before publishing or copying artifacts.
Supervisor binaries are static in every configuration. The default musl
variant uses cargo zigbuild when available, including native CPU
architectures, so C dependencies are compiled for the musl target instead of the
host GNU libc target. The glibc-static variant uses plain cargo build with
+crt-static and requires a native per-architecture build. Local Docker image tasks infer the
target architecture from DOCKER_PLATFORM when set. Otherwise, they require
valid container engine host metadata and fail when the engine query is
unavailable or reports an unsupported architecture, avoiding host-kernel
fallbacks that can target the wrong architecture. CI invokes the same staging
step via the rust-native-build.yml workflow (per-architecture, per-component)
and uploads the result as an artifact that the image build job downloads back
into the staging directory before running Buildx.
Runtime layout:
- Gateway:
gcr.io/distroless/cc-debian13:nonrootbase, GNU-linked binary at/usr/local/bin/openshell-gateway, runs as UID/GID1000:1000. Linux GNU gateway binaries must not referenceGLIBC_*symbols newer thanGLIBC_2.28; release workflows verify this before publishing artifacts. The gateway bundles z3, so the image does not need a distro-provided z3 runtime. - VM driver: host GNU-linked binary installed at
/usr/libexec/openshell/openshell-driver-vmin Linux packages and published as a release artifact. Linux GNU VM driver binaries must not referenceGLIBC_*symbols newer thanGLIBC_2.28; release workflows verify this before publishing artifacts. - Supervisor: Alpine base with
nftables, static binary at/openshell-sandbox(musl by default; seeSUPERVISOR_LIBCabove). Static linkage keeps the binary usable when the image is mounted/extracted into sandbox environments (Docker extraction, Podman image volumes, Kubernetes init-container copy-self), whose libc and glibc version are not known at build time, whilenftablessupports Kubernetes supervisor sidecar egress enforcement. The VM driver bundles its own supervisor build (tasks/scripts/vm/build-supervisor-bundle.sh) and does not readSUPERVISOR_LIBC.
Gateway image builds bake the corresponding supervisor image tag into the
gateway binary so Docker sandboxes do not depend on :latest by default.
The Helm chart omits the supervisor image from gateway configuration unless an
operator supplies a repository or tag override, preserving that build-time
pairing for Kubernetes sandboxes as well.
Package formulas also pin Docker supervisor extraction to the matching release
image tag so standalone gateway binaries do not infer image tags from package
versions.
The Homebrew service keeps gateway TLS under the Homebrew state directory but
mirrors Docker sandbox client TLS into $HOME/.local/state/openshell/homebrew/tls
at service start, because Docker Desktop bind mounts must use paths visible to
the macOS user's shared home directory.
Local image work should use mise tasks rather than direct Docker commands so
the same staging and tagging assumptions are used locally and in CI.
Container-engine selection is centralized in tasks/scripts/container-engine.sh.
CONTAINER_ENGINE=docker|podman is the only explicit override. Docker- and
Podman-backed e2e wrappers validate that override against their lane, set
OPENSHELL_E2E_DRIVER, and reject the removed
OPENSHELL_E2E_CONTAINER_ENGINE selector so build helpers and Rust e2e support
containers use the same engine. When no explicit override is present, an e2e
driver requirement wins, then a local-cluster requirement, then host
auto-detection.
Local Kubernetes image workflows opt into cluster-aware selection with
CONTAINER_ENGINE_TARGET=local-k8s-cluster. The hint is intentionally scoped to
Skaffold-style push: false builds where the image must land in the engine
backing the active local cluster: k3d-* contexts require Docker, kind-*
contexts use KIND_EXPERIMENTAL_PROVIDER=docker|podman when set, and ambiguous
or unknown contexts require an explicit CONTAINER_ENGINE. Other image builds
do not infer from kube context.
Disposable Test Guests
The Nix test guest harness under nix/test-guest boots native-architecture cloud images
through QEMU for package, release, and E2E validation. A prepared cache entry is
captured after the exact ordered Ansible configuration list and before
test-specific packages, copied binaries, forwarded ports, or commands.
Prepared disks are flattened, sanitized QCOW2 images. The local cache keeps them read-only and each test receives a fresh writable overlay and cloud-init identity. The optional shared cache stores the compressed standalone disk and its compatibility metadata as a custom OCI artifact. Normal test runs ensure the exact local entry exists, invoking the cache builder automatically on a miss before booting a disposable overlay. The separate cache app owns OCI pulls and explicit publication. OCI pulls require a trusted manifest digest and retain that provenance with the local entry; mutable tags are used only for explicit publication.
Python Wheel Packaging
The generated protobuf/gRPC stubs under python/openshell/_proto/ are gitignored
build outputs of mise run python:proto. The task uses uv run --frozen to
synchronize the current worktree's .venv from uv.lock before generation.
maturin honors .gitignore when collecting python-source files, so native
builds (Linux CI, local pip install .) would drop them and ship an unimportable
wheel. pyproject.toml
pins them back in with [tool.maturin].include globs. The release workflows
install each Linux wheel in a clean image and import openshell.sandbox as a
smoke check.
TypeScript SDK Packaging
The native TypeScript SDK in sdk/typescript uses Connect over the generated
OpenShell protobuf surface. sdk/typescript/buf.gen.yaml selects the client
proto closure, and mise run sdk:ts:proto generates gitignored sources under
src/gen. TypeScript compilation includes those sources in dist, so package
consumers do not run code generation.
Branch checks run mise run sdk:ts:ci, enforce an 80% line-coverage floor, and
exercise version stamping plus npm publish --dry-run. Tagged releases publish
@nvidia/openshell-sdk to GitHub Packages. The repository keeps package version
0.0.0; the release task derives and temporarily stamps the npm version from
the release tag.
CI and E2E
Required checks run on GitHub Actions. Workflows that use NVIDIA self-hosted runners trigger from copy-pr-bot mirror branches, so trusted PRs are mirrored into pull-request/<N> branches before those workflows run. main also uses GitHub merge queue so the final queued integration commit is validated before it merges.
The high-level CI model:
- PR-context gate jobs publish required statuses for the PR head commit.
- Standard branch checks run from trusted mirror branches.
- Label-gated Docker, Podman, VM, GPU, and Kubernetes E2E checks run from trusted mirror branches.
- Merge-group checks run against GitHub's temporary queue branch for the final integration state.
- Gate jobs verify that the mirror branch matches the PR head, or that the merge-group workflow ran for the queued SHA, and that the expected non-gate workflow actually ran.
- Release workflows rebuild and publish binaries, wheels, images, and docs.
Repository CI keeps telemetry compiled into release-parity artifacts but disables emission for Rust tests, E2E runs, and release canaries. This prevents synthetic activity from contributing to product usage metrics.
See CI.md for the contributor workflow, labels, and maintainer merge-queue workflow.
Docs Site
Published docs live in docs/. Navigation lives in docs/index.yml. Fern site
configuration, components, theme assets, and publish settings live in fern/.
Use mise run docs for strict validation and mise run docs:serve for local
preview. PR previews are produced by .github/workflows/branch-docs.yml when
Fern credentials are available. Production docs publish from the release tag
workflow.
Validation Expectations
- Run
mise run pre-commitbefore committing. - Run
mise run testafter code changes. - Run
mise run e2efor sandbox, policy, driver, or deployment changes when the affected runtime can be exercised. - Run
mise run cibefore opening a PR when practical. - Run
mise run docswhendocs/orfern/changes.
Architecture-only changes should still check links and references because this directory is used by agents during implementation and review.