diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml index 75cd5689..2334c3ff 100644 --- a/.github/workflows/bench.yml +++ b/.github/workflows/bench.yml @@ -18,9 +18,9 @@ jobs: with: persist-credentials: false - - uses: dtolnay/rust-toolchain@stable - - - uses: Swatinem/rust-cache@v2 + - uses: actions-rust-lang/setup-rust-toolchain@v1 + with: + rustflags: "" - name: Install protoc uses: arduino/setup-protoc@v3 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1a834339..27bdcae8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -67,13 +67,13 @@ jobs: - uses: actions/checkout@v6 with: persist-credentials: false - # dprint formats TOML + Markdown + Rust. The exec plugin shells out to - # rustfmt for Rust, so the toolchain (with rustfmt) must be on PATH first. - # Plugin versions/checksums are pinned in dprint.json, so output is - # deterministic regardless of CLI version. - - uses: dtolnay/rust-toolchain@stable + # dprint formats TOML + Markdown + Rust via its exec plugin, which shells + # out to rustfmt, so the toolchain must be present. setup-rust-toolchain + # installs it from rust-toolchain.toml (including the rustfmt component). + - uses: actions-rust-lang/setup-rust-toolchain@v1 with: - components: rustfmt + cache: false + rustflags: "" - uses: dprint/check@v2.3 check: @@ -88,11 +88,7 @@ jobs: with: persist-credentials: false - - uses: dtolnay/rust-toolchain@stable - with: - components: clippy - - - uses: Swatinem/rust-cache@v2 + - uses: actions-rust-lang/setup-rust-toolchain@v1 - name: Install protoc uses: arduino/setup-protoc@v3 @@ -116,11 +112,14 @@ jobs: with: persist-credentials: false - - uses: dtolnay/rust-toolchain@nightly + - id: nightly + run: echo "toolchain=$(cat .rust-nightly)" >> "$GITHUB_OUTPUT" - - uses: Swatinem/rust-cache@v2 + - uses: actions-rust-lang/setup-rust-toolchain@v1 with: - workspaces: fuzz -> fuzz/target + toolchain: ${{ steps.nightly.outputs.toolchain }} + rustflags: "" + cache-workspaces: fuzz -> fuzz/target - name: Install protoc uses: arduino/setup-protoc@v3 @@ -128,5 +127,7 @@ jobs: repo-token: ${{ secrets.GITHUB_TOKEN }} - name: Check fuzz targets - run: cargo check + env: + NIGHTLY: ${{ steps.nightly.outputs.toolchain }} + run: cargo "+$NIGHTLY" check working-directory: fuzz diff --git a/.github/workflows/demo-e2e.yml b/.github/workflows/demo-e2e.yml index 4c8b6118..e5b89da6 100644 --- a/.github/workflows/demo-e2e.yml +++ b/.github/workflows/demo-e2e.yml @@ -21,9 +21,9 @@ jobs: with: persist-credentials: false - - uses: dtolnay/rust-toolchain@stable - - - uses: Swatinem/rust-cache@v2 + - uses: actions-rust-lang/setup-rust-toolchain@v1 + with: + rustflags: "" - name: Install protoc uses: arduino/setup-protoc@v3 diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index eda7ed2b..72aa02bc 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -35,14 +35,17 @@ jobs: persist-credentials: false - name: Install Rust nightly (required by cargo-doc-md) - uses: dtolnay/rust-toolchain@nightly - - - name: Install Rust stable - uses: dtolnay/rust-toolchain@stable + uses: actions-rust-lang/setup-rust-toolchain@v1 + with: + toolchain: nightly + cache: false + rustflags: "" - - uses: Swatinem/rust-cache@v2 + - name: Install Rust (pinned in rust-toolchain.toml) + uses: actions-rust-lang/setup-rust-toolchain@v1 with: - shared-key: docs + rustflags: "" + cache-shared-key: docs - name: Install protoc uses: arduino/setup-protoc@v3 diff --git a/.github/workflows/fuzz.yml b/.github/workflows/fuzz.yml index 5872704a..0c8821c7 100644 --- a/.github/workflows/fuzz.yml +++ b/.github/workflows/fuzz.yml @@ -22,11 +22,14 @@ jobs: with: persist-credentials: false - - uses: dtolnay/rust-toolchain@nightly + - id: nightly + run: echo "toolchain=$(cat .rust-nightly)" >> "$GITHUB_OUTPUT" - - uses: Swatinem/rust-cache@v2 + - uses: actions-rust-lang/setup-rust-toolchain@v1 with: - workspaces: fuzz -> fuzz/target + toolchain: ${{ steps.nightly.outputs.toolchain }} + rustflags: "" + cache-workspaces: fuzz -> fuzz/target - name: Install protoc uses: arduino/setup-protoc@v3 @@ -40,4 +43,7 @@ jobs: run: cargo binstall --no-confirm --locked cargo-fuzz - name: Run ${{ matrix.target }} - run: cargo fuzz run --target x86_64-unknown-linux-gnu ${{ matrix.target }} -- -max_total_time=300 + env: + NIGHTLY: ${{ steps.nightly.outputs.toolchain }} + TARGET: ${{ matrix.target }} + run: cargo "+$NIGHTLY" fuzz run --target x86_64-unknown-linux-gnu "$TARGET" -- -max_total_time=300 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 22111a77..a2619f94 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -64,9 +64,11 @@ jobs: fi echo "version=${VERSION}" >> "${GITHUB_OUTPUT}" - - uses: dtolnay/rust-toolchain@stable + - uses: actions-rust-lang/setup-rust-toolchain@v1 with: - targets: ${{ matrix.target }} + target: ${{ matrix.target }} + cache: false + rustflags: "" - name: Install protoc uses: arduino/setup-protoc@v3 diff --git a/.rust-nightly b/.rust-nightly new file mode 100644 index 00000000..f692697d --- /dev/null +++ b/.rust-nightly @@ -0,0 +1 @@ +nightly-2026-06-04 diff --git a/Cargo.toml b/Cargo.toml index 338cae8f..b4a28458 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,6 @@ version = "0.1.1" edition = "2024" license = "Apache-2.0" repository = "https://github.com/Firma-AI/openfirma" -rust-version = "1.88.0" [workspace.lints.clippy] pedantic = { level = "deny", priority = -1 } diff --git a/Makefile b/Makefile index 6fa946fe..247e64de 100644 --- a/Makefile +++ b/Makefile @@ -60,9 +60,9 @@ deny: check: fmt lint test build audit deny -# Requires nightly: rustup toolchain install nightly +# Pinned nightly lives in .rust-nightly (cargo-fuzz requires nightly). fuzz-check: - cd fuzz && cargo +nightly check + cd fuzz && cargo +$(shell cat .rust-nightly) check bench: cargo bench --workspace --no-fail-fast diff --git a/crates/firma-authority/Cargo.toml b/crates/firma-authority/Cargo.toml index ffa5d86f..644e58e2 100644 --- a/crates/firma-authority/Cargo.toml +++ b/crates/firma-authority/Cargo.toml @@ -4,7 +4,6 @@ version.workspace = true edition.workspace = true license.workspace = true repository.workspace = true -rust-version.workspace = true description = "Mini Authority — policy loading, capability issuance, and gRPC streams for Firma OSS" [dependencies] diff --git a/crates/firma-config/Cargo.toml b/crates/firma-config/Cargo.toml index 00692e70..9e8376a6 100644 --- a/crates/firma-config/Cargo.toml +++ b/crates/firma-config/Cargo.toml @@ -4,7 +4,6 @@ version.workspace = true edition.workspace = true license.workspace = true repository.workspace = true -rust-version.workspace = true description = "Platform config-path discovery for the firma CLI" [lints] diff --git a/crates/firma-core/Cargo.toml b/crates/firma-core/Cargo.toml index c0b564b4..557d05c4 100644 --- a/crates/firma-core/Cargo.toml +++ b/crates/firma-core/Cargo.toml @@ -4,7 +4,6 @@ version.workspace = true edition.workspace = true license.workspace = true repository.workspace = true -rust-version.workspace = true description = "Shared types, capability tokens, Cedar wrapper, and error types for Firma OSS" [dependencies] diff --git a/crates/firma-demo-fixture/Cargo.toml b/crates/firma-demo-fixture/Cargo.toml index fff7ba8e..578ad0fe 100644 --- a/crates/firma-demo-fixture/Cargo.toml +++ b/crates/firma-demo-fixture/Cargo.toml @@ -4,7 +4,6 @@ version.workspace = true edition.workspace = true license.workspace = true repository.workspace = true -rust-version.workspace = true description = "Deterministic ALLOW/DENY fixture server and CI driver client for the Firma demo" [lints] diff --git a/crates/firma-demo-tui/Cargo.toml b/crates/firma-demo-tui/Cargo.toml index 3dc91e22..2e03de2d 100644 --- a/crates/firma-demo-tui/Cargo.toml +++ b/crates/firma-demo-tui/Cargo.toml @@ -4,7 +4,6 @@ version.workspace = true edition.workspace = true license.workspace = true repository.workspace = true -rust-version.workspace = true [[bin]] name = "firma-demo-tui" diff --git a/crates/firma-grpc-interceptor-proto/Cargo.toml b/crates/firma-grpc-interceptor-proto/Cargo.toml index 8bc6f5fc..c8af9cff 100644 --- a/crates/firma-grpc-interceptor-proto/Cargo.toml +++ b/crates/firma-grpc-interceptor-proto/Cargo.toml @@ -4,7 +4,6 @@ version.workspace = true edition.workspace = true license.workspace = true repository.workspace = true -rust-version.workspace = true description = "Protobuf/gRPC definitions for the Firma interceptor hook (agent to sidecar)" [dependencies] diff --git a/crates/firma-proto/Cargo.toml b/crates/firma-proto/Cargo.toml index 22b0523a..53cafa94 100644 --- a/crates/firma-proto/Cargo.toml +++ b/crates/firma-proto/Cargo.toml @@ -4,7 +4,6 @@ version.workspace = true edition.workspace = true license.workspace = true repository.workspace = true -rust-version.workspace = true description = "Protobuf/gRPC service definitions and generated code for Firma OSS" [dependencies] diff --git a/crates/firma-proto/src/client.rs b/crates/firma-proto/src/client.rs index 31f050af..7bb2ba26 100644 --- a/crates/firma-proto/src/client.rs +++ b/crates/firma-proto/src/client.rs @@ -59,7 +59,7 @@ pub fn build_channel( .connect_timeout(connect_timeout) .keep_alive_timeout(Duration::from_secs(30)) .http2_keep_alive_interval(Duration::from_secs(30)) - .tcp_keepalive(Some(Duration::from_secs(60))); + .tcp_keepalive(Some(Duration::from_mins(1))); match scheme.as_str() { "https" => { diff --git a/crates/firma-run/Cargo.toml b/crates/firma-run/Cargo.toml index b217ac86..8fa926f3 100644 --- a/crates/firma-run/Cargo.toml +++ b/crates/firma-run/Cargo.toml @@ -4,7 +4,6 @@ version.workspace = true edition.workspace = true license.workspace = true repository.workspace = true -rust-version.workspace = true [lints] workspace = true diff --git a/crates/firma-run/src/proxy_bridge.rs b/crates/firma-run/src/proxy_bridge.rs index 46bf87cc..0888d707 100644 --- a/crates/firma-run/src/proxy_bridge.rs +++ b/crates/firma-run/src/proxy_bridge.rs @@ -810,12 +810,7 @@ fn find_crlf(buffer: &[u8]) -> Option { #[cfg(test)] mod tests { use std::collections::BTreeMap; - use std::io::{Read, Write}; - use std::net::{SocketAddr, TcpListener, TcpStream}; - use std::time::Duration; - #[cfg(unix)] - use super::HostBridgeHandle; use super::{BodyKind, append_missing_headers, find_header_terminator, parse_request_metadata}; #[test] @@ -874,6 +869,20 @@ mod tests { let meta = parse_request_metadata(req).expect("metadata"); assert_eq!(meta.body, BodyKind::Chunked); } +} + +// The bridge spins up real TCP listeners and only exists on the non-structural +// (macOS) path, so these tests are Unix-only and grouped here to keep the +// platform gate on the module rather than on each test and import. +#[cfg(test)] +#[cfg(unix)] +mod host_bridge_tests { + use std::collections::BTreeMap; + use std::io::{Read, Write}; + use std::net::{SocketAddr, TcpListener, TcpStream}; + use std::time::Duration; + + use super::HostBridgeHandle; /// Verifies that `HostBridgeHandle` injects `x-firma-session-id` into a /// plain HTTP request routed through the bridge. @@ -881,7 +890,6 @@ mod tests { /// This is the regression test for FIR-213: on macOS (non-structural path) /// the bridge was never started, so the sidecar received an empty /// `session_id` and denied every request. - #[cfg(unix)] #[test] fn host_bridge_injects_session_id_into_http_request() { // ── upstream mock ────────────────────────────────────────────────── @@ -951,7 +959,6 @@ mod tests { /// Verifies that `HostBridgeHandle` injects `x-firma-session-id` into the /// CONNECT request that Claude Code issues for HTTPS destinations. - #[cfg(unix)] #[test] fn host_bridge_injects_session_id_into_connect_request() { let upstream_listener = TcpListener::bind("127.0.0.1:0").expect("upstream bind"); @@ -1013,7 +1020,6 @@ mod tests { /// Verifies that an existing `x-firma-session-id` header is not /// duplicated when the client already carries one. - #[cfg(unix)] #[test] fn host_bridge_does_not_duplicate_existing_session_id() { let upstream_listener = TcpListener::bind("127.0.0.1:0").expect("upstream bind"); diff --git a/crates/firma-sidecar/Cargo.toml b/crates/firma-sidecar/Cargo.toml index 1eae7870..695b18b1 100644 --- a/crates/firma-sidecar/Cargo.toml +++ b/crates/firma-sidecar/Cargo.toml @@ -4,7 +4,6 @@ version.workspace = true edition.workspace = true license.workspace = true repository.workspace = true -rust-version.workspace = true description = "HTTP proxy sidecar with enforcement pipeline for Firma OSS" [features] diff --git a/crates/firma-sidecar/src/local_exec/endpoint.rs b/crates/firma-sidecar/src/local_exec/endpoint.rs index 3daa4409..b600fd28 100644 --- a/crates/firma-sidecar/src/local_exec/endpoint.rs +++ b/crates/firma-sidecar/src/local_exec/endpoint.rs @@ -135,7 +135,7 @@ impl LocalExecEndpoint { let store_for_pruner = self.handler.token_store(); let prune_cancel = cancel.clone(); tokio::spawn(async move { - let mut interval = tokio::time::interval(Duration::from_secs(60)); + let mut interval = tokio::time::interval(Duration::from_mins(1)); loop { tokio::select! { () = prune_cancel.cancelled() => break, @@ -375,7 +375,7 @@ mod tests { let socket_path = tmp.path().join("test-local-exec.sock"); let config = LocalExecHandlerConfig { default_action: action, - token_ttl: Duration::from_secs(60), + token_ttl: Duration::from_mins(1), retry_after_ms: 500, }; let handler = LocalExecHandler::new(config); diff --git a/crates/firma-sidecar/src/local_exec/handler.rs b/crates/firma-sidecar/src/local_exec/handler.rs index 67242660..630caceb 100644 --- a/crates/firma-sidecar/src/local_exec/handler.rs +++ b/crates/firma-sidecar/src/local_exec/handler.rs @@ -466,7 +466,7 @@ mod tests { fn config(action: DefaultAction) -> LocalExecHandlerConfig { LocalExecHandlerConfig { default_action: action, - token_ttl: Duration::from_secs(60), + token_ttl: Duration::from_mins(1), retry_after_ms: 500, } } diff --git a/crates/firma-sidecar/src/local_exec/token_store.rs b/crates/firma-sidecar/src/local_exec/token_store.rs index e91dc26f..78e2c7b9 100644 --- a/crates/firma-sidecar/src/local_exec/token_store.rs +++ b/crates/firma-sidecar/src/local_exec/token_store.rs @@ -218,7 +218,7 @@ impl InMemoryTokenStore { Self { tokens: Mutex::new(HashMap::new()), ttl, - expiry_grace: Duration::from_secs(300), + expiry_grace: Duration::from_mins(5), } } @@ -423,7 +423,7 @@ mod tests { use super::*; fn store() -> InMemoryTokenStore { - InMemoryTokenStore::new(Duration::from_secs(60)) + InMemoryTokenStore::new(Duration::from_mins(1)) } fn issue_token(store: &InMemoryTokenStore) -> String { diff --git a/crates/firma-stack/Cargo.toml b/crates/firma-stack/Cargo.toml index 6837bc9c..22ebd558 100644 --- a/crates/firma-stack/Cargo.toml +++ b/crates/firma-stack/Cargo.toml @@ -4,7 +4,6 @@ version.workspace = true edition.workspace = true license.workspace = true repository.workspace = true -rust-version.workspace = true description = "Process supervision and observability primitives for the firma stack" [lints] diff --git a/crates/firma-stack/src/start.rs b/crates/firma-stack/src/start.rs index d176e322..d4e54a3e 100644 --- a/crates/firma-stack/src/start.rs +++ b/crates/firma-stack/src/start.rs @@ -91,7 +91,7 @@ fn spawn_stack_inner(cfg: &StackConfig, state_dir: &Path) -> Result let auth_addr = read_authority_listen_addr(&cfg.config_file)?; std::fs::write(state_dir.join("authority.listen"), format!("{auth_addr}\n"))?; debug!(addr = %auth_addr, "waiting for authority TCP listen"); - wait_for_tcp("authority", auth_addr, Duration::from_secs(60))?; + wait_for_tcp("authority", auth_addr, Duration::from_mins(1))?; info!(addr = %auth_addr, "authority listening"); debug!(config = %cfg.config_file.display(), exe = ?exe, "spawning sidecar"); @@ -100,12 +100,12 @@ fn spawn_stack_inner(cfg: &StackConfig, state_dir: &Path) -> Result let side_addr = read_sidecar_listen_addr(&cfg.config_file)?; std::fs::write(state_dir.join("sidecar.listen"), format!("{side_addr}\n"))?; debug!(addr = %side_addr, "waiting for sidecar TCP listen"); - wait_for_tcp("sidecar", side_addr, Duration::from_secs(60))?; + wait_for_tcp("sidecar", side_addr, Duration::from_mins(1))?; info!(addr = %side_addr, "sidecar listening"); debug!("waiting for sidecar CA material"); wait_for_ca_material( &state_dir.join("generated-firma-ca"), - Duration::from_secs(60), + Duration::from_mins(1), )?; debug!("CA material present"); diff --git a/crates/firma/Cargo.toml b/crates/firma/Cargo.toml index d156383b..9b370675 100644 --- a/crates/firma/Cargo.toml +++ b/crates/firma/Cargo.toml @@ -4,7 +4,6 @@ version.workspace = true edition.workspace = true license.workspace = true repository.workspace = true -rust-version.workspace = true description = "Firma CLI for managing and running OpenFirma components" [[bin]] diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 00000000..01a3bee0 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,3 @@ +[toolchain] +channel = "1.96.0" +components = ["clippy", "rustfmt"]