Files
khadhroony-solana-project/docs/plans/022-V0_3_1_STORE_RAW_PLAN.md

44 KiB
Raw Blame History

Plan 0.3.1 — Store API RAW foundation

1. Statut et base

Ce plan est établi par 0.3.1-pre.001 à partir de la release stable v0.2.14 et de l'archive historique obligatoire khadhroony-bot3_v0.5.3-pre.005-fix010.zip.

La base KSP vérifiée à l'ouverture est :

workspace.package.version = 0.2.14
deltas/0.2.14/rel.001.md présent
prompts/020-V0_3_1_START_PROMPT.md présent
ksp-program-api présent
ksp-store-api absent
ksp-store-lib absent

L'archive opérateur ne contient pas de metadata Git exploitable ; le tag v0.2.14 ne peut donc pas être revérifié localement. La version Cargo, le delta rel.001, le prompt 020 et la surface Program API publiée concordent avec la base stable attendue.

Le journal opérateur fourni avec l'archive montre un baseline stable intégralement vert avant ouverture de 0.3.1 : audits Rust/Markdown, cargo check --workspace, Clippy et cargo test --workspace passent.

Après application de pre.001, la version Cargo cible est :

0.3.1-pre.1

2. Décision opérateur qui recalibre le prompt initial

Le brainstorming de pre.001 a séparé la création de l'API Store de son implémentation PostgreSQL.

La trajectoire devient :

0.3.1 = ksp-store-api uniquement
0.3.2 = ksp-store-lib + ksp-store-postgres-lib
        feature postgres par défaut
        PostgreSQL de référence via tokio-postgres

Cette décision remplace pour 0.3.1 la mission combinée ksp-store-api + ksp-store-lib décrite dans le prompt de démarrage. pre.001-fix.001 réconcilie immédiatement le ROADMAP.md afin que la trajectoire globale ne conserve pas une séquence désormais fausse.

Conséquences immédiates :

aucune dépendance PostgreSQL en 0.3.1
aucun tokio-postgres dans Cargo en 0.3.1
aucun pool
aucune table
aucun SQL
aucune migration
aucun std.store
aucun ksp-store-lib

0.3.1 doit stabiliser le contrat logique suffisamment pour que 0.3.2 puisse ensuite demander :

quelle représentation PostgreSQL satisfait le mieux ce contrat ?

et non :

comment exposer les tables déjà créées ?

3. Mission recalibrée

0.3.1 introduit ksp-store-api comme modèle logique commun et API fonctionnelle backend-agnostic du niveau N1 / D1 RAW.

La crate doit posséder :

modèles objet/struct persistants communs
références durables
provenance d'acquisition
outcomes d'écriture/idempotence
queries et pagination bornées
health/readiness communs utiles
contrats/capabilities backend extensibles
format canonique de notification de disponibilité si la référence RAW est stabilisée

La façade runtime concrète Store, la sélection d'un backend compilé et l'orchestration commune appartiendront à ksp-store-lib en 0.3.2. Les consumers ordinaires jobs/workers/apps dépendront alors uniquement de ksp-store-lib, qui réexportera la surface commune nécessaire de ksp-store-api.

Elle ne possède pas :

schéma physique
rows backend
SQL
migrations
pool/connection
transactions SQL
backend selection par lecture Config
tokio-postgres
PostgreSQL/MySQL/SQLite/RocksDB/ClickHouse types
Transport
Program decoder
Materializer

Le modèle public ksp-store-api est volontairement réutilisable par toute implémentation future. Un backend peut choisir une représentation physique radicalement différente sans modifier les DTO/entités et opérations logiques consommés par l'extérieur.

4. Modèle d'extensibilité backend

4.1 Graphe durable

Le graphe cible devient :

                              ksp-store-api
                         modèles + contrats communs
                                   |
                +------------------+------------------+
                |                                     |
                v                                     v
          ksp-store-lib                    ksp-store-postgres-lib
     façade/runtime commun                    impl PostgreSQL
       backend dispatch                         tokio-postgres
                |                                     ^
                | feature postgres (default) ----------+
                |
                +-- feature mysql ------> futur ksp-store-mysql-lib
                +-- autres features ----> futurs backends

Règles de dépendances :

ksp-store-lib              -> ksp-store-api
ksp-store-lib[postgres]    -> ksp-store-postgres-lib
ksp-store-postgres-lib     -> ksp-store-api
futur ksp-store-mysql-lib  -> ksp-store-api

ksp-store-postgres-lib -X-> ksp-store-lib
backend alternatif     -X-> ksp-store-lib

ksp-store-lib est donc le point de consommation normal du workspace, tandis que ksp-store-api reste le contrat d'implémentation partagé entre la façade et les backends.

4.2 Features de ksp-store-lib

0.3.2 introduira au minimum :

default = [postgres]
postgres -> dépendance optionnelle ksp-store-postgres-lib

Les futures features peuvent ajouter mysql, sqlite, rocksdb, clickhouse ou tout autre backend réellement implémenté. Plusieurs features backend peuvent être compilées simultanément ; la feature décide quels backends sont disponibles dans le binaire, jamais lequel est sélectionné au runtime.

La Config décide le backend actif parmi ceux compilés. Un backend KSP connu mais absent des features du binaire doit produire une erreur distincte et stable de type STORE_BACKEND_NOT_COMPILED, sans fallback silencieux vers PostgreSQL ou un autre backend.

4.3 Config, URI et secrets

ksp-store-api, ksp-store-lib et les crates backend ne lisent jamais directement .env ni les variables KSP/KSPB. ksp-config-lib reste propriétaire des documents, placeholders, .env, provenance et sensibilité.

La configuration future suit :

std.store / profil composite
        |
        v
ksp-config-lib
  résolution ${KSP_SECRET_*}
  + validation schema/sémantique
        |
        v
settings runtime ksp-store-lib
        |
        v
backend compilé sélectionné
        |
        v
crate backend privée

Règles fixées pour 0.3.2 et les futurs backends :

  • utiliser une URI/DSN lorsque le moteur possède une forme URI naturelle (postgresql://..., futur mysql://..., etc.) ;
  • permettre des options typées backend-specific uniquement lorsqu'elles sont réellement nécessaires et sans serde_json::Value opaque comme contrat runtime ;
  • considérer l'URI entière comme Secret dès qu'elle peut embarquer user/password/token ou autre credential ;
  • obtenir les secrets via des placeholders KSP_SECRET_* résolus par Config, avec inventaire dans .env.example au même delta que leur première utilisation ;
  • ne jamais versionner un vrai .env ni un URI/credential réel ;
  • ne jamais exposer URI, password, token, query secret ou chemin sensible dans Debug, logs, erreurs ou health ;
  • distinguer backend inconnu de backend KSP connu mais non compilé avant tentative de connexion ;
  • éviter tout fallback implicite vers PG*, .pgpass ou autre source d'environnement lue directement par le driver/backend lorsque Config a déjà fourni les settings effectifs.

La forme exacte de StoreSettings et du document std.store appartient au design de 0.3.2; 0.3.1 fixe seulement ces responsabilités et invariants.

4.4 Surface consumer

Les jobs/workers/apps ne dépendent pas directement de ksp-store-postgres-lib ni des futurs backends. Le chemin normal est :

ksp-job-* / ksp-worker-* / app
        -> ksp-store-lib
             -> réexporte les types API communs nécessaires
             -> expose Store/open/read/write/query communs
             -> dispatch vers le backend choisi par Config

Le consumer ne contient aucun #[cfg(feature = "postgres")] métier et ne branche pas sur postgres, mysql ou un nom de moteur pour lire/écrire les données.

4.5 Modèle public et modèle interne

Trois surfaces restent strictement distinctes :

ksp-store-api
    RawTransaction / RawLog / provenance / queries / outcomes
    = modèle objet et contrats communs à toutes les implémentations

ksp-store-lib
    Store / lifecycle / dispatch / settings runtime publics communs
    = façade de consommation normale

ksp-store-postgres-lib
    Postgres*Row / SQL / schema / migrations / pool / statements
    = implémentation privée au backend

Aucun type de row backend, handle de pool, statement préparé, migration ou nom d'objet SQL ne traverse ksp-store-lib vers ses consumers.

5. Sources KSP relues

Le gate pre.001 a relu les sources prescrites par le prompt :

RULES.md
docs/000-README.md
docs/rules/RULES_GENERAL.md
docs/rules/RULES_KSP.md
docs/rules/RULES_RUST.md
docs/rules/RULES_DEPENDENCIES.md
docs/rules/RULES_DOCUMENTATION.md
docs/rules/FILE_CONTRACTS.md
docs/rules/VERSION_WORKFLOW.md
docs/rules/PROMPT_STRUCTURE.md

docs/architecture/000-README.md
docs/architecture/001-PROJECT_OBJECTIVES.md
docs/architecture/002-LAYERS_AND_DEPENDENCIES.md
docs/architecture/003-COMPONENT_CONTRACTS.md
docs/architecture/004-COMPONENT_INVENTORY.md
docs/architecture/005-DEPENDENCY_GRAPH.md
docs/architecture/006-WIRE_AND_PROGRAM.md
docs/architecture/007-EXECUTION_AND_POLICY.md
docs/architecture/008-DATA_MATERIALIZATION_AND_STORE.md
docs/architecture/009-ACQUISITION_WORKERS_AND_JOBS.md
docs/architecture/010-APPS_SERVICES_SCENARIOS_AND_CONTROL.md

Les règles directement structurantes sont :

KSP-API-001..007
KSP-CONFIG-001..018
KSP-NOTIFY-001..006
KSP-STORE-001..002
KSP-DATA-001..004
KSP-PIPE-001..007
DEP-KSP-001..005
DEP-CARGO-001..007
DEP-STORE-001..008
DEP-TRANSPORT-001..005
DEP-PIPE-001..008
DEP-WORKER-001..003
DEP-JOB-001..003

Le résultat normatif principal est :

Store API possède les contrats persistants
Store API -X-> Transport/Program/Materializer
Transport -X-> Store API
conversion explicite à la composition
persist -> commit -> notification
notification != backlog
at-least-once + persistence idempotente

6. N1 / D1 RAW : définition retenue

6.1 Rôle

N1 est la couche acquisition persistée / RAW replayable.

Elle ne se limite pas à une table raw_transactions. Elle regroupe les faits d'acquisition nécessaires pour rejouer ultérieurement la normalisation générique Solana sans redemander arbitrairement la donnée au provider.

La première surface concrète 0.3.1 retient :

RawTransaction
RawTransactionObservation
RawLog
RawLogObservation
provenance commune
références durables communes

Les familles suivantes sont explicitement prévues mais reportées tant que leur acquisition/replay exact n'est pas suffisamment auditée :

RawAccount / account observation
RawBlock
RawSlot / slot update si un besoin durable distinct existe
autres familles d'acquisition réellement nécessaires

Aucun enum fermé ne doit empêcher leur ajout.

6.2 Transport n'est pas une famille RAW

Les mécanismes suivants sont des origines d'acquisition, pas des modèles persistants distincts :

HTTP JSON-RPC
WebSocket JSON-RPC
Yellowstone gRPC
provider extension
import
backfill

Interdit :

RawHttpTransaction
RawWsTransaction
RawGrpcTransaction

La même transaction logique acquise par HTTP, WS et gRPC doit converger vers un RawTransaction commun, avec plusieurs observations/provenances si nécessaire.

6.3 Transaction canonique et observation

Le contrat sépare :

RawTransaction
    = fait RAW canonique source-independent et replayable

RawTransactionObservation
    = fait qu'une source donnée a observé/acquis cette transaction

Exemple :

                 RawTransaction T
                        ^
                        |
        +---------------+---------------+
        |               |               |
 HTTP/Helius obs   WS/Helius obs   gRPC/PublicNode obs

La duplication d'observation ne duplique pas automatiquement la transaction canonique.

6.4 Raw logs

RawLog est une famille N1 distincte et n'est pas confondue avec la future décomposition CORE des logs.

Elle doit préserver l'observation de logs suffisamment fidèlement pour :

conserver l'ordre des lignes
conserver slot/signature/identité applicable
conserver le statut source nécessaire
permettre replay/inspection
permettre de relier un log à une transaction lorsqu'elle est connue

Une observation logsSubscribe peut également servir de déclencheur pour acquérir ensuite la transaction complète. Le modèle de provenance ne doit pas empêcher une lineage future :

RawLogObservation
    -> déclenche acquisition transaction
RawTransactionObservation
    -> RawTransaction

Cette relation causale n'est pas un orchestrateur et n'est pas obligatoirement implémentée dans 0.3.1.

6.5 Autres RAW

Le design doit permettre d'ajouter plus tard des familles distinctes sans créer un RawAnything { kind, json } universel.

Le partage se fait par composition de primitives communes :

RawAcquisitionProvenance
RawPayload
RawContentHash / idempotence key
RawDataReference
PageRequest / Page

et non par effacement des sémantiques propres à chaque famille.

7. Payload replayable

7.1 Décision de niveau API

ksp-store-api possède un payload de persistence RAW KSP, pas un payload réseau provider.

Le contrat candidat est un conteneur borné et versionné :

RawPayload
    format_id
    format_version
    bytes
    content_hash

format_id identifie un format de persistence KSP source-independent. Il ne vaut pas helius_json, yellowstone_proto ou un autre wire provider.

La conversion :

transport model
    -> conversion explicite
    -> RawPayload KSP

reste hors de ksp-store-api.

7.2 Pas de codec improvisé

0.3.1 ne choisit pas un codec réseau ou Solana concurrent. bincode reste interdit pour les codecs wire KSP.

Le premier format concret de transaction canonique doit pouvoir être produit par la future couche de conversion à partir des wires génériques KSP. Sa définition détaillée peut évoluer pendant 0.3.1 tant que les invariants publics restent :

source-independent
versionné
borné
lossless pour la normalisation générique couverte
Debug sans bytes
hashable/idempotent

Le Store ne prétend pas que des bytes arbitraires sans format connu sont replayables.

8. Future décomposition N1 -> niveau générique Solana

La frontière architecturale actuelle reste nommée :

RAW -> CORE -> DECODE -> SPECIALIZED

Le nom CORE n'est pas considéré comme irréversible. Une revue de nomenclature pourra être faite avant l'ouverture effective de D2 sans modifier les responsabilités déjà fixées.

Le rôle de D2 reste en revanche clair : décomposer/normaliser une transaction RAW en faits Solana génériques indépendants des decoders Program.

Le futur flux doit permettre notamment :

RawTransaction
    -> transaction/message générique
    -> account keys
    -> instruction top-level #0
    -> instruction top-level #1
    -> CPI/inner instruction #1.0
    -> CPI/inner instruction #1.1
    -> logs/meta/balances/return data applicables

Chaque instruction/CPI doit ensuite pouvoir être traitée indépendamment :

instruction générique
    -> decoder disponible ?
         oui -> decode
         non -> Unsupported/NotApplicable durable sans bloquer le reste

0.3.1 ne crée aucun type D2/CORE et ne dépend pas de ksp-program-api.

9. Provenance commune

La provenance candidate doit pouvoir représenter sans secret :

network/cluster
provider code
endpoint logical id optionnel
transport/protocol code
acquisition method
origin live/backfill/import/replay/repair
commitment optionnel
capture/session/filter logical ids optionnels
observed/received timestamp
source payload size/hash optionnels

Interdits :

endpoint URL
DSN
API key
token/password
payload source complet par simple diagnostic

Les codes provider/protocol/method restent ouverts et bornés ; aucun enum provider fermé n'est introduit dans Store API.

10. Identité et idempotence

10.1 Principes

L'API ne fournit pas de méthode has_* à appeler avant un write idempotent.

Le backend doit recevoir une opération unique avec ces sémantiques :

clé déjà absente
    -> Inserted

même clé + même contenu canonique
    -> AlreadyPresent

même clé + contenu différent
    -> Conflict

Cela évite un check-then-insert race.

10.2 Transactions

La clé logique d'un RawTransaction est dérivée au minimum du réseau et de la signature canonique. Un hash du contenu canonique protège contre une collision logique avec contenu divergent.

La signature ne doit pas être représentée comme un identifiant SQL i64 dans l'API.

10.3 Observations

Chaque observation possède une clé d'idempotence déterministe fournie par le producer/composition selon un contrat documenté. Deux acquisitions légitimes distinctes peuvent donc être conservées même si elles pointent vers le même RAW.

10.4 Logs

L'identité exacte d'un RawLog sera stabilisée avec son modèle concret ; elle doit être déterministe et ne doit pas dépendre d'un primary key backend.

Aucune promesse exactly-once distribuée n'est faite.

11. API candidate

11.1 Contrats communs

ksp-store-api définit le modèle et les opérations backend-agnostic qu'une implémentation doit satisfaire. 0.3.1 ne construit pas de backend, ne sélectionne aucun moteur et n'introduit pas encore la façade runtime concrète Store.

Le backend concret implémente un contrat public d'extension, mais son modèle interne reste privé. En 0.3.2, ksp-store-lib::Store enveloppera ce contrat et deviendra la seule façade de consommation normale des jobs/workers/apps.

11.2 Object-safety et async

La future sélection runtime impose que le contrat backend puisse être stocké derrière une abstraction dynamique sans connaître le moteur concret.

La stratégie candidate est :

StoreBackend: Send + Sync
méthodes object-safe
futures boxed KSP-owned via un alias StoreFuture<'a, T>
future ksp-store-lib::Store possède Arc<dyn StoreBackend>

Cette stratégie évite une dépendance async-trait uniquement pour masquer la transformation. Le coût d'une box de future est accepté au niveau Store, dominé par l'I/O de persistence, et doit rester mesurable si un chemin futur démontre le contraire.

11.3 Capabilities

Éviter un trait de 150 méthodes. La candidate est une composition de capabilities N1 :

StoreHealth
RawTransactionStore
RawLogStore

Un contrat composite StoreBackend peut agréger les capabilities N1 nécessaires à la future façade ksp-store-lib::Store.

Les futures couches D2/D3/D4 ne sont pas ajoutées par anticipation.

11.4 Opérations atomiques métier

Aucun handle de transaction SQL/public n'est exposé.

Les invariants multi-écritures sont exprimés par des opérations logiques communes. Candidate :

persist_transaction_acquisition(transaction, observation)
record_transaction_observation(observation)
get_raw_transaction(reference)
list_raw_transactions(query, page)
list_transaction_observations(query, page)

persist_log_acquisition(raw_log, observation)
record_log_observation(observation)
get_raw_log(reference)
list_raw_logs(query, page)
list_log_observations(query, page)

health()

persist_*_acquisition signifie au contrat que le RAW et son observation réussissent atomiquement ou échouent ensemble. PostgreSQL réalisera cela avec une transaction privée en 0.3.2; un autre backend utilisera son mécanisme natif.

11.5 Outcomes

Le vocabulaire candidat :

RawWriteOutcome
    Inserted
    AlreadyPresent

Conflict
    = Error KSP stable, pas un succès silencieux

Pour une acquisition combinée, l'outcome doit distinguer au minimum l'état du RAW et de l'observation sans révéler de clé backend.

11.6 Pagination

Le concept historique PageRequest/PageSlice est repris avec :

default = 100
maximum = 500
cursor opaque et borné
ordre déterministe par query

Le cursor est un token d'implémentation retourné par la façade et réinjecté tel quel par le consumer. Son contenu ne devient pas API et ne doit pas être utilisé comme transport d'un SQL fragment.

12. Références durables et notifications

L'ownership KSP-NOTIFY-001..006 reste à ksp-store-api.

Le candidat est une référence compacte :

RawDataReference
    Transaction(RawTransactionReference)
    Log(RawLogReference)
    futures variantes non exhaustives

Puis un signal logique :

RawDataAvailable {
    reference,
}

Le mécanisme de diffusion est hors scope.

Ordre obligatoire :

persist
commit
notify

La notification ne transporte pas le payload RAW et ne remplace jamais list_*/backlog Store.

Si les références concrètes ne sont pas assez stabilisées lors de leur tranche, le type de notification peut être reporté sans déplacer son ownership.

13. Health et diagnostics

La façade peut exposer un health commun minimal :

Unknown
Healthy
Degraded
Unhealthy

avec un backend code sûr et un code diagnostic borné si nécessaire.

Ne pas exposer dans l'API commune :

migration SQL state
pool handles
connection count détaillé par driver
server error brut
DSN
SQLSTATE arbitraire présenté comme contrat portable

Les diagnostics spécifiques à PostgreSQL appartiennent à ksp-store-postgres-lib. ksp-store-lib ne projette vers le contrat commun que les états réellement portables et sanitised.

14. Dependency graph 0.3.1

Graphe maximal par défaut :

ksp-store-api
└── ksp-core-lib

Aucune dépendance externe n'est nécessaire pour la foundation candidate :

pas de serde
pas de chrono
pas de tokio
pas de async-trait
pas de futures-util
pas de ksp-interface-lib tant qu'un type Interface réel n'est pas requis

Les timestamps persistants peuvent être représentés par un newtype KSP en unités Unix explicites plutôt que d'ajouter chrono par réflexe. Le choix exact est prouvé dans la tranche de modèle.

15. Audit historique kbot3

15.1 Surface auditée

L'archive historique a été extraite et les surfaces prescrites ont été relues, notamment :

ks-store/Cargo.toml
ks-store/README.md
ks-store/USAGE.md
ks-store/TODO.md
ks-store/src/lib.rs
ks-store/src/store.rs
ks-store/src/contracts/**
ks-store/src/postgres/**
ks-store/migrations/postgres/**
config/store.config.json
config/schemas/store.config.schema.json
ks-config/src/store.rs
docs/architecture/STORAGE_ARCHITECTURE.md
docs/guides/POSTGRES_STORAGE.md
docs/plans/V0_5_3_KS_STORE_NORMALIZATION_PLAN.md

Le Store historique couvre 16 tables N1-N3, 240 ressources SQL atomiques et 79 index attendus. Cette surface confirme l'intérêt de la séparation façade/backend, de l'idempotence et des observations, mais elle est beaucoup trop large pour 0.3.1.

15.2 Matrice d'héritage

Concept historique Observation kbot3 Décision Application KSP
séparation façade Store / PostgreSQL PostgreSQL et sqlx::PgPool privés REPRENDRE API commune séparée; façade + backend PostgreSQL reportés à 0.3.2
StoreOpenOptions / backend selection backend + serde_json::Value opaque dans Store REDESSINER Config produit les settings communs; ksp-store-lib sélectionne un backend compilé et construit Store
health/readiness contrat backend-neutral présent REPRENDRE health minimal portable dans ksp-store-api
contracts/dto/raw.rs transaction canonique + observations transaction/account REDESSINER transaction + logs + provenance commune, sans JSON universel ni lifecycle D2/D3
contracts/entity/raw.rs rows exposant i64, JSON et timestamps backend-shaped REDESSINER entités logiques sans PK SQL ni type physique
RawTransactionStore has_* puis inserts séparés REDESSINER write idempotent atomique sans check-then-insert
repository traits capabilities async séparées REPRENDRE capabilities N1 séparées + façade commune
pagination 100 par défaut, 500 max, cursor opaque REPRENDRE mêmes bornes candidates, cursor toujours opaque
replay contracts filtres mêlés au processing N2/N3 REPORTER 0.3.1 fournit seulement lectures RAW nécessaires au futur replay
error contracts erreurs Store historiques REDESSINER ksp_core_lib::Error/Result + codes Store stables
schema validation / migration bootstrap validation stricte des objets PostgreSQL REPORTER responsabilité ksp-store-postgres-lib en 0.3.2
migrations atomiques ressources SQL actives nombreuses REPORTER aucune migration dans Store API
constraints/indexes 79 index attendus REPORTER design physique seulement après stabilisation API
idempotence / uniqueness observation keys + canonical identity REPRENDRE sémantique publique déterministe; mécanisme backend privé
raw transaction table table physique k_sol_raw_transactions REPORTER RawTransaction commun maintenant; table privée à ksp-store-postgres-lib en 0.3.2
acquisition observations transaction + account observations REPRENDRE concept N1 central; transaction + log initialement
processing ledger couvre N2/N3 processing REPORTER appartient aux futures couches de processing, pas N1 foundation
CORE tables transaction, keys, instructions, CPI, logs, balances, return data REPORTER future couche D2, aucune surface en 0.3.1
DECODE coverage/events contrats decoder/materializer REPORTER future D3
materialization journal k_sol_mat_outputs REPORTER future D3
maintenance truncate/drop scripts destructifs dédiés REJETER aucune API runtime générique destructive Store
Config Store backend + options JSON historiques REDESSINER Config reste owner; settings Store/URI secrets adaptés vers ksp-store-lib en 0.3.2
runtime diagnostics résumés sanitised mais backend riches REDESSINER health portable dans API; détails backend privés

16. Audit PostgreSQL et driver de référence futur

16.1 Décision

Le choix opérateur est fixé pour la version suivante :

ksp-store-lib
    façade/runtime commun
    feature postgres = feature par défaut
    feature postgres -> ksp-store-postgres-lib

ksp-store-postgres-lib
    backend officiel de référence = PostgreSQL
    driver interne = tokio-postgres

Ce choix n'ajoute aucune dependency à 0.3.1.

16.2 État externe audité au 28 août 2026

Sources primaires actuelles consultées : documentation PostgreSQL et documentation/package tokio-postgres.

Résultats :

PostgreSQL stable courant : 18.6, publié le 13 août 2026
tokio-postgres courant : 0.7.18, publié le 12 juin 2026
edition tokio-postgres : Rust 2024
MSRV tokio-postgres : Rust 1.85
runtime async : Tokio
prepared statements : supportés
transactions / isolation : supportées
COPY in/out : supporté
pipelining : supporté
TLS : connector externe, à choisir explicitement en 0.3.2

tokio-postgres::Config peut être construit et renseigné programmaticalement. 0.3.2 devra éviter les chemins de configuration implicites : l'URI/les options effectives viennent de ksp-config-lib via les settings publics de ksp-store-lib, puis sont converties vers le backend PostgreSQL sans lecture directe de .env, PG* ou .pgpass.

16.3 SQLx

SQLx 0.9.0 a été réaudité comme alternative mais n'est plus retenu après décision opérateur.

La comparaison a néanmoins confirmé l'intérêt d'éviter dans KSP les defaults de connexion influencés par PG*/.pgpass lorsque le backend peut être construit explicitement depuis Config.

La performance ne constitue pas à elle seule la raison du choix : 0.3.2 devra optimiser les vrais chemins KSP avec prepared statements, batching/COPY lorsque justifié, transactions et indexes adaptés.

17. Contrat attendu de 0.3.2 sans le concevoir physiquement ici

0.3.2 introduira deux crates dans la même release :

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

ksp-store-lib devra :

dépendre de ksp-store-api
réexporter la surface API commune nécessaire aux consumers
exposer la façade Store et son lifecycle
posséder la sélection/dispatch des backends compilés
activer postgres par défaut
importer ksp-store-postgres-lib seulement sous feature postgres
accepter les settings effectifs fournis par Config
rejeter distinctement un backend connu mais non compilé
ne jamais exposer les types internes d'un backend

ksp-store-postgres-lib devra implémenter le même modèle objet et les mêmes contrats de ksp-store-api et pourra créer librement :

PostgresStoreBackend
Postgres*Row privés
migrations
schema version
prepared statements
indexes
pool
transaction handles privés
mapping Rust API <-> PostgreSQL
tokio-postgres

Les consumers jobs/workers/apps dépendront uniquement de ksp-store-lib, avec la feature par défaut ou une sélection explicite de features. Les crates backend alternatives dépendront de ksp-store-api, pas de ksp-store-lib.

La compatibilité PostgreSQL devra être testée par round-trip des modèles API et par introspection/validation du schema réel.

18. Sorties PostgreSQL du prompt initial explicitement déplacées

Le prompt de démarrage demandait encore à pre.001 un schéma PostgreSQL candidat, une migration policy et un gate PostgreSQL live parce que 0.3.1 devait initialement contenir API et implémentation. La décision de split rend ces sorties prématurées.

Elles sont donc classées ainsi :

Sortie initialement demandée Décision pre.001 Release propriétaire
tables/colonnes/PK/FK/indexes RAW REPORTER 0.3.2 ksp-store-postgres-lib
mapping u64/bytes/timestamps vers PostgreSQL REPORTER 0.3.2
migration layout/version/checksum REPORTER 0.3.2
schema introspection/conformance REPORTER 0.3.2
pool/TLS/timeouts REPORTER 0.3.2
PostgreSQL live gate REPORTER 0.3.2
SQL injection/statement policy exécutable REPORTER 0.3.2

Les invariants API qui contraignent déjà ce futur schéma restent fixés maintenant :

identités logiques non dépendantes d'une PK SQL
idempotence Inserted/AlreadyPresent/Conflict
atomicité RAW + observation
pagination bornée
payload versionné et borné
provenance sans secret
round-trip exact du modèle objet commun

Aucun nom de table candidat n'est donc produit par 0.3.1-pre.001. C'est une conséquence volontaire du split, pas une omission du gate.

La trajectoire roadmap corrigée est :

0.3.1  ksp-store-api RAW/observations
0.3.2  ksp-store-lib + ksp-store-postgres-lib
0.3.3  wires Interface nécessaires à l'acquisition/normalisation future
0.3.4  ksp-job-api + premier backfill
0.3.5  application backfill/inspection RAW

19. Threat model 0.3.1

Menace Réponse de design
payload hostile trop grand bornes à la construction des contrats RAW avant persistence
raw payload leak via Debug implémentations Debug manuelles/redacted pour conteneurs sensibles
secret provider/endpoint provenance ne contient que des ids/codes sûrs, jamais URL/credential
duplicate race write idempotent atomique, jamais has_* comme précondition
même clé / contenu divergent conflit explicite, pas d'overwrite silencieux
partial RAW + observation opération persist_*_acquisition atomique au contrat
cursor hostile taille/limit bornées; cursor opaque non interprété par consumer
query non bornée aucune liste publique sans PageRequest borné
backend leak aucun type driver/row/table/SQL dans la façade
backend error leak mapping vers erreurs KSP sûres; source externe auditée avant chaînage
closed-world backend contrat implémentable hors ksp-store-lib
closed-world RAW family types/familles extensibles sans JSON fourre-tout
cross-backend mismatch conformance suite commune sur chaque implémentation
scope creep D2/D3/D4 aucun type CORE/DECODE/SPECIALIZED dans 0.3.1

Les menaces DSN/SQL injection/schema drift/migrations restent documentées pour 0.3.2, où elles deviennent exécutables. Elles ne justifient pas des types SQL dans l'API.

20. Stratégie de tests 0.3.1

Prévoir :

unit tests module-local
public API canaries crate-root
external backend canary
object-safe backend contract canary
manifest/dependency firewall
exact public export inventory
exact production module inventory
bounds payload/provenance/cursor
Debug/redaction tests
idempotence outcome semantics
transaction/log identity validation
atomic-operation contract fake backend
pagination bounds
notification reference contract si introduit
release completeness
cargo tree -p ksp-store-api --edges normal
cargo tree --duplicates

Le backend externe canary doit implémenter le contrat avec une petite implémentation mémoire de test située hors des modules de production. Il prouve :

aucune dépendance à ksp-store-lib
aucune connaissance PostgreSQL
implémentation des mêmes capabilities API
round-trip via le contrat backend commun

Aucun PostgreSQL live test n'appartient à 0.3.1 puisque le backend PostgreSQL n'existe pas encore.

21. Ownership

Concept Owner Visibilité Introduction Raison
RawTransaction ksp-store-api public 0.3.1 modèle persistant commun
RawTransactionObservation ksp-store-api public 0.3.1 provenance/acquisition distincte du RAW
RawLog ksp-store-api public 0.3.1 seconde famille N1 explicitement demandée
RawLogObservation ksp-store-api public 0.3.1 provenance logs
RawPayload ksp-store-api public 0.3.1 contrat persistant replayable versionné
RAW references ksp-store-api public 0.3.1 reads, notification, lineage
write outcomes ksp-store-api public 0.3.1 sémantique idempotente commune
query/page ksp-store-api public 0.3.1 backlog/replay backend-agnostic
health portable ksp-store-api public 0.3.1 consumer commun
backend extension trait ksp-store-api public 0.3.1 implémentation externe réelle
Store facade ksp-store-lib public 0.3.2 point de consommation commun
backend dispatch/settings ksp-store-lib public/privé selon contrat 0.3.2 features disponibles + sélection Config
PostgreSQL settings internes ksp-store-postgres-lib privé ou constructor-boundary 0.3.2 construction du backend de référence
PostgreSQL rows ksp-store-postgres-lib privé 0.3.2 mapping physique
pool/connection ksp-store-postgres-lib privé 0.3.2 détail runtime
SQL/statements ksp-store-postgres-lib privé 0.3.2 détail backend
migrations/schema ksp-store-postgres-lib privé/opérateur 0.3.2 détail backend
transport -> RAW conversion composition/pipeline futur privé/réutilisable selon besoin release acquisition DEP-KSP-003
RAW -> D2 normalization future pipeline générique hors 0.3.1 série suivante aucune dépendance Program
notification mechanism futur host/runtime hors API de diffusion ultérieur Store API possède seulement le format

22. Hors scope strict 0.3.1

ksp-store-lib
PostgreSQL runtime
tokio-postgres dependency
pool/TLS PostgreSQL
SQL/tables/indexes/migrations
std.store Config
backend MySQL/SQLite/Oracle/RocksDB/ClickHouse
Transport acquisition runtime
conversion Transport -> RAW fonctionnelle
D2/CORE structs et persistence
RAW -> D2 normalizer
Program decode
Materializer
processing ledger
worker/job/backfill
application Store/RAW
notification transport concret
retention/archive/purge lifecycle
truncate/drop/maintenance API

23. Prévision souple recalibrée 0.3.1

Chaque tranche vise environ 1520 minutes de travail effectif.

pre.001 — Audit, modèle N1 et split API/backend

Base, règles, architecture, audit kbot3, audit PostgreSQL/driver futur, RAW transactions/logs, ownership, API candidate, threat model, tests et sizing.

pre.002 — Scaffold ksp-store-api

Créer uniquement la crate API, manifest Core-only, façade crate-root et documentation initiale. Aucun modèle fonctionnel lourd.

pre.003 — Primitives RAW communes + transaction

Introduire payload/reference/provenance/idempotence/timestamps bornés puis RawTransaction et son observation.

pre.004 — RAW logs + extensibilité N1

Introduire RawLog/observation, vérifier ordering/identity/bounds et verrouiller l'absence de type RAW fourre-tout.

pre.005 — Capabilities backend extensibles

Implémenter les contrats object-safe et l'external backend canary, sans façade runtime Store et sans runtime DB. La façade commune appartient à ksp-store-lib en 0.3.2.

pre.006 — Queries, pagination, outcomes et notification reference

Finaliser reads/list/backlog bornés, atomic acquisition contract et référence de notification si suffisamment stable.

pre.007 — Adversarial/API hardening + completeness

Payload/cursor/provenance hostile, Debug, external backend, exact exports/modules, dependency firewall et scope négatif D2/D3/D4.

pre.008 — Gate technique final

Workspace complet, tests Store API, graphes Cargo. Aucun développement fonctionnel nouveau.

pre.009 — Réconciliation documentaire finale

README/USAGE, plan, validation et références durables réellement concernées. Aucun CHANGELOG.md, ROADMAP.md ni prompt suivant.

pre.010 — Préparation de publication minimale

Uniquement :

Cargo.toml
CHANGELOG.md
ROADMAP.md
prompts/021-V0_3_2_START_PROMPT.md
delta pre.010

Le prompt 0.3.2 ouvre ensemble ksp-store-lib et ksp-store-postgres-lib, avec feature postgres par défaut et tokio-postgres strictement dans la crate backend.

rel.001 — Publication stable

Mécanique de publication uniquement.

La release reste estimée clôturable dans une session si les tranches conservent cette granularité et si aucun codec/normalizer D2 n'est tiré dans le scope.

24. Trajectoire 0.3.x résultante

La prévision durable devient, sous réserve des gates de chaque release :

0.3.1  ksp-store-api / N1 RAW contract
0.3.2  ksp-store-lib + ksp-store-postgres-lib / PostgreSQL reference via tokio-postgres
0.3.3  generic Interface/wire additions nécessaires à acquisition/normalisation
0.3.4  ksp-job-api + premier backfill RAW concret
0.3.5  application backfill/inspection RAW
ensuite worker/service live RAW avant ouverture D2

Cette renumérotation remplace la prévision 0.3.1..0.3.4 de la base v0.2.14; pre.001-fix.001 l'écrit immédiatement dans le ROADMAP.md pour éviter de poursuivre avec une trajectoire fausse.

25. Critères de fermeture de 0.3.1

La stable 0.3.1 est prête seulement si :

ksp-store-api existe seule dans le domaine Store
modèle objet RAW commun stable
RawTransaction + observations stables
RawLog + observations stables
future extension N1 non bloquée
contrats backend communs stables
backend externe implémentable sans ksp-store-lib
aucun type backend/SQL public
queries/pagination bornées
idempotence/conflict semantics explicites
atomic acquisition contract explicite
références durables stables
notification format introduit ou reporté explicitement
aucune dépendance PostgreSQL
aucune surface D2/D3/D4
workspace et graphes entièrement verts
prompt `0.3.2` cohérent avec ksp-store-lib + ksp-store-postgres-lib + tokio-postgres