Plan: Crate Quality Parity

On this page

Status

Step Description Status

1

Add doc comments to all public APIs in shared crates

Done (2026-04-14) — #![warn(missing_docs)] on shared crates per roadmap Tier 7

2

Add security headers middleware (CSP, X-Frame-Options, X-Content-Type-Options)

Done (2026-04-18) — SetResponseHeaderLayer in canopy-api/src/lib.rs lines 123-135

3

Add rate limiting module using governor crate

Done (2026-04-18) — rate_limit_middleware in canopy-api/src/lib.rs, rate_limit_rpm default 6000

4

Change CORS default from "*" to explicit localhost origins

Done (2026-04-18) — default http://localhost:3000,http://localhost:8080

5

Modularize canopy-test-lib into focused submodules

Done (2026-04-18) — split into auth, client, config, infrastructure, poll submodules

6

Expand canopy-auth defensive test coverage

Done (2026-04-18) — 15 tests in jwks.rs: valid token, expired, wrong audience, wrong issuer, missing kid, unknown kid, tampered signature, malformed, empty string, JWKS not loaded, forced refresh debounce, audience validation

7

Introduce newtype wrappers for domain IDs

Done (2026-04-18) — 20 ID types via define_id! macro in canopy-common/src/id.rs

8

Full validation pass

Done (2026-04-18) — clippy zero warnings, 572 unit tests pass, cargo doc builds

Branch: chore/crate-quality-parity

Context

Canopy’s shared crates (crates/canopy-*) provide the foundation for all services. Security headers, rate limiting, CORS, and newtype IDs are complete. Three quality items remain:

  1. Doc comments: Most public functions and structs lack /// doc comments. Adding #![warn(missing_docs)] to each crate surfaces gaps.

  2. Test-lib modularization: canopy-test-lib has 4 files (lib.rs, auth.rs, client.rs, infrastructure.rs). Growing test infrastructure would benefit from clearer module boundaries.

  3. Auth defensive tests: canopy-auth/src/claims.rs has 16 unit tests covering role checks, but canopy-auth/src/jwks.rs has only 5 tests. Missing: expired token handling, wrong audience, malformed JWT body, concurrent JWKS refresh.

Steps

Step 1: Add doc comments to shared crates

Files: All crates/canopy-/src/.rs files

For each shared crate, add #![warn(missing_docs)] to lib.rs and fix all resulting warnings by adding /// doc comments to every public item (struct, enum, function, module, trait, constant).

Crates to cover (in dependency order):

Crate Key public items

canopy-common

au_composition module (AuMemberStatus, AssistanceUnit), error module (ApiError), fti_audit module, id module (define_id macro, 20 ID types), pagination, date, settings

canopy-auth

Claims, RealmAccess, JwksManager, JwksConfig

canopy-db

connect(), validate_database_name(), run_migrations()

canopy-mq

Publisher, Subscriber, EventEnvelope, TraceContext

canopy-api

ApiServer, AppState, ServerOptions, TrustedProxies, rate limiting types

canopy-store

S3 object store helpers

canopy-reference

All enums (Program, DeterminationStatus, IncomeType, AssetType, etc.), cross_program constants

canopy-rules-client

RulesClient, EvaluateRequest, EvaluateResponse

canopy-signing

SigningKey, VerifyingKeyRegistry, DeterminationSigner trait, RotationState

canopy-typst

render_pdf(), template loading

canopy-policy

CitationManifest, Citation, WorkflowTemplate, WorkflowStep, validation functions

canopy-test-lib

TestClient, TestResponse, acquire_token, infrastructure_available

Verification: cargo doc --workspace --no-deps builds without warnings.

Step 5: Modularize canopy-test-lib

Files: crates/canopy-test-lib/src/

Current structure (4 files):

src/
├── lib.rs           # re-exports
├── auth.rs          # acquire_token, acquire_token_for
├── client.rs        # TestClient, TestResponse
└── infrastructure.rs # infrastructure_available

Proposed structure (no new files needed, but add focused re-export groupings):

The current 4-file structure is already clean. The planned 8-submodule split (builders, clients, harness, config, events, signing, token) was designed for a much larger test lib. With only 4 functional modules, the current structure is appropriate.

Action: Add doc comments to all public items. Do NOT split into more files — the current structure is right-sized.

Step 6: Expand canopy-auth defensive test coverage

Files: crates/canopy-auth/src/jwks.rs

Current JWKS tests (5): - jwks_refresh_populates_keys - unknown_kid_triggers_retry_flow - validate_token_unknown_kid_rejected - forced_refresh_debounce_within_30_seconds - validate_malformed_token_rejected

Tests to add:

#[tokio::test]
async fn expired_token_rejected() {
    // Create a token with exp in the past
    // Verify validate() returns Err
}

#[tokio::test]
async fn wrong_audience_rejected() {
    // Create a token with aud != expected
    // Verify validate() returns Err
}

#[tokio::test]
async fn token_without_exp_rejected() {
    // JWT missing exp claim
    // Verify validate() returns Err
}

#[tokio::test]
async fn empty_token_rejected() {
    // Empty string token
    // Verify validate() returns Err
}

Note: These may require constructing test JWTs with jsonwebtoken::encode() and a test RSA key. The existing tests in jwks.rs already use a mock JWKS endpoint — follow the same pattern.

Step 8: Full validation pass

Run all checks to confirm quality parity:

  1. cargo doc --workspace --no-deps — zero warnings

  2. cargo clippy --all-targets — -D warnings — zero warnings

  3. cargo xtask test --unit — all tests pass

  4. cargo xtask test — all integration tests pass

  5. Every shared crate has #![warn(missing_docs)] in its lib.rs

Files Touched

File Change

crates/canopy-*/src/lib.rs

Add #![warn(missing_docs)] to each crate

crates/canopy-/src/.rs

Add /// doc comments to all public items

crates/canopy-auth/src/jwks.rs

Add 4 defensive test scenarios

crates/canopy-test-lib/src/*.rs

Add doc comments (no structural changes)

Verification

  1. cargo doc --workspace --no-deps — zero warnings

  2. cargo clippy --all-targets — -D warnings — zero warnings

  3. cargo xtask test --unit — all tests pass (572+)

  4. cargo xtask test — all integration tests pass (211+)

Edit this page · default