v0.3.2-pre.011

This commit is contained in:
2026-08-30 05:55:29 +02:00
parent e1fe419028
commit b375f263dc
13 changed files with 1011 additions and 36 deletions

View File

@@ -0,0 +1,124 @@
<!-- file: crates/ksp-store-postgres-lib/README.md -->
<!-- version: 1 -->
# ksp-store-postgres-lib
`ksp-store-postgres-lib` est le backend PostgreSQL physique officiel du Store KSP.
La crate implémente la fondation connexion/pool/TLS/migrations/health derrière `ksp-store-lib`. Elle dépend directement de `ksp-store-api` mais ne dépend jamais de la façade `ksp-store-lib`.
## Responsabilités
La crate possède seule pour PostgreSQL :
- le parsing et la normalisation de la configuration physique `tokio-postgres` ;
- le pool borné `deadpool-postgres` ;
- la policy TLS physique avec Rustls ;
- les roots système et le provider cryptographique AWS-LC ;
- le bootstrap/moteur de migrations privé KSP ;
- la table metadata `ksp_store_schema_migrations` ;
- le sentinel `V000__bootstrap.sql` et son checksum SHA-256 ;
- l'advisory transaction lock borné des migrations ;
- les snapshots runtime/health sûrs destinés au bridge de façade ;
- la fermeture explicite du pool et son fallback `Drop` best-effort ;
- la classification d'erreurs backend sans conserver le texte d'erreur PostgreSQL.
## Frontière d'utilisation
Les applications, jobs et workers KSP ne dépendent normalement pas de cette crate :
```text
consumer -> ksp-store-lib -> [feature postgres] ksp-store-postgres-lib
```
La surface publique de cette crate existe pour le bridge inter-crates et les tests d'intégration backend. Elle ne constitue pas une seconde façade Store.
`ksp-store-postgres-lib` ne réexporte pas `tokio-postgres`, Deadpool ou Rustls.
## Connexion et pool
`PostgresBackend::open` :
1. valide et normalise l'URI fournie explicitement ;
2. impose la policy TLS typée ;
3. construit un pool borné ;
4. prouve une connexion physique ;
5. vérifie/applique le bootstrap selon les settings ;
6. ne retourne qu'après succès de cette fondation.
Le backend ne lit aucun environnement, `.env`, `PG*`, `.pgpass` ou fichier TLS implicite libpq.
## TLS
Les modes sont exactement :
```text
Disabled
VerifyFull
```
`VerifyFull` exige :
- TLS ;
- roots système ;
- certificat valide ;
- vérification de l'identité serveur ;
- aucune dégradation automatique en plaintext.
Les configurations ne permettant pas de vérifier une identité serveur, comme `hostaddr` seul, sont rejetées.
## Migrations
La fondation embarque uniquement :
```text
migrations/V000__bootstrap.sql
```
Elle crée la metadata privée :
```text
ksp_store_schema_migrations
```
Le moteur vérifie version, nom et checksum SHA-256, sérialise les runners par advisory transaction lock et refuse une history divergente ou plus récente que le runtime.
Aucune migration métier RAW n'appartient à cette fondation.
## Health et erreurs
`PostgresBackendRuntimeSnapshot` et `PostgresBackendHealthSnapshot` ne contiennent que des compteurs et états sûrs destinés à la façade.
`PostgresBackendError` ne conserve que :
```text
PostgresBackendErrorKind
phase statique
```
Le texte d'erreur PostgreSQL, l'URI, SQL et les valeurs bind ne traversent pas cette frontière.
## Support PostgreSQL
La politique de support de `0.3.2` fixe PostgreSQL 15 comme major minimal. Le test live de fondation refuse explicitement un serveur plus ancien ; le backend ne fixe aucun plafond arbitraire de major PostgreSQL. La compatibilité de migration reste basée sur le schéma KSP.
La preuve opérateur réelle et le major effectivement exercé sont conservés dans la matrice de validation, pas dans cette documentation durable.
## Hors périmètre actuel
La crate ne contient encore :
- aucune implémentation PostgreSQL des capabilities `RawTransaction*` ;
- aucune implémentation PostgreSQL des capabilities `RawAccount*` ;
- aucun repository métier RAW ;
- aucune table/index métier ;
- aucune orchestration worker/job ;
- aucun transport d'acquisition ou decoder Program.
## Documentation
- [`USAGE.md`](USAGE.md) — bridge physique et lifecycle ;
- [`../ksp-store-lib/README.md`](../ksp-store-lib/README.md) — façade runtime destinée aux consumers ;
- [`../../docs/architecture/008-DATA_MATERIALIZATION_AND_STORE.md`](../../docs/architecture/008-DATA_MATERIALIZATION_AND_STORE.md) — architecture Store ;
- [`../../docs/plans/023-V0_3_2_STORE_POSTGRES_FOUNDATION_PLAN.md`](../../docs/plans/023-V0_3_2_STORE_POSTGRES_FOUNDATION_PLAN.md) — décisions pool/TLS/migrations ;
- [`../../docs/validation/019-V0_3_2_STORE_POSTGRES_FOUNDATION.md`](../../docs/validation/019-V0_3_2_STORE_POSTGRES_FOUNDATION.md) — preuves déterministes et PostgreSQL réel.

View File

@@ -0,0 +1,155 @@
<!-- file: crates/ksp-store-postgres-lib/USAGE.md -->
<!-- version: 1 -->
# Utilisation de ksp-store-postgres-lib
## 1. Quand utiliser cette crate directement
Le consumer applicatif normal utilise `ksp-store-lib`.
Une dépendance directe à `ksp-store-postgres-lib` est réservée aux composants qui implémentent ou testent le bridge physique PostgreSQL. La crate backend ne doit pas devenir une façade parallèle.
Un tel composant doit déclarer explicitement le backend et `ksp-store-api`, car `PostgresBackendSettings::new` reçoit le `RawNetworkId` backend-neutral sans le réexporter :
```toml
[dependencies]
ksp-store-api = { path = "../ksp-store-api" }
ksp-store-postgres-lib = { path = "../ksp-store-postgres-lib" }
```
## 2. Construire le bridge physique
`PostgresBackendSettings` reçoit des valeurs déjà possédées et validées par la couche appelante. L'URI est sensible et son `Debug` est redacted.
```rust
fn backend_settings(
network: ksp_store_api::RawNetworkId,
connection_uri: std::string::String,
) -> ksp_store_postgres_lib::PostgresBackendSettings {
return ksp_store_postgres_lib::PostgresBackendSettings::new(
network,
connection_uri,
8,
std::time::Duration::from_secs(10),
std::time::Duration::from_secs(5),
std::time::Duration::from_secs(10),
std::time::Duration::from_secs(5),
ksp_store_postgres_lib::PostgresBackendTlsMode::VerifyFull,
true,
std::time::Duration::from_secs(30),
std::time::Duration::from_secs(10),
);
}
```
Le backend reçoit un seul `RawNetworkId`. Une instance physique n'est pas un routeur multi-réseau.
## 3. Ouvrir, sonder et fermer
```rust
async fn use_backend(
settings: ksp_store_postgres_lib::PostgresBackendSettings,
) -> std::result::Result<(), ksp_store_postgres_lib::PostgresBackendError> {
let backend = ksp_store_postgres_lib::PostgresBackend::open(settings).await;
let backend = match backend {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let runtime = backend.runtime_snapshot();
let _capacity = runtime.pool_capacity();
let _size = runtime.pool_size();
let _available = runtime.pool_available();
let _waiting = runtime.pool_waiting();
let health = backend.health().await;
let _ready = health.ready();
let _migration_version = health.migration_version();
let _pending = health.pending_migration_count();
let _safe_error_kind = health.last_error_kind();
return backend.close(std::time::Duration::from_secs(5)).await;
}
```
`open` prouve la connexion et le bootstrap avant de retourner. `close` ferme le pool puis attend son drain dans la deadline fournie.
## 4. Choisir le mode TLS
### `VerifyFull`
À utiliser pour les connexions PostgreSQL protégées :
```rust
ksp_store_postgres_lib::PostgresBackendTlsMode::VerifyFull
```
Le backend charge les roots système et vérifie certificat + identité serveur. Il rejette une configuration ne fournissant pas d'identité vérifiable.
### `Disabled`
```rust
ksp_store_postgres_lib::PostgresBackendTlsMode::Disabled
```
Ce mode désactive explicitement TLS. Il ne doit être utilisé que lorsque la topologie de déploiement justifie clairement une connexion non chiffrée.
La valeur typée choisie par KSP prime sur les paramètres SSL de l'URI.
## 5. Bootstrap et migrations
Le backend embarque son propre moteur de migrations. Le seul artefact initial est :
```text
migrations/V000__bootstrap.sql
```
Le bootstrap maintient :
```text
ksp_store_schema_migrations
version
name
checksum SHA-256
```
Le runner est transactionnel et sérialisé par advisory transaction lock. Une divergence de checksum/nom/version ou une history plus récente est terminale ; aucun down migration automatique n'est exécuté.
`auto_migrate = false` permet de vérifier l'état sans appliquer de migration pending.
## 6. Classifier les erreurs sans fuite
```rust
match error.kind() {
ksp_store_postgres_lib::PostgresBackendErrorKind::ConfigInvalid => {}
ksp_store_postgres_lib::PostgresBackendErrorKind::ConnectFailed => {}
ksp_store_postgres_lib::PostgresBackendErrorKind::PoolTimeout => {}
ksp_store_postgres_lib::PostgresBackendErrorKind::HealthFailed => {}
ksp_store_postgres_lib::PostgresBackendErrorKind::MigrationFailed => {}
ksp_store_postgres_lib::PostgresBackendErrorKind::MigrationMismatch => {}
ksp_store_postgres_lib::PostgresBackendErrorKind::SchemaNewer => {}
ksp_store_postgres_lib::PostgresBackendErrorKind::ShutdownTimeout => {}
ksp_store_postgres_lib::PostgresBackendErrorKind::TlsFailed => {}
_ => {}
}
let _safe_phase = error.phase();
```
Ne pas reconstruire un diagnostic utilisateur à partir de l'erreur brute PostgreSQL : cette erreur n'est volontairement pas conservée par le bridge.
## 7. Ce que cette crate ne permet pas encore
La fondation physique n'implémente pas les traits `RawTransaction*` ou `RawAccount*` de `ksp-store-api`.
Un backend ouvert et healthy prouve uniquement :
```text
connexion/pool
TLS selon policy
bootstrap/history
health/readiness
close borné
```
Il ne prouve aucune persistence métier RAW.