v0.2.11-pre.003

This commit is contained in:
2026-08-25 21:00:19 +02:00
parent 2dbddfe367
commit be2a06bc80
18 changed files with 1415 additions and 45 deletions

300
deltas/0.2.11/pre.003.md Normal file
View File

@@ -0,0 +1,300 @@
<!-- file: deltas/0.2.11/pre.003.md -->
<!-- version: 1 -->
# Delta `0.2.11-pre.003` — HTTP REST commun et rate limiting
## 1. Base requise
Cette tranche s'applique exclusivement après :
```text
v0.2.10
+ 0.2.11-pre.001
+ 0.2.11-pre.001-fix.001
+ 0.2.11-pre.002
+ 0.2.11-pre.002-fix.001
+ 0.2.11-pre.002-fix.002
+ 0.2.11-pre.002-fix.003
```
La version Cargo attendue à l'entrée est :
```text
0.2.11-pre.2.fix.3
```
La version Cargo de sortie est :
```text
0.2.11-pre.3
```
## 2. Gate d'entrée acquis
L'opérateur a validé `0.2.11-pre.002-fix.003` le `2026-08-25` avec :
```text
cargo fmt --all exécuté
python3 scripts/audit_rust_workspace_rules.py clean
python3 scripts/audit_markdown_tables.py clean, 116 tables / 105 files
cargo check --workspace PASS
cargo clippy --workspace --all-targets PASS
cargo test -p ksp-offchain-transport-lib PASS, 10 unit + 1 boundary + 2 public API
```
`cargo test --workspace` n'a pas été fourni pour cet état exact et n'est pas déclaré PASS ici.
## 3. Objectif
Matérialiser les primitives HTTP REST communes de `ksp-offchain-transport-lib` et le rate limiting local sans avancer aucun provider.
La tranche doit préparer directement `pre.004+` tout en conservant les frontières :
```text
http_* = mécanique transport crate-wide et crate-private
market_price_* = première capacité métier, SOL/USD uniquement
aucun client HTTP générique exporté aux consumers
aucun SDK provider
aucun Config -> Off-chain ajouté avant pre.009
aucun refresh registry/service avant pre.007/pre.008
```
## 4. HTTP REST commun
`http_client.rs` matérialise un `reqwest::Client` réutilisable par les adapters internes.
La construction impose explicitement :
```text
rustls
redirects désactivés
Referer automatique désactivé
proxy système désactivé
retries implicites reqwest désactivés
User-Agent KSP explicite
connect timeout borné
request timeout borné
```
`reqwest` reste une dépendance directe de la crate et aucun SDK provider n'est ajouté.
La requête GET interne :
```text
accepte uniquement HTTPS pour les adapters runtime
refuse credentials dans l'URL
encode les query pairs via Url
permet des headers sensibles marqués sensitive
redacte URL et headers dans Debug
```
Un constructeur HTTP non HTTPS existe uniquement sous `cfg(test)` pour les serveurs loopback déterministes.
## 5. Bornes de réponse et JSON
Les valeurs initiales sont :
```text
connect timeout défaut = 5 s
connect timeout hard max = 30 s
request timeout défaut = 10 s
request timeout hard max = 120 s
body défaut = 1 MiB
body hard max = 4 MiB
```
Le body est borné pendant sa lecture par chunks. La présence ou l'absence de `Content-Length` ne permet donc pas de contourner la limite.
Après lecture, la syntaxe JSON est validée avec `serde::de::IgnoredAny`. Le document brut borné reste disponible pour le futur adapter typed, ce qui évite de faire passer un nombre provider par une représentation générique `f64` avant le parsing `MarketPriceDecimal`.
## 6. Classification HTTP
Les codes crate-wide ajoutés sous le domaine `offchain_transport` distinguent :
```text
http_settings_invalid
http_client_build_failed
http_request_invalid
http_connection_failed
http_timeout
http_request_failed
http_access_denied
http_rate_limited
http_temporary_failure
http_response_too_large
http_invalid_json
http_rate_limit_invalid
```
La classification de statut est :
```text
401/403 -> access denied
429 -> rate limited
408 et 5xx -> temporary failure
autre non-2xx -> request failed
2xx -> body borné puis JSON syntaxiquement validé
```
Les erreurs `reqwest` sont converties après `without_url()`. Aucun body remote n'est recopié dans `KspError`.
`Retry-After` est actuellement exploité sous sa forme `delta-seconds`, bornée défensivement à une heure. Une forme HTTP-date non parseable reste ignorée plutôt que devinée.
## 7. Admission et cooldown
`http_admission.rs` fournit un limiter non bloquant.
Les policies internes sont :
```text
Fixed
Dynamic
Unlimited
```
Une policy fixe utilise un token bucket lissé :
```text
requests/window
burst explicite uniquement lorsqu'il est documenté
burst absent -> capacité locale conservatrice de 1
```
Une policy dynamique n'invente aucune cadence locale. Elle applique seulement les cooldowns appris du provider.
`try_admit()` ne dort jamais : il retourne immédiatement `Ready` ou `Deferred(duration)`. Cette propriété prépare le `refresh multiple/all` de `pre.008`, qui devra continuer avec les autres providers au lieu d'attendre un provider en cooldown.
Un `429` peut étendre le cooldown avec `Retry-After`; la durée provider est bornée à une heure et comparée au fallback local.
## 8. Tests ajoutés
Les nouveaux tests unitaires couvrent :
```text
settings HTTP par défaut et bornes pathologiques
policy fixe invalide
burst absent lissé conservativement
burst documenté consommé atomiquement
refill déterministe du token bucket
cooldown provider et borne Retry-After
Debug request sans URL/credential
GET JSON valide
redirect refusé
body chunked dépassant la limite
429 + Retry-After sans propagation du body canari
JSON invalide après statut 2xx
```
Les canaries d'intégration vérifient aussi :
```text
reqwest rustls présent
Config toujours absent
SDK providers toujours absents
tracing direct toujours absent
modules http_* présents
HttpRestClient non exporté publiquement
redirect/no-proxy/no-retry/without_url explicites dans le client
codes d'erreur HTTP dans le domaine offchain_transport
```
## 9. Fichiers ajoutés
```text
crates/ksp-offchain-transport-lib/src/http_admission.rs
crates/ksp-offchain-transport-lib/src/http_client.rs
crates/ksp-offchain-transport-lib/src/http_settings.rs
crates/ksp-offchain-transport-lib/unit_tests/http_admission.rs
crates/ksp-offchain-transport-lib/unit_tests/http_client.rs
crates/ksp-offchain-transport-lib/unit_tests/http_settings.rs
deltas/0.2.11/pre.003.md
```
## 10. Fichiers modifiés
```text
Cargo.toml
crates/ksp-offchain-transport-lib/Cargo.toml
crates/ksp-offchain-transport-lib/src/error.rs
crates/ksp-offchain-transport-lib/src/lib.rs
crates/ksp-offchain-transport-lib/tests/dependency_boundary.rs
crates/ksp-offchain-transport-lib/tests/public_api.rs
docs/plans/018-V0_2_11_OFFCHAIN_PRICE_TRANSPORT_PLAN.md
docs/validation/014-V0_2_11_OFFCHAIN_PRICE_TRANSPORT.md
```
## 11. Fichiers supprimés
```text
aucun
```
## 12. Fichiers volontairement inchangés
```text
CHANGELOG.md
ROADMAP.md
README.md
.env.example
config/**
crates/ksp-config-lib/**
crates/ksp-onchain-transport-lib/**
prompts/**
```
`ROADMAP.md` et `CHANGELOG.md` restent hors de la tranche conformément à leur ownership de release.
## 13. Validations exécutées dans le sandbox
```text
python3 scripts/audit_rust_workspace_rules.py
General Rust rule audit: clean
Rust export completeness audit: 0 candidate(s)
KSP workspace Rust rule audit: clean
python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas/0.2.11
clean
contrôle lignes Rust > 160 sur ksp-offchain-transport-lib
PASS
inspection diff contre 0.2.11-pre.002-fix.003
aucun provider adapter ajouté
aucune dépendance ksp-config-lib ajoutée
aucune dépendance tracing directe ajoutée
```
## 14. Validations non exécutées dans le sandbox
Le sandbox de génération ne fournit pas `cargo`, `rustc` ou `rustfmt`.
Après application, exécuter :
```bash
cargo fmt --all
python3 scripts/audit_rust_workspace_rules.py
python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas/0.2.11
cargo check --workspace
cargo clippy --workspace --all-targets
cargo test -p ksp-offchain-transport-lib
cargo test --workspace
```
## 15. Décisions et points différés
Aucune question ne bloque `pre.004`.
Restent volontairement différés :
```text
mapping MarketPriceProviderRateLimit -> HttpAdmissionPolicy par provider
CoinGecko/CoinMarketCap/CoinPaprika wire DTOs et endpoints
credentials provider effectifs
registry et availability runtime
refresh single/multiple
Config std.offchain_transport
Retry-After HTTP-date si un provider retenu l'exige réellement
POST/écritures HTTP pour futures familles off-chain
```
`pre.004` reste propriétaire du premier lot d'adapters : CoinGecko, CoinMarketCap et CoinPaprika.