# ksp-store-postgres-lib `ksp-store-postgres-lib` est le backend PostgreSQL physique officiel du Store KSP. La crate implémente connexion, pool, TLS, migrations, health et persistence RAW 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 et le moteur de migrations privé KSP ; - la table metadata `ksp_store_schema_migrations` ; - l'advisory transaction lock borné des migrations ; - les schémas physiques et statements privés `RawTransaction` et `RawAccountState` ; - 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/intégrations 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 et vérification de l'identité serveur. Une configuration ne permettant pas de vérifier cette identité, comme `hostaddr` seul, est rejetée. ## Migrations et schéma Le moteur de migrations embarqué vérifie version logique, 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. Le bootstrap metadata est conservé comme migration V000. La migration logique V001 matérialise le schéma `RawTransaction` et V002 le schéma `RawAccountState`, chacune en ressources séparées `tables/`, `constraints/` et `indexes/` afin que le backend puisse vérifier leur compatibilité effective sans transformer les fichiers SQL en parser généraliste. La base est liée à un seul `RawNetworkId` via `ksp_store_identity`. Une migration enregistrée mais physiquement divergente est un mismatch ; les réparations additives sûres dépendent de `schema_autoupdate`. ## 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 Le major minimal supporté est PostgreSQL 15. Le backend ne fixe aucun plafond arbitraire de major ; la compatibilité opérationnelle reste fondée sur le contrat de schéma KSP et l'introspection du catalogue. ## Lectures RAW transaction Le backend expose : ```text get_raw_transaction get_raw_transaction_observation get_raw_transaction_retention_state get_raw_transaction_tombstone ``` Le SQL et les rows restent privés. Le mapping PostgreSQL est fallible et couvre notamment `NUMERIC(20,0) -> u64`, `BIGINT -> u32/u64`, timestamps bornés, bytes de taille fixe et codes de provenance. `Full` lit le payload chaud, `Archived` le reconstruit depuis la relation archive et `Purged` retourne `None`; le tombstone reste accessible séparément. ## Écritures RAW transaction Le backend expose : ```text persist_raw_transaction_acquisition record_raw_transaction_observation ``` L'acquisition canonique et son observation initiale sont commises dans une seule transaction PostgreSQL. Les clés uniques physiques fournissent l'admission idempotente ; après un conflit unique, le backend verrouille la ligne gagnante et compare le contenu réel avant de conclure `AlreadyPresent` ou `Conflict`. Un tombstone `Purged` compatible produit `SkippedPurged/NotRecorded` en mode normal. `ForceRehydrate` restaure explicitement le payload `Full` et l'observation dans la même transaction. ## Pagination RAW transaction `list_raw_transactions` parcourt les références canoniques récupérables avec un ordre total `(slot, signature)`. Les tombstones `Purged` sont exclus. La continuation est une keyset stricte, jamais un `OFFSET`. Le cursor backend V1 est opaque et lié au réseau, à la direction et aux bornes de slots de la query. Store n'impose aucun plafond métier arbitraire à la taille de page ; seule la limitation physique du `LIMIT + 1` PostgreSQL est exposée. ## Rétention RAW transaction `transition_raw_transaction_retention` applique les transitions physiques : ```text Full -> Archived -> Purged ``` Le backend verrouille la ligne canonique avec `FOR UPDATE`, compare l'état courant à l'état attendu et applique la mutation atomiquement. L'archivage conserve le payload exact dans la relation archive ; la purge conserve seulement le tombstone minimal. Toute transition impliquant `Compacted` est rejetée avec `RetentionCompactionUnsupported` tant qu'aucune représentation compactée réelle n'est implémentée. ## Lectures RAW account Le backend expose : ```text get_raw_account_state get_raw_account_observation ``` `get_raw_account_state` reconstruit l'état complet à partir de `(pubkey, slot, state_hash)` sans narrowing du domaine `u64`. Les bytes `pubkey`, `owner` et `state_hash` sont revalidés à leur largeur exacte et `data` reste un `BYTEA` complet, vide autorisé, borné par le contrat Store API. `get_raw_account_observation` reconstruit la provenance commune et les métadonnées account optionnelles, notamment `is_startup`, `transaction_signature` et `write_version`. La signature est une metadata fixed-width et ne crée aucune FK vers la famille transaction. ## Écritures RAW account Le backend expose : ```text persist_raw_account_acquisition record_raw_account_observation ``` L'acquisition état+observation est transactionnelle. Les inserts utilisent `ON CONFLICT ... DO NOTHING`, puis verrouillent et comparent le contenu gagnant avant de conclure `AlreadyPresent` ou `Conflict`; aucun `DO UPDATE` n'est utilisé. Une collision divergente d'observation fait échouer toute l'acquisition et rollback un éventuel nouvel état. L'ajout d'une observation vérifie que l'état référencé existe déjà et ne crée jamais implicitement cet état. ## Pagination RAW account `list_raw_account_states` parcourt les références selon l'ordre total `(slot, pubkey, state_hash)`, en ASC ou DESC, avec filtre pubkey optionnel. La continuation est keyset, sans `OFFSET`. Le cursor `KSPA` est opaque et lié au réseau, au filtre pubkey, à la direction, aux bornes de slots et à la dernière clé complète. Il est distinct du cursor transaction `KSPT`. ## Hors périmètre La crate ne contient : - aucune rétention, archive, purge, suppression ou compaction account ; - aucune orchestration worker/job ; - aucun transport d'acquisition ou decoder Program ; - aucune policy autonome de batch, priorité ou rétention. ## Documentation - [`USAGE.md`](USAGE.md) — guide pratique du bridge physique et de ses capabilities ; - [`../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) — validation de fondation ; - [`../../docs/plans/024-V0_3_3_STORE_POSTGRES_RAW_TRANSACTION_PLAN.md`](../../docs/plans/024-V0_3_3_STORE_POSTGRES_RAW_TRANSACTION_PLAN.md) — design `RawTransaction` ; - [`../../docs/validation/020-V0_3_3_STORE_POSTGRES_RAW_TRANSACTION.md`](../../docs/validation/020-V0_3_3_STORE_POSTGRES_RAW_TRANSACTION.md) — validation `RawTransaction` ; - [`../../docs/plans/025-V0_3_4_STORE_POSTGRES_RAW_ACCOUNT_PLAN.md`](../../docs/plans/025-V0_3_4_STORE_POSTGRES_RAW_ACCOUNT_PLAN.md) — design `RawAccountState` et complétude RAW ; - [`../../docs/validation/021-V0_3_4_STORE_POSTGRES_RAW_ACCOUNT.md`](../../docs/validation/021-V0_3_4_STORE_POSTGRES_RAW_ACCOUNT.md) — validation `RawAccountState` et conformance RAW.