Files
khadhroony-solana-project/docs/plans/023-V0_3_2_STORE_POSTGRES_FOUNDATION_PLAN.md
2026-08-29 16:45:14 +02:00

31 KiB

Plan 0.3.2 — Store/PostgreSQL runtime foundation

1. Objet et base

Cette release part exclusivement de la stable :

v0.3.1
workspace.package.version = 0.3.1

Le gate d'ouverture est :

0.3.2-pre.001

Les deux archives requises par le prompt sont disponibles et ont été auditées :

khadhroony-solana-project-v0.3.1.zip
khadhroony-bot3_v0.5.3-pre.005-fix010.zip

La metadata Git n'est pas incluse dans l'archive KSP ; le tag v0.3.1 ne peut donc pas être interrogé localement. Les preuves internes concordantes sont :

workspace.package.version = 0.3.1
deltas/0.3.1/rel.001.md présent
prompts/021-V0_3_2_START_PROMPT.md présent
crates/ksp-store-api présent
crates/ksp-store-lib absent
crates/ksp-store-postgres-lib absent

ksp-store-api est conservé comme contrat backend-agnostic publié par 0.3.1. 0.3.2 ne doit pas le remodeler pour simplifier PostgreSQL.

2. Scope de la release

0.3.2 introduit ensemble :

ksp-store-lib
ksp-store-postgres-lib
Config std.store
connexion/pool/TLS PostgreSQL
moteur privé de migrations/bootstrap
diagnostics/health runtime minimaux
integration test PostgreSQL réel opt-in

La release reste une fondation runtime/backend. Elle n'implémente aucune persistence métier :

RawTransaction          réservé à 0.3.3
RawAccountState         réservé à 0.3.4
CORE / DECODE / D3 / D4 hors scope

Aucune table, index, repository ou capability RAW n'est créé uniquement pour démontrer que PostgreSQL fonctionne.

3. Divergence normative découverte à l'ouverture

La base stable contient une divergence réelle entre les documents les plus récents et deux règles plus anciennes.

Les documents déjà cohérents avec la trajectoire 0.3.1 sont :

docs/plans/022-V0_3_1_STORE_RAW_PLAN.md
docs/architecture/003-COMPONENT_CONTRACTS.md
docs/architecture/004-COMPONENT_INVENTORY.md
docs/architecture/005-DEPENDENCY_GRAPH.md

Ils imposent :

ksp-store-lib -> ksp-store-api
ksp-store-lib[postgres] -> ksp-store-postgres-lib
ksp-store-postgres-lib -> ksp-store-api
ksp-store-postgres-lib -X-> ksp-store-lib

En revanche, la base v0.3.1 conservait encore :

KSP-API-006
DEP-STORE-002
section ksp-store-lib de docs/architecture/008-DATA_MATERIALIZATION_AND_STORE.md

avec l'ancien modèle où PostgreSQL était physiquement contenu dans ksp-store-lib.

pre.001 réconcilie cette divergence sans changer le scope :

KSP-API-006      façade/runtime Store dans ksp-store-lib ; PostgreSQL dans ksp-store-postgres-lib
DEP-STORE-002    façade commune + backends optionnels par feature
DEP-STORE-009    ownership physique PostgreSQL par ksp-store-postgres-lib
DEP-STORE-010    consumers ordinaires -> ksp-store-lib, jamais backend direct
architecture 008 même séparation durable

Cette réconciliation est un prérequis du scaffold pre.002 ; elle ne constitue pas une implémentation backend.

4. Graphe Cargo cible

4.1 Graphe KSP

Le graphe cible de 0.3.2 est :

ksp-store-lib
├── ksp-store-api
├── ksp-logging-lib
└── [feature postgres]
    └── ksp-store-postgres-lib

ksp-store-postgres-lib
├── ksp-store-api
├── ksp-logging-lib
├── tokio
├── tokio-postgres
├── deadpool-postgres
├── tokio-postgres-rustls
├── rustls
└── sha2                         à partir de la tranche migrations

Interdictions :

ksp-store-postgres-lib -X-> ksp-store-lib
ksp-store-lib -X-> ksp-config-lib
ksp-store-postgres-lib -X-> ksp-config-lib
ksp-store-* -X-> Transport/Program/Materializer
apps/workers/jobs -X-> ksp-store-postgres-lib

ksp-config-lib pourra dépendre de ksp-store-lib pour construire des settings effectifs, comme pour les autres adapters Config -> runtime.

4.2 Features de ksp-store-lib

Contrat retenu :

[features]
default = ["postgres"]
postgres = ["dep:ksp-store-postgres-lib"]

ksp-store-lib --no-default-features reste compilable. Le type public identifiant PostgreSQL existe même lorsque la feature n'est pas compilée afin qu'une configuration connue mais indisponible puisse produire une erreur stable et explicite :

STORE_BACKEND_NOT_COMPILED

Aucune feature de backend ne modifie ksp-store-api.

4.3 Versions externes candidates retenues au gate

Audit au 29 août 2026 :

PostgreSQL stable courant       18.6
tokio-postgres                  0.7.18
deadpool-postgres               0.14.1
tokio-postgres-rustls           0.14.0
rustls                          0.23
sha2                            0.11.0
refinery audité mais rejeté     0.9.2

Les manifests utiliseront les contraintes compatibles avec DEP-CARGO-*, sans pin patch arbitraire. Les versions réellement résolues seront enregistrées par les graphes de la tranche qui les ajoute.

Licences auditées :

tokio-postgres                  MIT OR Apache-2.0
deadpool-postgres               MIT OR Apache-2.0
tokio-postgres-rustls           MIT
sha2                            MIT OR Apache-2.0
refinery                        MIT

Sources primaires/techniques consultées :

https://www.postgresql.org/support/versioning/
https://www.postgresql.org/docs/current/libpq-ssl.html
https://www.postgresql.org/docs/release/18.6/
https://docs.rs/tokio-postgres/0.7.18/tokio_postgres/
https://github.com/rust-postgres/rust-postgres/blob/master/tokio-postgres/Cargo.toml
https://docs.rs/deadpool-postgres/0.14.1/deadpool_postgres/
https://docs.rs/tokio-postgres-rustls/0.14.0/tokio_postgres_rustls/
https://docs.rs/sha2/0.11.0/sha2/
https://docs.rs/refinery/0.9.2/refinery/

5. PostgreSQL support policy

5.1 Major minimal

KSP 0.3.2 retient :

major PostgreSQL minimal supporté : 15
major testé prioritairement       : 18
patch de référence au gate        : 18.6

PostgreSQL 14 est encore supporté upstream au moment du gate mais atteint son EOL le 12 novembre 2026. L'ajouter à une nouvelle fondation en août 2026 créerait une dette immédiate sans bénéfice démontré.

Aucun plafond runtime strict n'est fixé à 18 : un serveur plus récent n'est pas rejeté uniquement à cause de son major. Les garanties de release portent sur le minimum 15 et le major courant réellement testé.

5.2 Driver

Décision acquise confirmée :

tokio-postgres

Raisons :

driver async natif ciblé
API de transaction explicite
support TLS via MakeTlsConnect
connection future explicite
pas d'ORM ni macro SQL imposée
compatible avec l'ownership backend séparé

La future implémentation ne doit jamais appeler un chemin qui lit implicitement PG*, .pgpass ou l'environnement KSP. La configuration tokio_postgres::Config est construite uniquement depuis des settings reçus de la façade.

6. Pooling

6.1 Décision

Le pool externe retenu est :

deadpool-postgres 0.14.x

Le choix est fondé sur l'ownership/lifecycle et non sur la popularité.

L'audit source montre que le manager :

crée Client + connection future tokio-postgres
spawn la connection future
conserve le JoinHandle dans ClientWrapper
abort le JoinHandle au Drop du wrapper

Le pool possède aussi un close() et des timeouts wait/create/recycle.

6.2 Alternative rejetée

bb8-postgres a été audité comme candidat maintenu, mais son manager spawn la connection future lors de connect sans conserver le JoinHandle dans l'objet retourné. Cette propriété est moins adaptée au critère KSP :

aucune connection task orpheline
shutdown borné et observable
ownership explicite du lifecycle

Construire un pool KSP complet est également rejeté pour 0.3.2 : cela réimplémenterait admission, waiters, recycle et fermeture sans besoin métier démontré.

6.3 Bornes runtime

Settings KSP candidats, à matérialiser exactement en pre.003/pre.005 :

max_connections          défaut 8       plage 1..64
connect_timeout_ms       défaut 10_000  plage 100..60_000
pool_wait_timeout_ms     défaut 5_000   plage 100..60_000
pool_create_timeout_ms   défaut 10_000  plage 100..60_000
pool_recycle_timeout_ms  défaut 5_000   plage 100..60_000
shutdown_timeout_ms      défaut 5_000   plage 100..30_000

Ces bornes sont des garde-fous de ressources/lifecycle, pas des policies de workers/jobs.

Aucun pool, client, statement cache ou type deadpool/tokio-postgres n'est exposé par ksp-store-lib.

7. TLS PostgreSQL

7.1 Connecteur retenu

Décision :

tokio-postgres-rustls 0.14.x
rustls 0.23
crypto provider aws-lc-rs
root store système via native-certs

Ce choix reste cohérent avec la stack Rustls déjà présente dans le workspace et évite d'ajouter OpenSSL/native-tls comme dépendance fonctionnelle Store.

7.2 Modes publics

La façade KSP n'expose initialement que :

Disabled
VerifyFull

VerifyFull signifie :

TLS obligatoire
chaîne certificat vérifiée contre les roots retenus
nom serveur vérifié par rustls
aucun fallback plaintext

Les modes Prefer et Require sans vérification de certificat ne sont pas exposés dans 0.3.2. Le type tokio_postgres::config::SslMode ne suffit donc pas à exprimer la policy publique KSP ; il est un détail backend et sera forcé conformément au mode KSP.

Custom CA, certificat client/mTLS et pinning sont reportés jusqu'à un besoin concret. Le backend ne lit jamais implicitement sslrootcert, sslcert, sslkey ou des fichiers libpq.

8. Store settings et backend selection

8.1 Ownership

StoreSettings appartient à ksp-store-lib et ne dépend pas de Config.

La forme retenue est typée, sans serde_json::Value opaque :

StoreSettings
└── StoreBackendSettings
    └── Postgres(PostgresStoreSettings)

PostgresStoreSettings est un type KSP-owned de la façade, pas un type de ksp-store-postgres-lib. Il contient uniquement des primitives/settings KSP et reste disponible sans feature postgres.

Lorsque la feature est active, ksp-store-lib convertit en privé ces settings vers un PostgresBackendSettings possédé par ksp-store-postgres-lib. Ce dernier n'est pas réexporté vers les consumers.

8.2 Surface candidate

Les groupes de settings sont :

connection_uri          String possédée, Debug redacted, aucun getter de diagnostic
pool                    bornes de section 6
tls                     Disabled ou VerifyFull
bootstrap               auto_migrate + migration_timeout_ms + lock_timeout_ms
shutdown_timeout_ms     lifecycle commun

Bornes bootstrap :

auto_migrate            défaut true
migration_timeout_ms    défaut 30_000  plage 1_000..300_000
migration_lock_ms       défaut 10_000  plage 100..120_000

Il n'existe pas de Default qui invente une URI de production. Config et les callers programmatiques doivent construire explicitement le backend settings.

8.3 URI et secrets

La connexion URI est considérée sensible intégralement, même lorsqu'elle ne contient pas de mot de passe visible.

Interdictions :

Debug de l'URI
URI dans ErrorContext
URI dans tracing
URI dans health snapshot
copie d'un message serveur contenant des valeurs arbitraires

Le backend parse la chaîne avec tokio-postgres, mais la policy TLS KSP ne peut pas être contournée par des paramètres URI conflictuels. Les options libpq susceptibles de charger des fichiers ou de modifier la policy TLS ne sont pas implicitement honorées par KSP ; les paramètres effectifs sont construits/normalisés depuis les settings typés.

9. Lifecycle Store

9.1 Ouverture

Surface cible :

Store::open(settings).await -> Result<Store>

Étapes :

validate settings
resolve backend kind
reject known backend not compiled before I/O
convert facade settings -> backend settings
construct pool/TLS
prove one bounded backend connection/readiness path
run/verify bootstrap according to auto_migrate
return Store only after readiness success

Le Store n'est pas Clone dans la fondation. Aucun pool/client backend ne peut s'échapper de la façade.

9.2 Fermeture

Surface cible :

Store::close(self).await -> Result<()>

La fermeture consomme le Store, ferme le pool, interdit de nouvelles acquisitions et attend la terminaison nécessaire dans la borne shutdown_timeout_ms.

Drop reste un fallback best-effort non bloquant ; le contrat de fermeture vérifiable est close(self).await.

Cette forme limite structurellement le risque de close concurrent avec une opération qui emprunte encore le Store.

9.3 Erreurs stables candidates

Domaine Store commun :

STORE_SETTINGS_INVALID
STORE_BACKEND_NOT_COMPILED
STORE_BACKEND_OPEN_FAILED
STORE_BACKEND_CLOSED
STORE_SHUTDOWN_TIMEOUT

Backend PostgreSQL :

STORE_POSTGRES_CONFIG_INVALID
STORE_POSTGRES_CONNECT_FAILED
STORE_POSTGRES_POOL_TIMEOUT
STORE_POSTGRES_TLS_FAILED
STORE_POSTGRES_MIGRATION_FAILED
STORE_POSTGRES_MIGRATION_MISMATCH
STORE_POSTGRES_SCHEMA_NEWER
STORE_POSTGRES_HEALTH_FAILED

Le mapping final doit conserver un contexte sûr seulement : backend code, action, état, version de migration non secrète, durée/compteur borné. Aucun texte d'erreur distant arbitraire n'est propagé.

10. Migrations/bootstrap

10.1 Décision

Le moteur de migrations 0.3.2 est KSP-owned et privé à ksp-store-postgres-lib.

refinery 0.9.2 a été audité : il fournit migrations embarquées, table d'historique, détection divergent/missing et transaction groupée. Il n'est pas retenu car la fondation KSP a besoin d'un contrat plus petit, d'un nom/table/lock précis, d'un mapping d'erreurs KSP strict et d'un checksum cryptographique explicitement stable. Son checksum courant repose sur un u64 produit à partir du DefaultHasher Rust, ce qui n'est pas le contrat de persistence que KSP souhaite figer.

10.2 Ressources

Les migrations futures sont :

SQL statique KSP-owned
embarquées au build avec include_str!
ordonnées par version entière croissante
nom immuable borné
checksum SHA-256 du SQL exact
aucune découverte filesystem runtime
aucun down migration implicite

Une correction de schéma est une nouvelle migration forward.

10.3 Metadata interne

0.3.2 autorise uniquement la relation d'infrastructure fixe :

ksp_store_schema_migrations

Elle contient au minimum :

version        BIGINT PRIMARY KEY
name           TEXT NOT NULL
checksum       TEXT NOT NULL
applied_at     TIMESTAMPTZ NOT NULL

Aucun nom de table/schema n'est configurable par l'utilisateur dans cette fondation. Cela élimine le besoin normal de SQL dynamique pour les identifiants.

La création/forme de cette table est elle-même protégée par un sentinel de bootstrap version 0 avec checksum connu ; les migrations métier futures commenceront à 1.

10.4 Algorithme

Bootstrap :

acquire dedicated pooled client
begin transaction
acquire advisory transaction lock KSP fixe avec attente bornée
set transaction-local statement timeout depuis valeur bornée
create/verify history metadata
verify bootstrap sentinel
load applied migrations ordered
reject unknown/newer versions
reject missing historical versions
reject name/checksum divergence
apply each pending embedded migration in order
insert corresponding history row
commit once

L'advisory lock est acquis sans attente infinie : KSP utilise une tentative non bloquante répétée jusqu'à la deadline plutôt qu'un lock bloquant non borné.

Une erreur de migration fait rollback de la transaction entière du run courant. Les migrations déjà commitées lors d'un run antérieur restent intactes.

10.5 Aucun schéma métier en 0.3.2

La liste de migrations de production 0.3.2 ne crée :

aucune table RawTransaction
aucune table RawAccountState
aucune table CORE/DECODE/SPECIALIZED
aucun index métier

La seule persistence autorisée est la metadata privée de migration/bootstrap.

11. Health/readiness

Le health portable est retenu dans 0.3.2, mais limité à la fondation runtime.

Justification :

Store::open doit distinguer construit vs réellement prêt
integration réelle doit prouver connect/bootstrap
ops futures ont besoin d'un diagnostic safe sans pool leak
close doit être observable sans exposer le backend

Surface candidate dans ksp-store-lib :

StoreHealthState
StoreHealthSnapshot
StoreRuntimeSnapshot

Projection sûre seulement :

backend kind
state
pool size/available sous forme de compteurs bornés
schema/migration version courante
pending migration count
last safe error code éventuel

Interdits : URI, host, username, database name si sensible, SQL, nom physique de relation, server error string, pool/client handles.

Le backend PostgreSQL peut utiliser SELECT 1 et une introspection minimale interne, puis mapper son résultat vers la projection portable.

12. Config std.store

12.1 Ownership

Seul ksp-config-lib possède :

config/std.store.json
config/schemas/store.schema.json
config/examples/store.example.json
registry descriptors
placeholder interpolation
.env / process environment
provenance/sensitivity
adapter Config -> StoreSettings

Store et backend ne dépendent pas de Config et ne lisent aucun environnement.

12.2 Shape candidate

Le document V1 est profilé comme les autres documents standards :

{
  "format_version": 1,
  "default_profile": "postgres_default",
  "profiles": [
    {
      "profile_id": "postgres_default",
      "backend": "postgres",
      "postgres": {
        "connection_uri": "${KSP_SECRET_STORE_POSTGRES_URI:-postgresql://localhost/ksp}",
        "pool": {
          "max_connections": 8,
          "connect_timeout_ms": 10000,
          "wait_timeout_ms": 5000,
          "create_timeout_ms": 10000,
          "recycle_timeout_ms": 5000
        },
        "tls": {
          "mode": "verify_full"
        },
        "bootstrap": {
          "auto_migrate": true,
          "migration_timeout_ms": 30000,
          "migration_lock_timeout_ms": 10000
        },
        "shutdown_timeout_ms": 5000
      }
    }
  ]
}

Le fallback localhost est classé Secret car il appartient à un placeholder KSP_SECRET_*; sa safe projection reste redacted. Il ne provoque aucune connexion automatique.

.env.example ajoutera dans la même tranche :

# PostgreSQL connection URI used by Config std.store profiles.
# KSP_SECRET_STORE_POSTGRES_URI=postgresql://user:password@localhost/ksp

La valeur d'exemple commentée n'est jamais un vrai credential.

12.3 Feature mismatch

Config reconnaît backend = "postgres" indépendamment de la feature du consumer. L'adapter peut construire le type public StoreSettings::Postgres; c'est ksp-store-lib qui retourne STORE_BACKEND_NOT_COMPILED si la feature manque.

Ainsi Config ne doit pas connaître les cfg(feature = "postgres") de chaque consumer.

13. Logging et redaction

Les deux crates runtime auront un constants.rs privé avec un target explicite :

ksp-store-lib              target ksp-store-lib
ksp-store-postgres-lib     target ksp-store-postgres-lib

Elles utilisent ksp-logging-lib, jamais tracing directement comme surface KSP.

Événements sûrs candidats :

store_open_start / store_open_ready / store_close
postgres_pool_open / postgres_pool_close
postgres_migration_start / applied / current / failed
postgres_health

Champs autorisés : action, backend code, durations, counts, migration version/name bornée. Sont interdits URI, SQL, bind values, password, remote error detail et payload arbitraire.

Le tracing transitif éventuel de deadpool-postgres reste une dépendance externe interne ; KSP ne le réexporte pas et la politique Logging existante garde les targets externes hors des diagnostics KSP normaux sauf configuration explicite.

14. Audit d'héritage kbot3 ciblé

L'archive historique a été relue sur :

ks-store/Cargo.toml
ks-store/src/store.rs
ks-store/src/postgres/**
ks-store/migrations/postgres/**
ks-config/src/store.rs
config/store.config.json
config/schemas/store.schema.json
docs/architecture/STORAGE_ARCHITECTURE.md
docs/guides/POSTGRES_STORAGE.md

Constats :

backend PostgreSQL monolithique à l'intérieur de ks-store
SQLx PgPool
StoreOpenOptions avec backend_options serde_json::Value
max_connections / connect_timeout / auto_initialize_schema
initialisation transactionnelle + advisory lock
health SELECT 1 / current_schema / version
schema N1-N3 large et repositories déjà métier
configuration historique capable de résoudre directement environnement KS*

Classification :

REPRENDRE
  façade backend-agnostic comme intention
  pool borné + timeout explicite
  auto-init explicite
  advisory lock pour bootstrap
  health léger
  SQL/objets physiques privés
  logs structurés sans SQL/binds

REDESSINER
  séparation ksp-store-lib / ksp-store-postgres-lib
  tokio-postgres au lieu de SQLx
  settings typés au lieu de backend_options JSON
  TLS explicite vérifié
  migration history version + SHA-256
  lifecycle close explicite
  error mapping sans texte SQLx/serveur
  Config actuel KSP avec KSP_SECRET_* seulement

REPORTER
  16 tables N1-N3 historiques
  240 ressources SQL métier
  79 index
  repositories RAW/CORE/DECODE
  processing ledgers/replay
  maintenance DROP/TRUNCATE

REJETER
  SQLx comme driver KSP
  backend PostgreSQL physiquement dans la façade
  lecture directe de l'environnement par Store/backend
  anciens prefixes KS*/KB*
  backend options JSON opaques
  exposition d'erreurs backend brutes
  statut de schéma basé uniquement sur présence d'objets sans historique immuable

L'archive kbot3 est une source de design historique, jamais une base de copie de code ou de schéma.

15. Threat model 0.3.2

15.1 Credential/diagnostic leak

Risque : DSN/password via Debug, errors, tracing, snapshots ou panics.

Mesures : wrapper/settings Debug redacted, aucun DSN dans ErrorContext, mapping stable des erreurs, aucune remote error string, tests canary avec secret hostile.

15.2 Bypass Config par environnement libpq

Risque : PGHOST, PGPASSWORD, .pgpass, sslrootcert ou autres sources implicites changent le runtime.

Mesures : Store/backend ne lisent aucun env, configuration driver construite depuis settings explicites, source scanner KSP, integration test stdin sans env.

15.3 Connection string hostile

Risque : URI surdimensionnée, malformed, options conflictuelles TLS, valeurs loggées.

Mesures : borne de longueur, parse avant I/O, erreurs génériques, policy TLS typée qui prime, pas d'écho input.

15.4 Connection storm / pool non borné

Risque : nombre excessif de connexions, waiters ou timeouts infinis.

Mesures : max_connections <= 64, tous les timeouts bornés, deadpool configuré explicitement, aucun retry loop de connexion caché dans Store.

15.5 Hung connect / migration / shutdown

Risque : futur bloqué indéfiniment.

Mesures : connect/pool timeouts, migration deadline, advisory try-lock borné, statement timeout local, close timeout.

15.6 Concurrent migration runners

Risque : DDL/history race.

Mesures : transaction unique + advisory transaction lock fixe, history relue sous lock, test concurrent réel.

15.7 Historical migration modifiée

Risque : source rebuildée avec SQL différent sous même version.

Mesures : nom/version immuables + SHA-256 persisté, mismatch terminal avant migration suivante.

15.8 Migration partielle

Risque : DDL appliqué sans history ou inversement.

Mesures : transaction unique pour le run, history insert dans la même transaction, rollback sur erreur, test d'échec injecté privé.

15.9 SQL/dynamic identifier injection

Risque : nom de schema/table fourni par Config interpolé dans SQL.

Mesures : aucun nom physique configurable en 0.3.2; ressources SQL statiques et valeurs paramétrées.

15.10 Feature/config mismatch

Risque : Config demande PostgreSQL mais binaire sans feature.

Mesures : type backend connu toujours présent + STORE_BACKEND_NOT_COMPILED avant I/O.

15.11 Connection task leaked/dropped

Risque : future tokio-postgres non pilotée ou tâche orpheline après close.

Mesures : deadpool-postgres retenu précisément pour ownership du JoinHandle par client wrapper ; aucun client/pool ne fuit de la façade ; close explicite + Drop fallback.

15.12 Schema history newer than runtime

Risque : ancien binaire lancé contre DB migrée par une version plus récente.

Mesures : version appliquée inconnue/supérieure => STORE_POSTGRES_SCHEMA_NEWER, aucune migration/down automatique.

15.13 Server error echo

Risque : message PostgreSQL incorpore table/value/credential/SQL snippet.

Mesures : conversion immédiate vers code KSP + contexte allowlisté ; source error brute non incluse dans Display/Debug public.

16. PostgreSQL integration test réel

Le test opt-in sera dans ksp-store-postgres-lib/tests/ et ignoré par défaut.

Entrée :

une URI PostgreSQL dédiée lue depuis stdin
aucune variable d'environnement
aucun affichage de l'URI

Sécurité :

refuser de démarrer si ksp_store_schema_migrations existe déjà
ne créer aucune table métier
ne DROP ni database ni schema
cleanup best-effort uniquement de la table metadata que le test a prouvé avoir créée

Scénario minimal :

connect et major >= 15
bootstrap initial
second bootstrap idempotent
deux bootstrap concurrents après reset contrôlé
corruption test-only du checksum sentinel -> mismatch explicite
restore/reset contrôlé
migration failure injectée sous cfg(test) -> rollback/history inchangée
health ready
close explicite borné
cleanup metadata

Le gate de référence visera PostgreSQL 18.6. Le test peut accepter un serveur >= 15, mais le rapport opérateur doit enregistrer le major réellement utilisé sans imprimer d'identité sensible.

17. Validation de dépendances et API

Canaris à créer au fil des tranches :

ksp-store-lib --no-default-features compile
postgres est feature default
known-but-not-compiled error observable
ksp-store-postgres-lib ne dépend pas ksp-store-lib
Store/backend ne dépendent pas Config/Transport/Program/Materializer
Store/backend production sources ne lisent pas env
aucun type tokio-postgres/deadpool/rustls dans crate root ksp-store-lib
ksp-store-api inchangé et dépend toujours seulement de ksp-core-lib
aucune des 10 capabilities RAW implémentée par PostgreSQL en 0.3.2
aucune table RAW créée par migrations

La façade ksp-store-lib réexportera la surface crate-root de ksp-store-api nécessaire aux consumers, mais ne réexportera aucun type ksp-store-postgres-lib.

18. Sizing et prévision souple recalibrée

Le scope reste clôturable dans une session si les responsabilités restent fines. La prévision initiale du prompt est conservée avec deux précisions : la réconciliation normative est absorbée par pre.001, et le choix migrations KSP-owned évite une tranche supplémentaire.

pre.001 — Audit, design, threat model et réconciliation normative

Livrer le présent plan, la matrice validation, l'audit externe/kbot3, les décisions pool/TLS/migrations/Config, le graphe exact et la correction des règles Store obsolètes. Aucun code runtime.

pre.002 — Scaffold des deux crates + feature graph

Créer les crates, manifests, modules minimaux, postgres default, --no-default-features, constants/logging targets et canaris de dépendances. Ajouter seulement les dépendances nécessaires au scaffold retenu.

pre.003 — Settings + backend selection + lifecycle contracts

Matérialiser StoreSettings, StoreBackendSettings, PostgresStoreSettings, erreurs stable, façade Store sans connexion lourde et réexports API. Tester la feature mismatch.

pre.004 — Config std.store

Document/schema/example/registry/adaptor, .env.example, provenance/sensitivity/redaction, packaging resources strictement nécessaires. Aucun env dans Store/backend.

pre.005 — PostgreSQL connection + deadpool + Rustls

Implémenter parse/normalisation URI, pool borné, connect/open/close, VerifyFull/Disabled, timeouts et erreurs redacted. Aucun SQL métier.

pre.006 — Migration/bootstrap foundation

Créer history metadata, sentinel, SHA-256, advisory lock borné, transaction, version/checksum/newer/missing handling et rollback. Aucun schéma RAW.

pre.007 — Composition end-to-end + health

Fermer StoreSettings -> Store -> PostgresBackend, health/readiness portable, close et mapping diagnostics.

pre.008 — PostgreSQL integration réelle

Ajouter/exécuter le smoke opt-in non destructif : initial/idempotent/concurrent/mismatch/failure/close sur serveur réel.

pre.009 — Hardening/completeness/dependency matrix

Inputs hostiles, redaction, no-env, exact exports/modules, external backend compatibility, --no-default-features, graphes/features/duplicates, non-régression API.

pre.010 — Gate technique final

Workspace/clippy/tests, targeted tests, PostgreSQL live gate, graphes Cargo finaux. Aucun développement fonctionnel nouveau.

pre.011 — Réconciliation documentaire finale

README/USAGE des deux crates, plan/validation/indexes/architecture réellement impactés. Pas de CHANGELOG/ROADMAP/prompt suivant.

pre.012 — Préparation de publication minimale

Uniquement la lane autorisée :

Cargo.toml
CHANGELOG.md
ROADMAP.md
prompts/022-V0_3_3_START_PROMPT.md
deltas/0.3.2/pre.012.md

rel.001 — Publication stable

Version stable + delta de publication uniquement.

19. Critères de clôture de la release

0.3.2 ne ferme que si :

les deux crates existent
feature postgres default et no-default compile
graphe backend séparé prouvé
tokio-postgres/deadpool/Rustls bornés et privés
Config std.store possédée par ksp-config-lib
aucune lecture env/PG*/.pgpass dans Store/backend
migration history version/checksum/lock/rollback prouvée
health/readiness safe prouvé
PostgreSQL réel vert
close borné vert
aucun RawTransaction/RawAccountState implémenté
aucun SQL métier RAW livré
ksp-store-api non régressé
README/USAGE et validation réconciliés
workspace/clippy/tests/graphes verts

20. Hors scope explicite

RawTransaction persistence/queries/retention
RawAccountState persistence/queries/retention
batch/backlog/priority worker/job
claim/lease métier
notifications persistées
replay Core/Decode
schema métier kbot3
SQLx
ORM
custom CA/mTLS sans besoin concret
maintenance destructive publique
Store Desk

21. Questions restantes

Aucune question architecturale ne bloque pre.002.

Les détails suivants sont réservés à leur tranche sans rouvrir les décisions du gate :

nom exact des structs/methods settings en pre.003
codes numériques/strings finaux des ErrorCode en pre.003
mapping exact deadpool timeouts en pre.005
construction exacte du rustls RootCertStore en pre.005
DDL précis de ksp_store_schema_migrations en pre.006
forme finale des snapshots health en pre.007

Toute découverte qui exigerait :

un type PostgreSQL dans ksp-store-api
une dépendance backend -> ksp-store-lib
une lecture directe d'environnement
une table RAW métier dans 0.3.2

est considérée comme contradiction architecturale et doit être réauditée avant poursuite.