diff --git a/.github/workflows/canary-container-images.yml b/.github/workflows/canary-container-images.yml index b817bfd81..5a028c22a 100644 --- a/.github/workflows/canary-container-images.yml +++ b/.github/workflows/canary-container-images.yml @@ -10,7 +10,9 @@ concurrency: cancel-in-progress: true permissions: + actions: write contents: read + id-token: write packages: write jobs: @@ -51,6 +53,7 @@ jobs: password: ${{ secrets.GITHUB_TOKEN }} - name: Build and push canary image + id: build_image uses: docker/build-push-action@v7 with: context: ${{ steps.service_config.outputs.context }} @@ -61,3 +64,38 @@ jobs: ${{ steps.image.outputs.name }}:sha-${{ steps.image.outputs.short_sha }} cache-from: type=gha cache-to: type=gha,mode=max + sbom: true + provenance: mode=max + + - name: Scan image for vulnerabilities + uses: aquasecurity/trivy-action@dc5a429b52fcf669ce959baa2c2dd26090d2a6c4 # v0.32.0 + with: + image-ref: ${{ steps.image.outputs.name }}@${{ steps.build_image.outputs.digest }} + scanners: vuln + format: table + output: trivy-results.txt + ignore-unfixed: true + exit-code: 0 + + - name: Generate SBOM + uses: anchore/sbom-action@f8bdd1d8ac5e901a77a92f111440fdb1b593736b # v0.20.6 + with: + image: ${{ steps.image.outputs.name }}@${{ steps.build_image.outputs.digest }} + artifact-name: sbom-${{ matrix.service }}-spdx + format: spdx-json + + - name: Document image security audit + shell: bash + run: | + { + echo "## Image security audit: \`${{ matrix.service }}\`" + echo "" + echo "- Image: \`${{ steps.image.outputs.name }}@${{ steps.build_image.outputs.digest }}\`" + echo "- Runtime base: \`debian:bookworm-20260518-slim\`" + echo "- SBOM artifact: \`sbom-${{ matrix.service }}-spdx\` (SPDX JSON)" + echo "" + echo "### Vulnerability scan (Trivy, image packages)" + echo '```text' + cat trivy-results.txt + echo '```' + } >> "$GITHUB_STEP_SUMMARY" diff --git a/.github/workflows/docs-pages.yml b/.github/workflows/docs-pages.yml index 7704ec854..48a8cc969 100644 --- a/.github/workflows/docs-pages.yml +++ b/.github/workflows/docs-pages.yml @@ -25,14 +25,14 @@ jobs: fetch-depth: 0 - name: Setup pnpm - uses: pnpm/action-setup@fdbc4fdcf7d143a5d0a39e9a02e103b13e309024 + uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8 with: - version: 11.3.0 + package_json_file: docs/package.json - name: Setup Node uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e with: - node-version: 24 + node-version: 26 cache: pnpm cache-dependency-path: docs/pnpm-lock.yaml diff --git a/.mise.toml b/.mise.toml index 75952365b..bb00a0e61 100644 --- a/.mise.toml +++ b/.mise.toml @@ -1,12 +1,12 @@ [tools] -buf = "1.69.0" -"cargo:protoc-gen-buffa" = "0.6.0" -"cargo:protoc-gen-buffa-packaging" = "0.6.0" -node = "24.13.1" -pnpm = "11.3.0" -python = "3.11" -uv = "0.9.5" -rust = { version = "1.93.0", profile = "default" } +buf = "1.70.0" +"cargo:protoc-gen-buffa" = "0.7.0" +"cargo:protoc-gen-buffa-packaging" = "0.7.0" +node = "26.3.0" +pnpm = "11.5.1" +python = "3.14.5" +uv = "0.11.18" +rust = { version = "1.96.0", profile = "default" } [tasks.dev] description = "Start development environment" diff --git a/devops/docker/compose/compose.yml b/devops/docker/compose/compose.yml index 9bf793ceb..976bb6d79 100644 --- a/devops/docker/compose/compose.yml +++ b/devops/docker/compose/compose.yml @@ -2,7 +2,7 @@ name: trogonai services: nats: - image: nats:2.11-alpine + image: nats:2.14.2-alpine command: - "--jetstream" - "--store_dir=/data" @@ -46,7 +46,7 @@ services: retries: 3 ngrok: - image: ngrok/ngrok:alpine + image: ngrok/ngrok:3.39.6-alpine env_file: - path: .env required: false diff --git a/devops/docker/compose/services/trogon-gateway/Dockerfile b/devops/docker/compose/services/trogon-gateway/Dockerfile index 93aee9394..e1b75c8c9 100644 --- a/devops/docker/compose/services/trogon-gateway/Dockerfile +++ b/devops/docker/compose/services/trogon-gateway/Dockerfile @@ -1,5 +1,5 @@ # ── Stage 1: chef — generate dependency recipe ────────────────────────────── -FROM rust:1.93-slim AS chef +FROM rust:1.96.0-slim-bookworm AS chef RUN cargo install cargo-chef --locked @@ -26,7 +26,7 @@ RUN cargo build --release -p trogon-gateway && \ strip target/release/trogon-gateway # ── Stage 4: runtime ──────────────────────────────────────────────────────── -FROM debian:bookworm-20250317-slim AS runtime +FROM debian:bookworm-20260518-slim AS runtime RUN apt-get update && apt-get install -y --no-install-recommends \ ca-certificates curl \ diff --git a/docs/package.json b/docs/package.json index d251e42df..215f424b8 100644 --- a/docs/package.json +++ b/docs/package.json @@ -2,7 +2,7 @@ "name": "@trogonstack/trogonai-docs", "private": true, "type": "module", - "packageManager": "pnpm@11.3.0", + "packageManager": "pnpm@11.5.1", "scripts": { "docs:dev": "vitepress dev .", "docs:build": "vitepress build .", diff --git a/rsworkspace/Cargo.lock b/rsworkspace/Cargo.lock index a4c95cc57..787f8b05b 100644 --- a/rsworkspace/Cargo.lock +++ b/rsworkspace/Cargo.lock @@ -383,26 +383,39 @@ dependencies = [ "hybrid-array", ] +[[package]] +name = "borsh" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfd1e3f8955a5d7de9fab72fc8373fade9fb8a703968cb200ae3dc6cf08e185a" +dependencies = [ + "bytes", + "cfg_aliases", +] + [[package]] name = "buffa" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "941c714734a660caa93a210c531dec553cc0ef417890409914d24032c94ab840" +checksum = "e6bfcf26f959599a1501e632486d1d5d9db5ecb18905c01b83061019b473e115" dependencies = [ "base64", "bytes", + "compact_str", + "ecow", "hashbrown 0.15.5", "once_cell", "serde", "serde_json", + "smol_str", "thiserror 2.0.18", ] [[package]] name = "buffa-types" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc84fac1e7efc335e74934a4ebe0534a30411ec2ea6a5a17ed294201c6b3b23c" +checksum = "f3056f933af716224d8c1e842a2937f47fe6b996dba549ed8e761c400deccf86" dependencies = [ "buffa", "bytes", @@ -432,6 +445,15 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6bd91ee7b2422bcb158d90ef4d14f75ef67f340943fc4149891dcce8f8b972a3" +[[package]] +name = "castaway" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a" +dependencies = [ + "rustversion", +] + [[package]] name = "cc" version = "1.2.59" @@ -541,6 +563,21 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" +[[package]] +name = "compact_str" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dfdd1c2274d9aa354115b09dc9a901d6c5576818cdf70d14cae2bdb47df00ab" +dependencies = [ + "castaway", + "cfg-if", + "itoa", + "rustversion", + "ryu", + "serde", + "static_assertions", +] + [[package]] name = "concurrent-queue" version = "2.5.0" @@ -800,6 +837,15 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" +[[package]] +name = "ecow" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78e4f79b296fbaab6ce2e22d52cb4c7f010fe0ebe7a32e34fa25885fd797bd02" +dependencies = [ + "serde", +] + [[package]] name = "ed25519" version = "2.2.3" @@ -2611,6 +2657,16 @@ version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +[[package]] +name = "smol_str" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9676b89cd56310a87b93dec47b11af744f34d5fc9f367b829474eec0a891350d" +dependencies = [ + "borsh", + "serde", +] + [[package]] name = "socket2" version = "0.6.3" @@ -2650,6 +2706,12 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "strsim" version = "0.11.1" diff --git a/rsworkspace/Cargo.toml b/rsworkspace/Cargo.toml index 3b58ad326..ae9b92944 100644 --- a/rsworkspace/Cargo.toml +++ b/rsworkspace/Cargo.toml @@ -33,8 +33,8 @@ async-nats = { version = "=0.47.0", default-features = false } async-trait = "=0.1.89" axum = { version = "=0.8.9", features = ["ws"] } bytes = "=1.11.1" -buffa = { version = "=0.6.0", features = ["json"] } -buffa-types = { version = "=0.6.0", features = ["json"] } +buffa = { version = "=0.7.0", features = ["json"] } +buffa-types = { version = "=0.7.0", features = ["json"] } futures = "=0.3.32" futures-util = "=0.3.32" tokio = "=1.52.1" diff --git a/rsworkspace/crates/trogon-decider/tests/ui/fail/after_then.stderr b/rsworkspace/crates/trogon-decider/tests/ui/fail/after_then.stderr index 245e8f0c7..7c93b5f1a 100644 --- a/rsworkspace/crates/trogon-decider/tests/ui/fail/after_then.stderr +++ b/rsworkspace/crates/trogon-decider/tests/ui/fail/after_then.stderr @@ -7,9 +7,9 @@ error[E0599]: no method named `when` found for struct `ThenEvents` in the | _____method `when` is available on `TestCase` | | 10 | | .given_no_history() - | | ------------------ method `when` is available on `TestCase` + | | ------------------ method `when` is available on `TestCase` 11 | | .when(TestCommand) - | | ----------------- method `when` is available on `TestCase>` + | | ----------------- method `when` is available on `TestCase>` 12 | | .then([TestEvent::Registered]) 13 | | .when(TestCommand); | | -^^^^ method not found in `ThenEvents` diff --git a/rsworkspace/crates/trogon-decider/tests/ui/fail/double_given.stderr b/rsworkspace/crates/trogon-decider/tests/ui/fail/double_given.stderr index 984cae440..e052c6540 100644 --- a/rsworkspace/crates/trogon-decider/tests/ui/fail/double_given.stderr +++ b/rsworkspace/crates/trogon-decider/tests/ui/fail/double_given.stderr @@ -1,13 +1,13 @@ -error[E0599]: no method named `given` found for struct `TestCase` in the current scope +error[E0599]: no method named `given` found for struct `TestCase` in the current scope --> tests/ui/fail/double_given.rs:11:10 | 9 | / TestCase::::new() 10 | | .given_no_history() 11 | | .given([]); - | | -^^^^^ method not found in `TestCase` + | | -^^^^^ method not found in `TestCase` | |_________| | | = note: the method was found for - - `TestCase::Event>>` + - `TestCase::Event>>` - `TestCase` diff --git a/rsworkspace/crates/trogon-decider/tests/ui/fail/double_when.stderr b/rsworkspace/crates/trogon-decider/tests/ui/fail/double_when.stderr index 03782e6e4..d444a5bfc 100644 --- a/rsworkspace/crates/trogon-decider/tests/ui/fail/double_when.stderr +++ b/rsworkspace/crates/trogon-decider/tests/ui/fail/double_when.stderr @@ -1,4 +1,4 @@ -error[E0599]: no method named `when` found for struct `TestCase>` in the current scope +error[E0599]: no method named `when` found for struct `TestCase>` in the current scope --> tests/ui/fail/double_when.rs:12:10 | 9 | TestCase::::new() @@ -7,7 +7,7 @@ error[E0599]: no method named `when` found for struct `TestCase` | | 10 | | .given_no_history() - | | ------------------ method `when` is available on `TestCase` + | | ------------------ method `when` is available on `TestCase` 11 | | .when(TestCommand) 12 | | .when(TestCommand); | |_________-^^^^ diff --git a/rsworkspace/crates/trogon-decider/tests/ui/fail/missing_given.stderr b/rsworkspace/crates/trogon-decider/tests/ui/fail/missing_given.stderr index 4e90e0706..8b4b3d071 100644 --- a/rsworkspace/crates/trogon-decider/tests/ui/fail/missing_given.stderr +++ b/rsworkspace/crates/trogon-decider/tests/ui/fail/missing_given.stderr @@ -5,5 +5,5 @@ error[E0599]: no method named `when` found for struct `TestCase` in | ^^^^ method not found in `TestCase` | = note: the method was found for - - `TestCase::Event>>` - - `TestCase` + - `TestCase::Event>>` + - `TestCase` diff --git a/rsworkspace/crates/trogon-decider/tests/ui/fail/missing_initial_when.stderr b/rsworkspace/crates/trogon-decider/tests/ui/fail/missing_initial_when.stderr index ce750461b..260e2a315 100644 --- a/rsworkspace/crates/trogon-decider/tests/ui/fail/missing_initial_when.stderr +++ b/rsworkspace/crates/trogon-decider/tests/ui/fail/missing_initial_when.stderr @@ -7,5 +7,4 @@ error[E0599]: no method named `then_error` found for struct `TestCase::Event, ::State, C>>` + = note: the method was found for `TestCase::Event, ::State, C>>` diff --git a/rsworkspace/crates/trogon-decider/tests/ui/fail/missing_when.stderr b/rsworkspace/crates/trogon-decider/tests/ui/fail/missing_when.stderr index 0ac96d42e..cdcb53c3d 100644 --- a/rsworkspace/crates/trogon-decider/tests/ui/fail/missing_when.stderr +++ b/rsworkspace/crates/trogon-decider/tests/ui/fail/missing_when.stderr @@ -1,12 +1,11 @@ -error[E0599]: no method named `then_error` found for struct `TestCase` in the current scope +error[E0599]: no method named `then_error` found for struct `TestCase` in the current scope --> tests/ui/fail/missing_when.rs:11:10 | 9 | / TestCase::::new() 10 | | .given_no_history() 11 | | .then_error(TestDecisionError::AlreadyRegistered); - | | -^^^^^^^^^^ method not found in `TestCase` + | | -^^^^^^^^^^ method not found in `TestCase` | |_________| | | - = note: the method was found for - - `TestCase::Event, ::State, C>>` + = note: the method was found for `TestCase::Event, ::State, C>>` diff --git a/rsworkspace/crates/trogon-nats/src/jetstream/mocks.rs b/rsworkspace/crates/trogon-nats/src/jetstream/mocks.rs index 7fbf2bda6..52d219a0d 100644 --- a/rsworkspace/crates/trogon-nats/src/jetstream/mocks.rs +++ b/rsworkspace/crates/trogon-nats/src/jetstream/mocks.rs @@ -932,7 +932,7 @@ impl JetStreamKvKeys for MockJetStreamKvStore { match result { Ok(keys) => { let items: Vec> = keys.into_iter().map(Ok).collect(); - Ok(futures::stream::iter(items.into_iter())) + Ok(futures::stream::iter(items)) } Err(kind) => Err(kv::HistoryError::new(kind)), }