-
Notifications
You must be signed in to change notification settings - Fork 69
Description
Proposal: Explicit Capability Declaration & Enforcement for Semantics-Gated Features
Background / Motivation
As s3s evolves, it may introduce new request parameters or other behavior changes that are optional at the protocol level but can still change the semantics of an operation when used.
Downstream implementers of the S3 trait (custom backends, adapters, proxies) may lag behind and unintentionally ignore newly introduced semantics. This can lead to silent behavior changes and subtle bugs.
To prevent this class of bugs, we propose an explicit capability mechanism: an S3 implementation explicitly declares which semantics/features it supports, and s3s explicitly enforces those declarations when a request uses a gated feature.
Goals
- Provide an explicit, opt-in contract for semantics-changing features.
- Allow downstream
S3implementations to explicitly declare supported capabilities. - Ensure
s3sexplicitly rejects requests that rely on capabilities not declared by the backend (fail fast rather than silently ignore). - Preserve backward compatibility for requests that do not rely on new/gated semantics.
- Keep the mechanism extensible: adding a new gated semantic feature should be a small, reviewable change.
Non-Goals
- This is not a “reject all unknown parameters” policy.
- This issue introduces the capability framework; it does not need to immediately gate every existing field/feature.
Proposed Design
1) Define explicit capabilities
Introduce a capability type in s3s, either:
enum Capability { ... }with a set (HashSet/BTreeSet), orbitflags! Capabilities
Capabilities should represent semantic features (often grouped), not every individual header/query key, to keep the surface stable and meaningful.
Examples (illustrative only):
Capability::ChecksumSemanticsCapability::SseKmsContextCapability::AdvancedConditionalRequestsCapability::SomeNewListBehavior
2) Backends explicitly declare capabilities
Prefer an extension trait to avoid breaking existing implementations:
trait S3Capabilities {
fn capabilities(&self) -> Capabilities;
}
Default behavior:
- If a backend does not implement
S3Capabilities, it is treated as explicitly declaringCapabilities::empty()(i.e., supports only the baseline semantics).
3) Explicit requirement mapping (feature → capability)
When s3s adds a semantics-changing feature (often introduced via optional request fields), it must also add an explicit mapping describing when that feature is “in use” and which capability it requires.
Two incremental approaches:
A. Input-driven requirements (low churn):
Compute requirements from parsed input:
fn required_capabilities(input: &OpInput) -> Capabilities
Only require a capability when the feature is actually used (field present / effective).
B. Presence-driven requirements (more precise):
If needed, requirements can be accumulated during parsing/deserialization based on explicit presence of headers/query params, even if equal to default values.
Start with A; use B only when there is a demonstrated need.
4) Explicit enforcement before calling downstream
Add a central enforcement point before invoking the downstream S3 method:
- Determine
requiredcapabilities for the request/operation. - Obtain
supportedcapabilities explicitly declared by the backend. - If
requiredis not a subset ofsupported, reject early with a clear error and log.
Error code can be NotImplemented or InvalidRequest (TBD). The error message should identify missing capabilities, e.g.:
Backend does not declare support for capability: ChecksumSemantics
This makes incompatibility obvious, actionable, and observable.
Backward Compatibility
- Existing backends remain source-compatible if this is done via an extension trait.
- Requests that do not use gated semantics continue to behave as before.
- Requests that use gated semantics fail fast on backends that do not explicitly declare support, preventing silent semantic drift.
Implementation Plan (Suggested)
- Introduce capability types and the
S3Capabilitiesextension trait (defaultempty). - Add a shared helper to check capabilities and produce standardized errors/logs.
- Pilot-gate one semantics feature to validate:
- requirement detection
- enforcement behavior
- error reporting
- Incrementally add explicit capability requirements for other semantics-sensitive features.
Testing Strategy
For each gated feature:
- Backend declares no capability + request uses feature => rejected.
- Backend declares no capability + request does not use feature => accepted.
- Backend declares capability + request uses feature => accepted.
For proxy/adapters:
- If capability is declared, ensure the adapter actually implements/mappings the semantics (avoid “declared supported but dropped”).
Open Questions
- Capability representation:
enumvsbitflags? - Default baseline: should any built-in backends declare a non-empty default set?
- Error code and message format for missing capabilities.
- Best location for requirement computation for generated operations (handwritten vs codegen).
Relationship to #250
Issue #250 is a concrete example of the broader problem: optional new parameters can change semantics and be silently ignored downstream. This proposal introduces an explicit capability declaration and enforcement mechanism so that new semantics are only enabled when the downstream backend explicitly opts in.