Skip to content

chore(release): bump infra to 0.5.0 and api to 0.4.1 (ADR-020 D2/D6) - #194

Merged
tradem merged 4 commits into
mainfrom
chore/bump-versions-0.5.0
Aug 6, 2026
Merged

chore(release): bump infra to 0.5.0 and api to 0.4.1 (ADR-020 D2/D6)#194
tradem merged 4 commits into
mainfrom
chore/bump-versions-0.5.0

Conversation

@tradem

@tradem tradem commented Aug 6, 2026

Copy link
Copy Markdown
Owner

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

Crate From To Kind Reason
infra 0.4.0 0.5.0 MINOR (D2) Additive public API (ai::transport + redirect policies); breaking OpenAiCompatibleModelCatalog::new signature (no in-tree callers, minor under major-zero)
api 0.4.0 0.4.1 PATCH (D6) Security fix ships via the api image; no crate API change, /v1 unchanged; re-pins infra 0.5.0
core 0.3.0 Unchanged

Dependent re-pins (equivalent to cargo release --dependent): architecture → api 0.4.1 / infra 0.5.0, integration-tests → infra 0.5.0, Cargo.lock regenerated.

Release notes

Added as per-crate CHANGELOG.md (ADR-020 D5):

  • infra 0.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 additive ai::transport module; 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).
  • api 0.4.1 — Ships the AI transport security fix via the api image (re-pin infra 0.5.0); PATCH, /v1 unchanged.

Release trigger

Pushing the tags infra-v0.5.0 / api-v0.4.1 (cut locally by the release owner, ADR-020 D5) triggers the api image build via release-image.yml (tag api-v0.4.1ghcr.io/<org>/breakdown-rs:api-v0.4.1 + moving api-v0.4 / api-v0).

⚠️ Heads-up (pre-existing, not fixed here)

The current api-v0.4.0 / infra-v0.4.0 tags point to commit d1c00ee, which is not on main (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 on main's current 04b3b6b or treating 0.5.0 as the first main-aligned release.

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).
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • Security

    • Strengthened transport security for AI provider connections.
    • Added DNS-rebinding protection, public-address validation, and safer redirect handling.
    • Hosted connections now fail safely when destinations cannot be validated.
  • Improvements

    • Improved reliability when connecting to hosted and local AI providers.
    • Enhanced hosted provider model discovery and connection setup.
    • Refined release versioning and updated supporting libraries.
  • Documentation

    • Added release notes covering security improvements, API updates, and dependency changes.

Walkthrough

The PR adds DNS-rebinding protection for hosted AI providers, updates asynchronous client construction and model listing, and releases infra 0.5.0 and api 0.4.1 with aligned manifests and changelogs.

Changes

AI transport security and crate releases

Layer / File(s) Summary
Hosted transport validation
backend/crates/infra/src/ai/transport.rs
Hosted destinations are resolved, checked for public addresses, pinned in HTTP clients, and tested across internal, invalid, and public address classes.
AI client and catalog integration
backend/crates/infra/src/ai/catalog.rs, backend/crates/infra/src/ai/client.rs, backend/crates/integration-tests/tests/ai_llm_smoke_test.rs
Ollama keeps the shared client. Hosted providers use validated clients. Chat client construction is asynchronous, and the smoke test awaits it.
Crate release metadata and dependency alignment
backend/crates/infra/Cargo.toml, backend/crates/infra/CHANGELOG.md, backend/crates/api/Cargo.toml, backend/crates/api/CHANGELOG.md, backend/crates/architecture/Cargo.toml, backend/crates/integration-tests/Cargo.toml
The crates are versioned to infra 0.5.0 and api 0.4.1. Dependent manifests and release notes use the updated versions and document the transport API changes.

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
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the primary version bumps for the infra and api crates and references the governing ADR.
Description check ✅ Passed The description directly explains the version bumps, release notes, dependency updates, release flow, and related security changes.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/bump-versions-0.5.0

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 6f2e5a7 and 7beadb2.

⛔ Files ignored due to path filters (1)
  • backend/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (6)
  • backend/crates/api/CHANGELOG.md
  • backend/crates/api/Cargo.toml
  • backend/crates/architecture/Cargo.toml
  • backend/crates/infra/CHANGELOG.md
  • backend/crates/infra/Cargo.toml
  • backend/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.md
  • backend/crates/infra/Cargo.toml
  • backend/crates/architecture/Cargo.toml
  • backend/crates/integration-tests/Cargo.toml
  • backend/crates/api/Cargo.toml
  • backend/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.toml
  • backend/crates/architecture/Cargo.toml
  • backend/crates/integration-tests/Cargo.toml
  • backend/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 & Integration

Keep api at version 0.4.1.

The api crate does not re-export or expose OpenAiCompatibleModelCatalog or its constructor.

			> Likely an incorrect or invalid review comment.

Comment thread backend/crates/infra/CHANGELOG.md
…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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 7beadb2 and 7d142ec.

📒 Files selected for processing (6)
  • backend/crates/api/CHANGELOG.md
  • backend/crates/infra/CHANGELOG.md
  • backend/crates/infra/src/ai/catalog.rs
  • backend/crates/infra/src/ai/client.rs
  • backend/crates/infra/src/ai/transport.rs
  • backend/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 using uuid::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.rs
  • backend/crates/infra/src/ai/catalog.rs
  • backend/crates/infra/src/ai/client.rs
  • backend/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.rs
  • backend/crates/infra/src/ai/catalog.rs
  • backend/crates/infra/src/ai/client.rs
  • backend/crates/infra/CHANGELOG.md
  • backend/crates/api/CHANGELOG.md
  • backend/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 in crates/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 retry NotFound while 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.rs
  • backend/crates/infra/src/ai/catalog.rs
  • backend/crates/infra/src/ai/client.rs
  • backend/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, and sqlx queries in crates/infra, not in core.
Audit metadata such as series_id must be resolved best-effort and must never block command processing; return None or a default when projections miss.
Every SQL statement passed to sqlx::query, query_as, or query_scalar must 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 to DomainError::ServiceUnavailable for in-loop retries; map permanent errors to ValidationError, 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 as const _INVARIANT: () = assert!(...).
Flush any final partial acknowledgment batch during graceful shutdown before run() returns Ok(()).

Files:

  • backend/crates/infra/src/ai/catalog.rs
  • backend/crates/infra/src/ai/client.rs
  • 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 use unwrap(), expect(), panic!(), unreachable!(), or todo!(). 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 with let _ = <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_test must be gated behind #[cfg(feature = "test-support")]; production API code must not expose test shims.

Files:

  • backend/crates/infra/src/ai/catalog.rs
  • backend/crates/infra/src/ai/client.rs
  • 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/catalog.rs
  • backend/crates/infra/src/ai/client.rs
  • backend/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 Correctness

The async constructor migration is complete. The single caller already awaits the future with .await? and handles errors correctly. No additional work is needed.

Comment thread backend/crates/infra/src/ai/transport.rs
Comment thread backend/crates/infra/src/ai/transport.rs
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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 7d142ec and ec92dde.

📒 Files selected for processing (2)
  • backend/crates/infra/CHANGELOG.md
  • backend/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.md
  • backend/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, and sqlx queries in crates/infra, not in core.
Audit metadata such as series_id must be resolved best-effort and must never block command processing; return None or a default when projections miss.
Every SQL statement passed to sqlx::query, query_as, or query_scalar must 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 to DomainError::ServiceUnavailable for in-loop retries; map permanent errors to ValidationError, 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 as const _INVARIANT: () = assert!(...).
Flush any final partial acknowledgment batch during graceful shutdown before run() returns Ok(()).

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 using uuid::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 use unwrap(), expect(), panic!(), unreachable!(), or todo!(). 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 with let _ = <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_test must 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

Comment thread backend/crates/infra/src/ai/transport.rs
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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 win

Bound DNS resolution separately from HTTP requests.

lookup_host runs before the reqwest::Client exists, so the client timeout cannot limit DNS resolution. Wrap the lookup in tokio::time::timeout and map expiration to DnsLookupFailed.

🤖 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 lift

Make the pinning test verify the selected address.

The test checks only the request URL and performs no request. It would pass if resolve_to_addrs were removed. Add a controlled-address test that sends a request and proves the validated SocketAddr is 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 win

Reject the remaining non-global addresses.

is_local_ipv4 accepts 192.0.0.1, and Ipv6Addr::to_ipv4() converts ::8.8.8.8 to 8.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

📥 Commits

Reviewing files that changed from the base of the PR and between ec92dde and 2c27a36.

📒 Files selected for processing (2)
  • backend/crates/infra/CHANGELOG.md
  • backend/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.md
  • backend/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, and sqlx queries in crates/infra, not in core.
Audit metadata such as series_id must be resolved best-effort and must never block command processing; return None or a default when projections miss.
Every SQL statement passed to sqlx::query, query_as, or query_scalar must 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 to DomainError::ServiceUnavailable for in-loop retries; map permanent errors to ValidationError, 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 as const _INVARIANT: () = assert!(...).
Flush any final partial acknowledgment batch during graceful shutdown before run() returns Ok(()).

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 using uuid::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 use unwrap(), expect(), panic!(), unreachable!(), or todo!(). 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 with let _ = <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_test must 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

@tradem tradem self-assigned this Aug 6, 2026
@tradem
tradem merged commit 71cb860 into main Aug 6, 2026
20 of 31 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant