chore(release): bump infra to 0.5.0 and api to 0.4.1 (ADR-020 D2/D6) - #194
Conversation
Version bump for the changes merged since the api-v0.4.0 / infra-v0.4.0 tags (issue #170 / PR #193) — the release the last merged PR was missing. infra 0.4.0 -> 0.5.0 (MINOR, ADR-020 D2): - New additive public API: `ai::transport` module with curated/hosted/ollama redirect policies. - Breaking signature change on `OpenAiCompatibleModelCatalog::new` (no in-tree callers; released as a minor under major-zero semver). - HTTPS-only + Ollama local-only AI provider transport policy (issue #170). api 0.4.0 -> 0.4.1 (PATCH, ADR-020 D6): - Re-pins infra to 0.5.0, shipping the AI transport security fix in the api image. No crate API change; HTTP path version stays /v1. Dependent re-pins (what `cargo release --dependent` would do): - api -> infra 0.5.0 - architecture -> api 0.4.1, infra 0.5.0 - integration-tests -> infra 0.5.0 - Cargo.lock regenerated Release notes (English) added as per-crate CHANGELOG.md files (ADR-020 D5). core stays 0.3.0 (no changes). Release tags (infra-v0.5.0, api-v0.4.1) are cut locally by the release owner per ADR-020 D5; pushing api-v0.4.1 triggers the api image build (release-image.yml).
📝 WalkthroughSummary by CodeRabbit
WalkthroughThe PR adds DNS-rebinding protection for hosted AI providers, updates asynchronous client construction and model listing, and releases ChangesAI transport security and crate releases
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant ChatClient
participant Transport
participant DNS
participant HostedProvider
ChatClient->>Transport: build hosted client
Transport->>DNS: resolve provider host
DNS-->>Transport: return public addresses
Transport->>HostedProvider: send request through pinned client
HostedProvider-->>ChatClient: return response
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@backend/crates/infra/CHANGELOG.md`:
- Around line 16-21: Update hosted redirect validation in
curated_provider_redirect_policy and hosted_redirect_allowed to resolve and
validate the destination address, rejecting private, loopback, and other
internal addresses even when the hostname and scheme are allowed. Pin the
validated resolved address for the initial request and every redirect target,
and add coverage for private IPv4, IPv6 loopback, and hostnames resolving to
local addresses.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: c8b6b610-be16-45e7-a77c-12c16d296b1b
⛔ Files ignored due to path filters (1)
backend/Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (6)
backend/crates/api/CHANGELOG.mdbackend/crates/api/Cargo.tomlbackend/crates/architecture/Cargo.tomlbackend/crates/infra/CHANGELOG.mdbackend/crates/infra/Cargo.tomlbackend/crates/integration-tests/Cargo.toml
📜 Review details
⏰ Context from checks skipped due to timeout. (9)
- GitHub Check: Testcontainers integration tests (Postgres + SierraDB)
- GitHub Check: rust_arkitect (source-level rules)
- GitHub Check: MSRV (rust-version 1.94)
- GitHub Check: Clippy
- GitHub Check: Check
- GitHub Check: Test
- GitHub Check: Build
- GitHub Check: Mutate (security-critical — blocking)
- GitHub Check: Mutate (core — informational)
🧰 Additional context used
📓 Path-based instructions (2)
backend/**/*
📄 CodeRabbit inference engine (backend/AGENTS.md)
Never hardcode secrets; code must pass
gitleaks.
Files:
backend/crates/api/CHANGELOG.mdbackend/crates/infra/Cargo.tomlbackend/crates/architecture/Cargo.tomlbackend/crates/integration-tests/Cargo.tomlbackend/crates/api/Cargo.tomlbackend/crates/infra/CHANGELOG.md
backend/**/*.{yaml,yml,json,toml}
📄 CodeRabbit inference engine (backend/AGENTS.md)
Define the API in the OpenAPI specification before implementing code, and map exact API types with
serde.
Files:
backend/crates/infra/Cargo.tomlbackend/crates/architecture/Cargo.tomlbackend/crates/integration-tests/Cargo.tomlbackend/crates/api/Cargo.toml
🔇 Additional comments (5)
backend/crates/infra/Cargo.toml (1)
7-7: LGTM!backend/crates/infra/CHANGELOG.md (1)
1-13: LGTM!Also applies to: 22-47
backend/crates/architecture/Cargo.toml (1)
17-19: LGTM!backend/crates/integration-tests/Cargo.toml (1)
22-22: LGTM!backend/crates/api/Cargo.toml (1)
7-15: 🗄️ Data Integrity & IntegrationKeep
apiat version0.4.1.The
apicrate does not re-export or exposeOpenAiCompatibleModelCatalogor its constructor.> Likely an incorrect or invalid review comment.
…uard) Addresses the CodeRabbit review finding on #194 (issue #170 follow-up): hosted_redirect_allowed checked only scheme/hostname/port, so an allowlisted hostname resolving to a private or loopback address could still be reached. - transport: validate_public_resolution() resolves the destination and fails closed unless every resolved address is globally routable — private, loopback, link-local, unique-local, CGNAT, multicast, documentation ranges and the broadcast address are rejected even when hostname and scheme are allowed. build_hosted_client() pins the validated addresses via ClientBuilder::resolve_to_addrs, so the initial request and every same-origin redirect (the policy rejects cross-host hops) connect only to vetted addresses and cannot be rebound after validation. - client: OpenAiCompatibleChatClient::new is now async and applies the guard. - catalog: hosted listings resolve + validate + pin per call; the curated Ollama origin keeps the shared local-only client. - tests: private IPv4, IPv6 loopback, hostname resolving to local (localhost), unresolvable host (fail closed) and public IPv4 coverage. - changelogs (infra 0.5.0 / api 0.4.1) updated.
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@backend/crates/infra/src/ai/transport.rs`:
- Around line 255-261: Update is_public_ip to use a globally reachable address
classifier instead of negating local-address checks, explicitly reject reserved
IPv4 ranges such as 198.18.0.1 and 240.0.0.1, and classify IPv4-compatible IPv6
addresses via Ipv6Addr::to_ipv4() so ::127.0.0.1 follows the IPv4 policy. Add
rejection tests covering all three addresses.
- Around line 309-313: Update the hosted-client builder in the
reqwest::Client::builder chain to call no_proxy() before build(), ensuring
system proxy environment variables cannot bypass the pinned-address policy while
preserving the existing timeout, redirect policy, and address resolution
configuration.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 081a250f-9e02-431b-baa2-a24f36a3608d
📒 Files selected for processing (6)
backend/crates/api/CHANGELOG.mdbackend/crates/infra/CHANGELOG.mdbackend/crates/infra/src/ai/catalog.rsbackend/crates/infra/src/ai/client.rsbackend/crates/infra/src/ai/transport.rsbackend/crates/integration-tests/tests/ai_llm_smoke_test.rs
📜 Review details
⏰ Context from checks skipped due to timeout. (7)
- GitHub Check: Testcontainers integration tests (Postgres + SierraDB)
- GitHub Check: rust_arkitect (source-level rules)
- GitHub Check: Test
- GitHub Check: Build
- GitHub Check: Clippy
- GitHub Check: Check
- GitHub Check: MSRV (rust-version 1.94)
🧰 Additional context used
📓 Path-based instructions (7)
backend/**/*.rs
📄 CodeRabbit inference engine (backend/AGENTS.md)
backend/**/*.rs: Define events in the past tense as enums, commands in the imperative as structs, and aggregates as noun-based state structs.
Generate all entity and event IDs with UUIDv7 usinguuid::Uuid::now_v7(); never use UUIDv4.
Follow the event-storming implementation sequence: past-tense event enum, imperative command struct, then noun-based aggregate state and behavior.
Files:
backend/crates/integration-tests/tests/ai_llm_smoke_test.rsbackend/crates/infra/src/ai/catalog.rsbackend/crates/infra/src/ai/client.rsbackend/crates/infra/src/ai/transport.rs
backend/**/*
📄 CodeRabbit inference engine (backend/AGENTS.md)
Never hardcode secrets; code must pass
gitleaks.
Files:
backend/crates/integration-tests/tests/ai_llm_smoke_test.rsbackend/crates/infra/src/ai/catalog.rsbackend/crates/infra/src/ai/client.rsbackend/crates/infra/CHANGELOG.mdbackend/crates/api/CHANGELOG.mdbackend/crates/infra/src/ai/transport.rs
backend/**/*_test.rs
📄 CodeRabbit inference engine (backend/AGENTS.md)
Tests must not depend on wall-clock timing or jittered sleeps; compute worst-case timing analytically against the test budget.
Files:
backend/crates/integration-tests/tests/ai_llm_smoke_test.rs
backend/crates/integration-tests/**/*.rs
📄 CodeRabbit inference engine (backend/AGENTS.md)
backend/crates/integration-tests/**/*.rs: Keep end-to-end black-box integration tests incrates/integration-tests; use ephemeral Postgres and SierraDB containers, bounded eventual-consistency retries, and only the public APIs of core and infra.
For direct event-appending Tier-4 tests, spawn projectors for every FK-referenced entity, poll for complete expected state rather than mere row existence, and retryNotFoundwhile waiting for projector convergence.
Files:
backend/crates/integration-tests/tests/ai_llm_smoke_test.rs
backend/**/*.{rs,typ,sh}
📄 CodeRabbit inference engine (backend/AGENTS.md)
Use AGPL-3.0 SPDX headers with the required copyright line. Add one separate co-author line per contributor directly below copyright, append without duplicating existing author lines.
Files:
backend/crates/integration-tests/tests/ai_llm_smoke_test.rsbackend/crates/infra/src/ai/catalog.rsbackend/crates/infra/src/ai/client.rsbackend/crates/infra/src/ai/transport.rs
backend/crates/infra/**/*.rs
📄 CodeRabbit inference engine (backend/AGENTS.md)
backend/crates/infra/**/*.rs: Place infrastructure implementations, event-store integrations, projectors, andsqlxqueries incrates/infra, not in core.
Audit metadata such asseries_idmust be resolved best-effort and must never block command processing; returnNoneor a default when projections miss.
Every SQL statement passed tosqlx::query,query_as, orquery_scalarmust be a static string literal; bind all values, and select identifiers such as columns or tables only from hardcoded allowlists.
Map temporary OpenDAL storage errors toDomainError::ServiceUnavailablefor in-loop retries; map permanent errors toValidationError, and ignore only not-found errors in delete paths.
When configuration constants must remain ordered, derive them from one shared named constant and add a compile-time assertion such asconst _INVARIANT: () = assert!(...).
Flush any final partial acknowledgment batch during graceful shutdown beforerun()returnsOk(()).
Files:
backend/crates/infra/src/ai/catalog.rsbackend/crates/infra/src/ai/client.rsbackend/crates/infra/src/ai/transport.rs
backend/crates/*/src/**/*.rs
📄 CodeRabbit inference engine (backend/AGENTS.md)
backend/crates/*/src/**/*.rs: Production code must not useunwrap(),expect(),panic!(),unreachable!(), ortodo!(). Propagate or explicitly handle errors with?, domain errors,anyhow, or fallback matching. Test-only or justified const-time allowances must be documented.
Never discard fallible results withlet _ = <call>in production code. Propagate or handle errors explicitly; suppress only with a justified// ast-grep-ignore: discard-result.
Test-only helpers ending in_for_testmust be gated behind#[cfg(feature = "test-support")]; production API code must not expose test shims.
Files:
backend/crates/infra/src/ai/catalog.rsbackend/crates/infra/src/ai/client.rsbackend/crates/infra/src/ai/transport.rs
🧠 Learnings (1)
📚 Learning: 2026-08-05T14:31:48.648Z
Learnt from: tradem
Repo: tradem/breakdown-rs PR: 169
File: backend/crates/infra/src/ai/workers.rs:334-337
Timestamp: 2026-08-05T14:31:48.648Z
Learning: In AI infrastructure code under backend/crates/infra/src/ai/, do not flag self.mappings.find(...) lookups matched by backend/rules/cqrs-boundary.yml when they have the active // ast-grep-ignore: cqrs-boundary directive. These suppressions are intentional for reviewed AI import idempotency mapping lookups.
Applied to files:
backend/crates/infra/src/ai/catalog.rsbackend/crates/infra/src/ai/client.rsbackend/crates/infra/src/ai/transport.rs
🔇 Additional comments (4)
backend/crates/infra/CHANGELOG.md (1)
12-13: LGTM!Also applies to: 14-40, 42-49, 51-55, 57-62
backend/crates/api/CHANGELOG.md (1)
12-21: LGTM!Also applies to: 23-26, 28-31
backend/crates/integration-tests/tests/ai_llm_smoke_test.rs (1)
56-57: LGTM!backend/crates/infra/src/ai/client.rs (1)
46-67: 🎯 Functional CorrectnessThe async constructor migration is complete. The single caller already awaits the future with
.await?and handles errors correctly. No additional work is needed.
Second CodeRabbit review pass on #194 (issue #170 follow-up): - transport: is_local_ipv4 now also rejects the RFC 2544 benchmarking range (198.18.0.0/15) and the Class E reserved range (240.0.0.0/4, incl. broadcast) — Rust's is_private/is_documentation do not classify them, yet they are never globally routable. - transport: is_local_ipv6 classifies embedded IPv4 via Ipv6Addr::to_ipv4 instead of to_ipv4_mapped, so the deprecated IPv4-compatible form (::a.b.c.d, RFC 4291 §2.5.5.1) is also governed by the IPv4 policy — ::127.0.0.1 previously slipped through as "public". - transport: build_hosted_client disables system proxies (.no_proxy): HTTP_PROXY/HTTPS_PROXY/ALL_PROXY would resolve the CONNECT target outside the pinned-address policy and tunnel bearer credentials through an unvalidated hop. - tests: rejection coverage for 198.18.0.0/15, 240.0.0.0/4, broadcast, IPv4-compatible and IPv4-mapped IPv6; public IPv6 positive case.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@backend/crates/infra/src/ai/transport.rs`:
- Around line 211-231: Update is_local_ipv4 to reject the entire 0.0.0.0/8
range, not only Ipv4Addr::UNSPECIFIED, and update is_local_ipv6 to reject the
IPv6 site-local fec0::/10 prefix. Add tests covering 0.0.0.1 and fec0::1 while
preserving existing local-address handling.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: aa899bb3-a96d-4dc3-8894-4650e3623f99
📒 Files selected for processing (2)
backend/crates/infra/CHANGELOG.mdbackend/crates/infra/src/ai/transport.rs
📜 Review details
⏰ Context from checks skipped due to timeout. (7)
- GitHub Check: Clippy
- GitHub Check: MSRV (rust-version 1.94)
- GitHub Check: Check
- GitHub Check: Build
- GitHub Check: Test
- GitHub Check: Testcontainers integration tests (Postgres + SierraDB)
- GitHub Check: rust_arkitect (source-level rules)
🧰 Additional context used
📓 Path-based instructions (5)
backend/**/*
📄 CodeRabbit inference engine (backend/AGENTS.md)
Never hardcode secrets; code must pass
gitleaks.
Files:
backend/crates/infra/CHANGELOG.mdbackend/crates/infra/src/ai/transport.rs
backend/crates/infra/**/*.rs
📄 CodeRabbit inference engine (backend/AGENTS.md)
backend/crates/infra/**/*.rs: Place infrastructure implementations, event-store integrations, projectors, andsqlxqueries incrates/infra, not in core.
Audit metadata such asseries_idmust be resolved best-effort and must never block command processing; returnNoneor a default when projections miss.
Every SQL statement passed tosqlx::query,query_as, orquery_scalarmust be a static string literal; bind all values, and select identifiers such as columns or tables only from hardcoded allowlists.
Map temporary OpenDAL storage errors toDomainError::ServiceUnavailablefor in-loop retries; map permanent errors toValidationError, and ignore only not-found errors in delete paths.
When configuration constants must remain ordered, derive them from one shared named constant and add a compile-time assertion such asconst _INVARIANT: () = assert!(...).
Flush any final partial acknowledgment batch during graceful shutdown beforerun()returnsOk(()).
Files:
backend/crates/infra/src/ai/transport.rs
backend/**/*.rs
📄 CodeRabbit inference engine (backend/AGENTS.md)
backend/**/*.rs: Define events in the past tense as enums, commands in the imperative as structs, and aggregates as noun-based state structs.
Generate all entity and event IDs with UUIDv7 usinguuid::Uuid::now_v7(); never use UUIDv4.
Follow the event-storming implementation sequence: past-tense event enum, imperative command struct, then noun-based aggregate state and behavior.
Files:
backend/crates/infra/src/ai/transport.rs
backend/crates/*/src/**/*.rs
📄 CodeRabbit inference engine (backend/AGENTS.md)
backend/crates/*/src/**/*.rs: Production code must not useunwrap(),expect(),panic!(),unreachable!(), ortodo!(). Propagate or explicitly handle errors with?, domain errors,anyhow, or fallback matching. Test-only or justified const-time allowances must be documented.
Never discard fallible results withlet _ = <call>in production code. Propagate or handle errors explicitly; suppress only with a justified// ast-grep-ignore: discard-result.
Test-only helpers ending in_for_testmust be gated behind#[cfg(feature = "test-support")]; production API code must not expose test shims.
Files:
backend/crates/infra/src/ai/transport.rs
backend/**/*.{rs,typ,sh}
📄 CodeRabbit inference engine (backend/AGENTS.md)
Use AGPL-3.0 SPDX headers with the required copyright line. Add one separate co-author line per contributor directly below copyright, append without duplicating existing author lines.
Files:
backend/crates/infra/src/ai/transport.rs
🧠 Learnings (1)
📚 Learning: 2026-08-05T14:31:48.648Z
Learnt from: tradem
Repo: tradem/breakdown-rs PR: 169
File: backend/crates/infra/src/ai/workers.rs:334-337
Timestamp: 2026-08-05T14:31:48.648Z
Learning: In AI infrastructure code under backend/crates/infra/src/ai/, do not flag self.mappings.find(...) lookups matched by backend/rules/cqrs-boundary.yml when they have the active // ast-grep-ignore: cqrs-boundary directive. These suppressions are intentional for reviewed AI import idempotency mapping lookups.
Applied to files:
backend/crates/infra/src/ai/transport.rs
🔇 Additional comments (2)
backend/crates/infra/CHANGELOG.md (1)
12-13: LGTM!Also applies to: 16-43, 45-52, 54-58, 60-65
backend/crates/infra/src/ai/transport.rs (1)
317-330: LGTM!Also applies to: 581-590
Third CodeRabbit review pass on #194 (issue #170 follow-up): - is_local_ipv4 rejects the whole 0.0.0.0/8 "this network" range (RFC 1122 §3.2.1.3), not only 0.0.0.0 — is_unspecified() matches a single address, so 0.0.0.1 previously slipped through as "public". - is_local_ipv6 rejects the deprecated site-local prefix fec0::/10 (RFC 3879), which is outside is_unique_local()'s fc00::/7. - tests: 0.0.0.0/8 (0.0.0.1, 0.255.255.255) and fec0::/10 (fec0::1) rejection coverage added.
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
backend/crates/infra/src/ai/transport.rs (3)
283-314: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick winBound DNS resolution separately from HTTP requests.
lookup_hostruns before thereqwest::Clientexists, so the client timeout cannot limit DNS resolution. Wrap the lookup intokio::time::timeoutand map expiration toDnsLookupFailed.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@backend/crates/infra/src/ai/transport.rs` around lines 283 - 314, Update validate_public_resolution to wrap tokio::net::lookup_host in tokio::time::timeout using the established client/request timeout value, mapping timeout expiration to RedirectViolation::DnsLookupFailed while preserving existing lookup-error and address-validation behavior.
616-644: 🔒 Security & Privacy | 🔵 Trivial | 🏗️ Heavy liftMake the pinning test verify the selected address.
The test checks only the request URL and performs no request. It would pass if
resolve_to_addrswere removed. Add a controlled-address test that sends a request and proves the validatedSocketAddris used.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@backend/crates/infra/src/ai/transport.rs` around lines 616 - 644, The build_hosted_client_pins_and_validates_public_destination test must exercise the client with a controlled resolver/server and assert that an actual request reaches the validated SocketAddr, rather than only checking the URL. Set up a deterministic local listener or equivalent address-controlled endpoint, invoke the built client, and verify the request is served by that selected address while preserving the existing validation behavior.
216-235: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick winReject the remaining non-global addresses.
is_local_ipv4accepts192.0.0.1, andIpv6Addr::to_ipv4()converts::8.8.8.8to8.8.8.8, which the current classifier accepts. Use a global-address classifier supported by the MSRV, or reject these ranges explicitly. Add both addresses to the rejection tests.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@backend/crates/infra/src/ai/transport.rs` around lines 216 - 235, Update is_local_ipv4 to reject all remaining non-global IPv4 ranges, including 192.0.0.1 and IPv4-compatible IPv6 values converted by Ipv6Addr::to_ipv4, using an MSRV-compatible global classifier or explicit range checks. Extend the rejection tests to cover both addresses.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@backend/crates/infra/src/ai/transport.rs`:
- Around line 283-314: Update validate_public_resolution to wrap
tokio::net::lookup_host in tokio::time::timeout using the established
client/request timeout value, mapping timeout expiration to
RedirectViolation::DnsLookupFailed while preserving existing lookup-error and
address-validation behavior.
- Around line 616-644: The
build_hosted_client_pins_and_validates_public_destination test must exercise the
client with a controlled resolver/server and assert that an actual request
reaches the validated SocketAddr, rather than only checking the URL. Set up a
deterministic local listener or equivalent address-controlled endpoint, invoke
the built client, and verify the request is served by that selected address
while preserving the existing validation behavior.
- Around line 216-235: Update is_local_ipv4 to reject all remaining non-global
IPv4 ranges, including 192.0.0.1 and IPv4-compatible IPv6 values converted by
Ipv6Addr::to_ipv4, using an MSRV-compatible global classifier or explicit range
checks. Extend the rejection tests to cover both addresses.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: aa168cb5-0ff9-40cf-87e3-9363976f4735
📒 Files selected for processing (2)
backend/crates/infra/CHANGELOG.mdbackend/crates/infra/src/ai/transport.rs
📜 Review details
🧰 Additional context used
📓 Path-based instructions (5)
backend/**/*
📄 CodeRabbit inference engine (backend/AGENTS.md)
Never hardcode secrets; code must pass
gitleaks.
Files:
backend/crates/infra/CHANGELOG.mdbackend/crates/infra/src/ai/transport.rs
backend/crates/infra/**/*.rs
📄 CodeRabbit inference engine (backend/AGENTS.md)
backend/crates/infra/**/*.rs: Place infrastructure implementations, event-store integrations, projectors, andsqlxqueries incrates/infra, not in core.
Audit metadata such asseries_idmust be resolved best-effort and must never block command processing; returnNoneor a default when projections miss.
Every SQL statement passed tosqlx::query,query_as, orquery_scalarmust be a static string literal; bind all values, and select identifiers such as columns or tables only from hardcoded allowlists.
Map temporary OpenDAL storage errors toDomainError::ServiceUnavailablefor in-loop retries; map permanent errors toValidationError, and ignore only not-found errors in delete paths.
When configuration constants must remain ordered, derive them from one shared named constant and add a compile-time assertion such asconst _INVARIANT: () = assert!(...).
Flush any final partial acknowledgment batch during graceful shutdown beforerun()returnsOk(()).
Files:
backend/crates/infra/src/ai/transport.rs
backend/**/*.rs
📄 CodeRabbit inference engine (backend/AGENTS.md)
backend/**/*.rs: Define events in the past tense as enums, commands in the imperative as structs, and aggregates as noun-based state structs.
Generate all entity and event IDs with UUIDv7 usinguuid::Uuid::now_v7(); never use UUIDv4.
Follow the event-storming implementation sequence: past-tense event enum, imperative command struct, then noun-based aggregate state and behavior.
Files:
backend/crates/infra/src/ai/transport.rs
backend/crates/*/src/**/*.rs
📄 CodeRabbit inference engine (backend/AGENTS.md)
backend/crates/*/src/**/*.rs: Production code must not useunwrap(),expect(),panic!(),unreachable!(), ortodo!(). Propagate or explicitly handle errors with?, domain errors,anyhow, or fallback matching. Test-only or justified const-time allowances must be documented.
Never discard fallible results withlet _ = <call>in production code. Propagate or handle errors explicitly; suppress only with a justified// ast-grep-ignore: discard-result.
Test-only helpers ending in_for_testmust be gated behind#[cfg(feature = "test-support")]; production API code must not expose test shims.
Files:
backend/crates/infra/src/ai/transport.rs
backend/**/*.{rs,typ,sh}
📄 CodeRabbit inference engine (backend/AGENTS.md)
Use AGPL-3.0 SPDX headers with the required copyright line. Add one separate co-author line per contributor directly below copyright, append without duplicating existing author lines.
Files:
backend/crates/infra/src/ai/transport.rs
🧠 Learnings (1)
📚 Learning: 2026-08-05T14:31:48.648Z
Learnt from: tradem
Repo: tradem/breakdown-rs PR: 169
File: backend/crates/infra/src/ai/workers.rs:334-337
Timestamp: 2026-08-05T14:31:48.648Z
Learning: In AI infrastructure code under backend/crates/infra/src/ai/, do not flag self.mappings.find(...) lookups matched by backend/rules/cqrs-boundary.yml when they have the active // ast-grep-ignore: cqrs-boundary directive. These suppressions are intentional for reviewed AI import idempotency mapping lookups.
Applied to files:
backend/crates/infra/src/ai/transport.rs
🔇 Additional comments (2)
backend/crates/infra/CHANGELOG.md (1)
5-13: LGTM!Also applies to: 25-29, 36-44, 46-53, 55-59, 61-66
backend/crates/infra/src/ai/transport.rs (1)
49-50: LGTM!Also applies to: 79-87, 116-130, 149-156, 316-340
Summary
The last PR merged to main (#193, AI provider transport security fix) changed
infra— including new public API — but no version bump followed. This PR bumps the versions per ADR-020 and adds release notes (English) so the next release can be cut.Version bumps
infraai::transport+ redirect policies); breakingOpenAiCompatibleModelCatalog::newsignature (no in-tree callers, minor under major-zero)apiapiimage; no crate API change,/v1unchanged; re-pinsinfra0.5.0coreDependent re-pins (equivalent to
cargo release --dependent):architecture→ api 0.4.1 / infra 0.5.0,integration-tests→ infra 0.5.0,Cargo.lockregenerated.Release notes
Added as per-crate
CHANGELOG.md(ADR-020 D5):infra0.5.0 — Security fix: AI provider transport now enforces HTTPS-only for hosted providers and restricts Ollama to local addresses (issue Enforce AI provider HTTPS and Ollama redirect policy #170, SSRF/credential-exfiltration via redirects); new additiveai::transportmodule; dependency updates (opendal 0.52→0.58, aes-gcm 0.10→0.11, base64 0.22→0.23, getrandom 0.3→0.4, rand_core 0.6→0.10, redis 1.4→1.5, schemars 1.2.1→1.2.2, serde 1.0.228→1.0.229, sha2 0.10→0.11).api0.4.1 — Ships the AI transport security fix via theapiimage (re-pininfra0.5.0); PATCH,/v1unchanged.Release trigger
Pushing the tags
infra-v0.5.0/api-v0.4.1(cut locally by the release owner, ADR-020 D5) triggers theapiimage build viarelease-image.yml(tagapi-v0.4.1→ghcr.io/<org>/breakdown-rs:api-v0.4.1+ movingapi-v0.4/api-v0).The current
api-v0.4.0/infra-v0.4.0tags point to commitd1c00ee, which is not onmain(the versioning PR was squash-merged afterwards). Consequences: the semver-checks baseline (git describe --tags) does not resolve on PR heads (check is skipped), and the 0.4.0 image predates the squash-merged versioning code. Consider re-cutting the 0.4.0 tags onmain's current04b3b6bor treating 0.5.0 as the first main-aligned release.