This is an automated email from the ASF dual-hosted git repository. xuanwo pushed a commit to branch xuanwo/default-provider-api-docs in repository https://gitbox.apache.org/repos/asf/opendal-reqsign.git
commit 8f844c0a29379e59636700c7b3913be799b41d8a Author: Xuanwo <[email protected]> AuthorDate: Wed Mar 18 18:37:14 2026 +0800 docs: define default credential provider API policy --- AGENTS.md | 80 +++++++++++++ CLAUDE.md | 83 +------------- docs/default-credential-provider-api.md | 197 ++++++++++++++++++++++++++++++++ 3 files changed, 278 insertions(+), 82 deletions(-) diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..cd4dc01 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,80 @@ +# CLAUDE.md + +## Project Overview + +Apache OpenDAL reqsign — a Rust library for signing HTTP API requests across +cloud providers (AWS, Azure, Google, Aliyun, Huawei, Tencent, Oracle, +Volcengine). + +## Architecture + +The codebase is a Cargo workspace with three layers: + +- **`core`** (`reqsign-core`): traits (`ProvideCredential`, `SignRequest`, + `SigningCredential`) and the runtime-agnostic `Context` / `Signer` types. +- **`context/*`**: pluggable runtime adapters (`file-read-tokio`, + `http-send-reqwest`, `command-execute-tokio`). +- **`services/*`**: per-cloud signing implementations, each its own crate. +- **`reqsign`**: facade crate re-exporting everything behind feature flags; + provides `default_context()` and per-service `default_signer()`. + +### Key Design Decisions + +- **No default implementations in `Context`**: `Context::new()` wires up + no-op stubs. Users (or `default_context()`) must explicitly plug in + `FileRead`, `HttpSend`, `Env`, `CommandExecute`. +- **`MaybeSend` futures**: core traits use `MaybeSend` instead of `async_trait` + so that the crate compiles for `wasm32-unknown-unknown`. +- **Dyn-trait pairs**: every async trait `Foo` has a `FooDyn` counterpart and a + blanket `impl FooDyn for T: Foo`, enabling `Arc<dyn FooDyn>` inside `Signer`. + +## API Design References + +When working on an API area with an explicit design document, follow that +document as the source of truth instead of inferring policy from the current +implementation. + +### Default Credential Provider + +All future refactors and API reviews around `DefaultCredentialProvider` must +follow the authoritative design in +[docs/default-credential-provider-api.md](docs/default-credential-provider-api.md). + +Treat any future API change that reintroduces the following as a design +regression unless explicitly approved: `configure_*`, `disable_*(bool)`, +fallback-based re-enabling of removed slots during `build()`. + +## Build & Test + +```bash +cargo fmt --all -- --check +cargo clippy --workspace --all-targets --all-features -- -D warnings +cargo test --no-fail-fast # unit tests +cargo test --doc --all-features --workspace # doc tests +``` + +### Integration Tests + +Integration tests live per-service under `services/<name>/tests/` and are gated +by `REQSIGN_<SERVICE>_TEST*` env vars. Secrets are loaded via 1Password Connect +in CI. Some providers (IMDS, ECS, SSO, Process, Cognito) use Python mock +servers checked into `services/aws-v4/tests/mocks/`. + +## WASM Compatibility + +`reqsign-core` and a subset of services (`aws`, `azure`, `aliyun`, `tencent`) +must compile for `wasm32-unknown-unknown`. Google is excluded because +`jsonwebtoken` does not support WASM. CI verifies this. + +## Versioning + +- Service crates and core share a lockstep major version (`3.0.0`). +- `reqsign-http-send-reqwest` has its own major (`4.0.0`). +- The facade `reqsign` crate follows an independent scheme (`0.20.0`). +- Release is triggered by pushing a `v*.*.*` tag; `cargo publish --workspace` + publishes everything. + +## License + +Apache-2.0 (ASF). All source files must carry the Apache header, enforced by +hawkeye via `licenserc.toml`. diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 8d18801..0000000 --- a/CLAUDE.md +++ /dev/null @@ -1,82 +0,0 @@ -# CLAUDE.md - -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. - -## Project Overview - -reqsign is a Rust library for signing HTTP API requests for cloud services (AWS, Azure, Google Cloud, Aliyun, Huawei Cloud, Oracle Cloud, Tencent Cloud). It follows a "build, sign, send" philosophy with modular architecture. - -## Common Development Commands - -### Build and Check -```bash -cargo check # Analyze code without building -cargo build --workspace # Build all crates -cargo build --workspace --release # Build optimized release version -``` - -### Testing -```bash -cargo test --no-fail-fast # Run all tests -cargo test --workspace --no-fail-fast # Test entire workspace -cargo test tests::it::services::fs # Test specific function -cargo test -p reqsign-aws-v4 # Test specific service crate -RUST_LOG=debug cargo test # Test with debug logging -``` - -### Linting and Formatting -```bash -cargo fmt --all # Format all code -cargo fmt --all -- --check # Check formatting without changes -cargo clippy --workspace --all-targets --all-features -- -D warnings # Lint with all features -``` - -### WASM Build -```bash -cargo build --workspace --target wasm32-unknown-unknown --exclude reqsign-file-read-tokio --exclude reqsign-http-send-reqwest -``` - -## Architecture - -### Workspace Structure -- `core/` - Core signing functionality and abstractions -- `context/` - Pluggable I/O implementations: - - `file-read-tokio/` - Async file reading with Tokio - - `http-send-reqwest/` - HTTP client with reqwest -- `services/` - Provider-specific implementations: - - `aws-v4/` - AWS Signature Version 4 - - `azure-storage/` - Azure Storage services - - `google/` - Google Cloud services - - `aliyun-oss/`, `huaweicloud-obs/`, `oracle/`, `tencent-cos/` - Other providers -- `reqsign/` - Main crate that re-exports all functionality with feature flags - -### Key Design Patterns -1. **Context System**: Abstract I/O operations (file reading, HTTP sending) behind traits in `core`, with implementations in `context/` -2. **Service Modularity**: Each cloud provider is a separate crate, allowing users to include only needed providers -3. **Feature Flags**: Main `reqsign` crate uses features to control which services are included -4. **Credential Loading**: Services load credentials from environment variables, with provider-specific prefixes - -### Testing Strategy -- Copy `.env.example` to `.env` and configure service credentials for integration tests -- Service tests can be disabled via environment variables (e.g., `REQSIGN_AWS_V4_TEST=false`) -- Tests require real service credentials for full integration testing -- Use `RUST_LOG=debug` for detailed test output - -## Development Tips - -### Adding New Services -1. Create new crate in `services/` directory -2. Implement service-specific signing logic -3. Add feature flag in main `reqsign/Cargo.toml` -4. Re-export in `reqsign/src/lib.rs` behind feature gate - -### Working with Specific Services -```bash -cd services/aws-v4 && cargo test # Test single service -cargo test -p reqsign-azure-storage # Test from workspace root -``` - -### Debugging -- Set `RUST_LOG=debug` for verbose output -- Set `RUST_BACKTRACE=full` for detailed error traces -- Check `.env.example` for required environment variables per service \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md new file mode 120000 index 0000000..47dc3e3 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +AGENTS.md \ No newline at end of file diff --git a/docs/default-credential-provider-api.md b/docs/default-credential-provider-api.md new file mode 100644 index 0000000..d46ee92 --- /dev/null +++ b/docs/default-credential-provider-api.md @@ -0,0 +1,197 @@ +# Default Credential Provider API Design + +This document defines the repository's authoritative public API design for all +`DefaultCredentialProvider` types. + +Implementations that do not match this document should be treated as legacy +debt. New code, reviews, and refactors must follow this document. + +## Goals + +- Make provider participation explicit. +- Remove boolean toggles from the public API. +- Remove `configure_*` patch-style APIs from the public API. +- Keep the top-level `DefaultCredentialProvider` API uniform across services. +- Keep builder APIs predictable across services while allowing service-specific + provider slots. + +## Product API + +Every service-level default provider exposes the same product-level API: + +```rust +DefaultCredentialProvider::new() +DefaultCredentialProvider::builder() +DefaultCredentialProvider::with_chain(chain) +DefaultCredentialProvider::push_front(provider) +impl Default for DefaultCredentialProvider +``` + +### Semantics + +- `new()` creates the documented default chain for that service. +- `default()` is identical to `new()`. +- `builder()` returns a builder pre-populated with the documented default slots. +- `with_chain(chain)` bypasses all default-chain assembly logic. +- `push_front(provider)` prepends a high-priority provider in front of the + documented default chain. + +## Builder API + +Every `DefaultCredentialProviderBuilder` exposes: + +```rust +DefaultCredentialProviderBuilder::new() +DefaultCredentialProviderBuilder::default() +.build() +``` + +For each supported provider slot, the builder exposes exactly two primary +methods: + +```rust +.env(provider) +.no_env() +``` + +The same pattern applies to all other slots: + +```rust +.profile(provider) +.no_profile() + +.sso(provider) +.no_sso() + +.imds(provider) +.no_imds() +``` + +### Rules + +- Public builder APIs must not expose `configure_*`. +- Public builder APIs must not expose `disable_*(bool)`. +- Each slot is controlled by one positive method and one removal method. +- Slot names must match the actual provider concept, not an overloaded alias. +- Builders expose only the slots that a service actually supports. + +## Internal State Model + +Each builder slot should be represented as: + +```rust +Option<T> +``` + +with this meaning: + +- `Some(T::default())`: slot enabled with the default provider configuration +- `Some(custom)`: slot enabled with a custom provider configuration +- `None`: slot removed from the chain + +This implies: + +- `DefaultCredentialProviderBuilder::default()` initializes all documented + default slots to `Some(T::default())`. +- `.no_env()` sets `env` to `None`. +- `.env(provider)` sets `env` to `Some(provider)`. +- `build()` pushes only `Some(...)` slots and skips `None`. + +No separate `enabled` flag is needed. + +## Slot Naming + +Use provider-concept names without the `CredentialProvider` suffix. + +Examples: + +- `env` +- `profile` +- `sso` +- `process` +- `ecs` +- `imds` +- `web_identity` +- `oidc` +- `config_file` +- `vm_metadata` + +Do not collapse distinct concepts under a vague shared name. + +Examples: + +- Prefer `web_identity(...)` over `assume_role(...)` when the slot is actually + `AssumeRoleWithWebIdentityCredentialProvider`. +- Prefer `oidc(...)` over `assume_role(...)` when the slot is specifically + OIDC-based. + +## Service Examples + +### AWS V4 + +```rust +let provider = DefaultCredentialProvider::builder() + .no_env() + .profile(ProfileCredentialProvider::new().with_profile("prod")) + .no_imds() + .build(); +``` + +Expected slots: + +- `env` +- `profile` +- `sso` +- `web_identity` +- `process` +- `ecs` +- `imds` + +### Aliyun OSS + +```rust +let provider = DefaultCredentialProvider::builder() + .env(EnvCredentialProvider::new()) + .no_oidc() + .build(); +``` + +Expected slots: + +- `env` +- `oidc` + +### Tencent COS + +```rust +let provider = DefaultCredentialProvider::builder() + .no_env() + .web_identity(AssumeRoleWithWebIdentityCredentialProvider::new()) + .build(); +``` + +Expected slots: + +- `env` +- `web_identity` + +## Migration Rules + +When changing an existing service: + +1. Remove all public `configure_*` methods. +2. Remove all public `disable_*(bool)` methods. +3. Initialize builder defaults with `Some(T::default())` for documented default + slots. +4. Add `slot(provider)` and `no_slot()` methods for every supported slot. +5. Update tests to verify that `no_slot()` truly removes the provider from the + chain. +6. Update examples and docs to use slot/no-slot APIs only. + +## Non-Goals + +- This document does not require a repository-wide refactor in one patch. +- This document does not require every service to expose the same set of + provider slots. +- This document does not remove `with_chain` or `push_front`; those remain the + escape hatch for advanced composition.
