102 KiB
Plan 0.5.3 — audit et normalisation de ks-store
1. Statut du document
Ce document constitue le livrable de plan/audit de 0.5.3-pre.001. La numérotation documentaire et des archives conserve trois chiffres (pre.001), tandis que la version SemVer Cargo correspondante est obligatoirement 0.5.3-pre.1 : les identifiants prerelease numériques SemVer ne peuvent pas comporter de zéro initial.
0.5.3-pre.001 ne modifie volontairement aucune table, migration SQL, requête de production ou API Rust de stockage. Les décisions ci-dessous doivent être validées avant la première migration SQL de 0.5.3.
La base auditée est la clôture 0.5.2 fournie dans l'archive finale du workspace.
2. Périmètre et décisions de cadrage
0.5.3 reste un chantier de stockage. Il couvre :
- la normalisation du namespace
kb_sol_* -> k_sol_*; - le gel structurel des tables stabilisées ;
- les temporalités blockchain et de persistance ;
- la provenance, l'idempotence et le replay ;
- la normalisation des migrations, contraintes, index et requêtes ;
- la suppression des fuites PostgreSQL hors de
ks-store; - la mise en place d'une façade de stockage backend-agnostique ;
- la remise à plat du contrat de configuration du backend sans casser les frontières
KS_*/KS_SECRET_*; - la définition des règles additives pour les futurs faits canoniques Solana.
Sont explicitement hors périmètre :
- la réconciliation générale des scénarios/exécuteurs de
0.5.4; - la migration générale des fixtures wallet de
0.5.4; - la régression de fixture Token-2022 reportée à
0.5.4; - l'infrastructure Anchor de
0.6.x; - l'implémentation des DEX de
0.7.x+; - une nouvelle refonte
.kswallet; - le renommage du repository/workspace.
2.1 Décisions structurantes de pre.001
Les décisions suivantes sont retenues pour les tranches suivantes :
- les tables Solana actives passent toutes sous
k_sol_*; aucune table Bot n'est incluse dans cette reconstruction ; - les 13 tables Core/raw/decode/materialization actuelles constituent la baseline conservée : aucune de ces 13 tables n'est fusionnée par principe ;
k_sol_core_instructionsconserve les instructions top-level etk_sol_core_inner_instructionsconserve les CPI. La distinction physique est considérée comme une optimisation et une représentation fidèle des deux sources canoniques Solana, tandis que le contrat de lecture peut fournir une projection logique unifiée aux décodeurs ; des contrats N1/N2/N3 génériques supplémentaires restent possibles si l'audit de readiness en démontre la nécessité avant gel ; - les CPI reçoivent les mêmes capacités de sélection, lifecycle/replay et provenance de processor que les top-level avant gel, afin qu'elles puissent devenir des inputs de niveau 3 autonomes ;
block_timedevient une donnée SQL de premier rang sur la transaction canonique raw et sur l'ancre Core transactionnelle ; il reste nullable et n'est jamais synthétisé ;- les tables filles ne dupliquent pas
block_timelorsqu'il est résoluble efficacement par la transaction Core ; kb_sol_mat_eventsreste le journal générique versionné des sorties de matérialisation, mais son nom durable devientk_sol_mat_outputs, cohérent avec son rôle réel ; les projections métier spécialisées seront additives ;- aucune table DEX/trading n'est créée en
0.5.3: aucun chantier DEX n'a encore fourni les invariants nécessaires à un contrat SQL gelable ; lorsque ces matérialisations arriveront, les tables devront être génériques par fait/concept et non par protocole ; - aucune projection metadata N4 n'est créée en
0.5.3, mais deux cibles conceptuelles sont déjà retenues : asset/token metadata commune Metaplex + Token-2022 et program metadata distincte pour SPM ; elles seront ajoutées lorsque leurs premiers contrats queryables seront suffisamment audités, puis resteront plus évolutives que les fondations N1-N3 ; - les bases Devnet/Mainnet/test actuelles sont considérées destructibles pour
0.5.3: la cible est une reconstruction propre, pas une compatibilité in-place avec le schéma0.5.2; l'ancien schéma doit être détecté/refusé plutôt que silencieusement mélangé au nouveau ; - les ressources SQL PostgreSQL sont rangées sous
ks-store/migrations/postgres/et chaque fichier contient une seule instruction SQL ; les fonctions privées PostgreSQL utilisentinclude_str!pour exécuter la ressource correspondante ; - tables, contraintes/keys, index et opérations de maintenance (
DROP,TRUNCATE) sont séparés en ressources/fonctions explicites et assemblés par un orchestrateur d'initialisation PostgreSQL transactionnel ; - le DDL dupliqué en chaînes Rust disparaît : le fichier SQL inclus est l'unique source du texte SQL exécuté ;
ks-storeexpose une façadeStoreet des contrats fonctionnels backend-agnostiques ;PostgresStore,PgPool, les typesPostgresReplay*et les diagnosticsPostgres*cessent d'être publics ;ks-storene dépend pas deks-config: la frontière applicative transporte une configuration résolue, mais l'interprétation du backend et la création de la connexion sont internes àks-store;- le bloc de configuration backend devient sélectionné et opaque jusqu'à
ks-store, afin queks-confign'impose plus simultanément les paramètres de backends non sélectionnés ; ks-storeexpose un résumé runtime sans secrets destiné notamment àkb-app-demo-desktop: backend actif, état de connexion, options publiques retenues, auto-initialisation, version/état du schéma et tables créées/vérifiées au démarrage ;- les surfaces historiques
demo_sql_*du desktop sont renommées structurellement endemo_store_*dèspre.002; la refonte fonctionnelle finale des tableaux/compteurs reste planifiée plus tard et aucune logique SQL/PostgreSQL ne doit revenir dans l'application ; - toutes les opérations PostgreSQL portent un target de logging sous
ks-store.pg; le SQL brut peut être journalisé au niveautrace, sans valeurs bindées ni secrets, tandis que les niveaux normaux journalisent l'identité d'opération, la durée et le résultat ; - le contrat de page publique abandonne les gros offsets comme mécanisme durable au profit d'une pagination stable/cursorisée pour les lectures qui en ont besoin ; aucune requête UI ne doit pouvoir demander 100 000 lignes en un appel ;
- les index non uniques sont des objets physiques évolutifs, exclus du gel logique des tables ; les PK, FK, uniques, checks, types, nullabilités et sémantiques de colonnes sont gelés ;
- les anciens traits publics sans implémentation réelle sont supprimés ou remplacés avant gel de l'API de crate ;
- le plan de prereleases est une trajectoire, pas une borne numérique rigide :
pre.007est seulement le premier candidat de clôture ; tout manque découvert ajoute une ou plusieurs prereleases et décale la clôture sans forcer une fermeture artificielle ; - le vocabulaire public et nouveau de stockage utilise
top-levelplutôt queouter: les instructions dites historiquementoutersont les instructions top-level detransaction.message.instructions; il n'existe pas de troisième famille physiqueouterà ajouter en plus de top-level et CPI/inner ; - la hiérarchie CPI est un invariant Core à préserver avant gel :
stack_heightdoit être transporté explicitement lorsque disponible etparent_instruction_pathdoit désigner le parent CPI immédiat lorsque la pile permet de le reconstruire, pas seulement l'instruction top-level racine ; - l'architecture de persistance distingue quatre niveaux : N1 raw/acquisition, N2 Core canonique, N3 decode/materialization générique et N4 projections spécialisées/queryables ; les contrats N1/N2/N3 sont fortement gelés après
0.5.3, sauf urgence ou omission structurelle majeure, tandis que N4 reste volontairement plus évolutif ; - les replays inter-niveaux sont des contrats de premier rang : N1 -> N2, N2 -> N3 et N3 -> N4 doivent pouvoir être rejoués indépendamment, de façon idempotente, versionnée et avec invalidation descendante contrôlée ;
k_sol_mat_outputsest conservée impérativement comme journal N3 générique même lorsque des projections N4 existent ; une projection spécialisée n'est jamais un remplacement de la sortie matérialisée canonique/versionnée ;- les metadata auront à terme deux projections N4 conceptuelles distinctes : une projection canonique d'asset/token alimentable par Metaplex Token Metadata et Token-2022, et une projection de metadata de programme pour Solana Program Metadata (SPM) ; elles ne sont pas créées en
0.5.3, mais leur séparation conceptuelle est retenue ; - le desktop ne doit plus exposer le backend concret : les surfaces portent la nomenclature
demo_store_*dèspre.002et seront refondues fonctionnellement vers des résultats tabulaires génériques,demo_configconsommera un résumé store sanitisé sans champs PostgreSQL/SQLite concrets, et le splash vérifiera des modèles logiques avec compteurs d'objets sans afficher les noms physiques des tables/index ; - la façade
Storeouverte au démarrage est destinée à être conservée dans l'état applicatif plutôt que recréée par chaque commande desktop ;ks-storepossède ainsi la connexion, l'initialisation et les diagnostics runtime, tandis que l'application ne conserve qu'une capacité backend-agnostique ; - le gel N1-N3 est conditionné par un canari de compatibilité avec les futurs mécanismes de décodage, Anchor/IDL étant le premier cas concret : l'ajout d'un décodeur futur doit pouvoir se faire sans modification structurelle normale de N1/N2/N3.
0.5.3n'implémente pas Anchor, mais doit vérifier que les niveaux gelés conservent les bytes d'instruction, comptes ordonnés et leurs attributs, hiérarchie CPI, logs, return data lorsqu'elle existe, temporalité, provenance et observations/états de comptes nécessaires à un redécodage ultérieur ; toute omission générique découverte par cet audit doit être corrigée avant gel ; - les 13 tables existantes constituent la baseline auditée, pas un quota de tables finales : leur séparation top-level/CPI est conservée, mais le nombre final peut augmenter en
0.5.3si le canari de readiness démontre qu'un fait Solana générique indispensable manque dans N1/N2/N3. Aucune nouvelle table ne doit être ajoutée pour Anchor en tant que technologie ; seules des structures canoniques Solana indépendantes du mécanisme de décodage sont admissibles.
3. Sources actives auditées
Le plan a été construit à partir des sources actives suivantes :
README.md,ROADMAP.md,CHANGELOG.md,RULES.md;- l'ensemble de
docs/rules/; docs/architecture/ARCHITECTURE.md;docs/architecture/STORAGE_ARCHITECTURE.md;docs/architecture/PIPELINE_ARCHITECTURE.md;docs/architecture/CRATE_MAP.md;docs/architecture/SURFACE_CRATE_MATRIX.md;ks-store/README.md,USAGE.md,TODO.md,CHANGELOG.md;- tout
ks-store/src/; - les migrations
ks-store/migrations/0001à0004; - les scripts SQL de
ks-store/maintenance/; - les DTO, entités, repositories et requêtes PostgreSQL ;
ks-pipelinepour le backfill, Core extraction et decode replay ;ks-pipeline-demo-scenariospour les consommateurs de store ;kb-app-demo-desktoppour les diagnostics/replay SQL ;config/store.config.json, son exemple et ses schémas JSON ;- les guides et scénarios actifs PostgreSQL/Mainnet/Devnet ;
- l'audit historique
0.5.0-pre.003uniquement pour les constats qui n'étaient pas encore formalisés dans l'architecture active.
4. Inventaire exhaustif du schéma actif 0.5.2
Le schéma actif comporte 13 tables de baseline. Ces 13 tables sont conservées et la séparation top-level/CPI est maintenue physiquement ; le nombre final de tables N1/N2/N3 n'est fixé qu'après le canari future-decoder, qui peut justifier l'ajout d'un contrat Solana générique manquant avant gel.
Table 0.5.2 |
Domaine | Rôle actuel | Identité principale | Décision 0.5.3 |
|---|---|---|---|---|
kb_sol_raw_transactions |
raw | document canonique source-indépendant | id, signature unique |
corriger puis geler sous k_sol_raw_transactions |
kb_sol_obs_transaction_observations |
acquisition | provenance d'acquisition | id, observation_key unique |
renommer puis geler |
kb_sol_core_transactions |
Core | ancre transactionnelle normalisée | id, signature unique |
corriger puis geler |
kb_sol_core_account_keys |
Core | clés résolues de transaction | id, (signature, account_index) unique |
renommer puis geler |
kb_sol_core_instructions |
Core | instructions top-level + lifecycle | id, (signature, instruction_path) unique |
corriger puis geler séparément |
kb_sol_core_inner_instructions |
Core | instructions CPI contextuelles | id, (signature, instruction_path) unique |
compléter lifecycle/replay puis geler séparément |
kb_sol_core_logs |
Core | logs ordonnés et liens fiables | id, (signature, log_index) unique |
renommer puis geler |
kb_sol_core_balance_changes |
Core | variations natives/token génériques | id, (signature, balance_change_index) unique |
renommer puis geler |
kb_sol_ops_processing_ledger |
opérations | état courant d'idempotence/reprocessing | (stage, processor_name, processor_version, input_key) unique |
renommer puis geler |
kb_sol_decode_events |
decode | observations décodées versionnées | (processor_name, processor_version, input_key, event_key) unique |
renommer puis geler |
kb_sol_decode_coverage_declarations |
decode | couverture déclarée d'un décodeur/version | identité fonctionnelle composite | renommer puis geler |
kb_sol_decode_coverage_observations |
decode | couverture réellement observée | (processor_name, processor_version, input_key) unique |
renommer puis geler |
kb_sol_mat_events |
materialization | journal générique des sorties versionnées | (processor_name, processor_version, input_key, output_key) unique |
renommer en k_sol_mat_outputs puis geler comme journal |
4.1 Migrations actives
Les quatre fichiers historiques actifs de 0.5.2 sont spécifiques PostgreSQL :
Migration 0.5.2 |
Tables principales | Décision 0.5.3 |
|---|---|---|
0001_canonical_transaction_store.sql |
raw + observations d'acquisition | remplacée par les ressources atomiques PostgreSQL du nouveau baseline |
0002_core_store.sql |
Core transaction/account/instruction/inner/log/balance | remplacée par les ressources atomiques PostgreSQL du nouveau baseline |
0003_processing_ledger.sql |
ledger | remplacée par les ressources atomiques PostgreSQL du nouveau baseline |
0004_decode_materialization_store.sql |
decode/coverage/materialization | remplacée par les ressources atomiques PostgreSQL du nouveau baseline |
Ces fichiers ne sont pas conservés comme chaîne de compatibilité active : les bases Devnet/Mainnet/test doivent être supprimées/recréées avant validation de la nouvelle architecture. Leur historique reste disponible dans Git et les archives de versions précédentes.
Constat important : le runtime reproduit aujourd'hui leur DDL sous forme de chaînes Rust dans ks-store/src/postgres/migrations.rs. Cette duplication est supprimée. En cible, chaque instruction SQL PostgreSQL existe dans un seul fichier sous ks-store/migrations/postgres/ et la fonction Rust correspondante l'embarque par include_str!.
4.2 Maintenance SQL active
Les scripts maintenance/drop_raw_core_store.sql et maintenance/reset_derived_store_keep_raw.sql sont également PostgreSQL-spécifiques et utilisent les noms kb_sol_*.
La cible les remplace par des ressources unitaires rangées avec le backend PostgreSQL, par exemple :
ks-store/migrations/postgres/
schema/
tables/
constraints/
indexes/
maintenance/
truncate/
drop/
Chaque fichier contient une seule instruction SQL. Les noms exacts seront normalisés pendant pre.003, mais la convention fonctionnelle visée est explicite, par exemple :
create_table_if_not_exists_k_sol_raw_transactions.sql
add_constraint_pk_k_sol_raw_transactions.sql
create_index_if_not_exists_ix_k_sol_raw_transactions_slot.sql
truncate_table_k_sol_raw_transactions.sql
drop_table_if_exists_k_sol_raw_transactions.sql
Les fonctions privées PostgreSQL correspondantes sont séparées et utilisent include_str!. L'initialiseur général appelle les opérations nécessaires dans un ordre déterministe et dans une transaction lorsque PostgreSQL le permet.
5. Matrice table -> DTO -> repository/query -> consommateurs
| Table cible | DTO/entités actuels principaux | Contrat/repository | Requêtes | Consommateurs externes |
|---|---|---|---|---|
k_sol_raw_transactions |
RawTransactionInsert, RawTransactionRow, RawPayloadLifecycleMark |
RawTransactionStore, CoreExtractionStore |
raw_queries, core_extraction_queries, replay_candidate_queries |
ks-pipeline::backfill, ks-pipeline::core_extraction |
k_sol_obs_transaction_observations |
TransactionObservationInsert, TransactionObservationRow |
RawTransactionStore |
raw_queries |
ks-pipeline::backfill |
k_sol_core_transactions |
CoreTransactionInsert, CoreTransactionRow |
CoreTransactionStore, CoreExtractionStore |
core_queries, core_extraction_queries, replay |
ks-pipeline::core_extraction, decode context indirect |
k_sol_core_account_keys |
CoreAccountKeyInsert, CoreAccountKeyRow |
CoreTransactionStore |
core_queries, replay |
ks-pipeline::core_extraction, decode replay context |
k_sol_core_instructions |
actuellement CoreInstructionInsert + CoreInnerInstructionInsert et deux rows |
CoreTransactionStore, DecodePipelineStore |
core_queries, decode_pipeline_queries, replay |
ks-pipeline::core_extraction, ks-pipeline::decode_replay |
k_sol_core_logs |
CoreLogInsert, CoreLogRow |
CoreTransactionStore |
core_queries, replay |
Core extraction/decode context |
k_sol_core_balance_changes |
CoreBalanceChangeInsert, CoreBalanceChangeRow |
CoreTransactionStore |
core_queries, replay |
Core extraction/decode context, recherche mint/owner/account |
k_sol_ops_processing_ledger |
ProcessingLedgerIdentity, ProcessingLedgerStatus, ProcessingLedgerRow |
CoreExtractionStore, DecodePipelineStore |
core/decode/replay | Core extraction et decode/materialization |
k_sol_decode_events |
DecodeObservationInsert |
DecodePipelineStore |
decode_pipeline_queries |
ks-pipeline::decode_replay |
k_sol_decode_coverage_declarations |
DecodeCoverageDeclarationInsert |
DecodePipelineStore |
decode_pipeline_queries |
ks-pipeline::decode_replay |
k_sol_decode_coverage_observations |
DecodeCoverageObservationInsert, DecodeCoverageSummaryRow |
DecodePipelineStore |
decode_pipeline_queries |
pipeline + desktop diagnostics |
k_sol_mat_outputs |
MaterializedOutputInsert, MaterializedEventQueryRow |
DecodePipelineStore |
decode_pipeline_queries |
pipeline, scénarios Devnet, desktop |
5.1 Contrats publics hérités sans implémentation
Quatre traits publics actuels n'ont aucune implémentation PostgreSQL et représentent des contrats historiques incomplets :
ProgramObservationStore;DecodedEventStore;MaterializedEventStore;ProcessingLedgerStore.
Leurs DTO historiques (DecodedEventInsert, MaterializedEventInsert, ProcessingLedgerMark et rows associés) sont plus pauvres que les contrats réellement utilisés par DecodePipelineStore et le ledger actuel.
Décision : ne pas les figer. Ils sont supprimés si aucun consommateur n'est découvert au moment du delta d'API, ou remplacés par un contrat réellement implémenté. Un canari d'API externe vérifiera qu'aucune surface morte n'est exportée au crate-root.
6. Audit temporel
6.1 Concepts temporels retenus
Les concepts suivants restent distincts :
slot: ordre blockchain, non convertible en temps civil par hypothèse ;block_time: timestamp Unix fourni par Solana, nullable ;detected_at: première détection locale éventuelle ;received_at: réception du payload source ;normalized_at: fin de normalisation canonique ;persisted_at: insertion de l'observation d'acquisition ;created_at: première création de la ligne logique dans la table concernée ;updated_at: dernière mutation/reprocessing de cette ligne logique ;- temps de matérialisation :
created_at/updated_atdu journalk_sol_mat_outputs, jamais confondu avecblock_time.
6.2 Matrice temporelle cible
| Table cible | slot |
block_time |
acquisition | insertion/update | Justification |
|---|---|---|---|---|---|
k_sol_raw_transactions |
requis | nullable, à ajouter | via table observations | created_at, updated_at |
ancre canonique et requêtes temporelles raw sans parser JSONB |
k_sol_obs_transaction_observations |
nullable | non | detected_at, received_at, normalized_at, persisted_at |
persisted_at |
table de transport/acquisition, pas de duplication du temps blockchain |
k_sol_core_transactions |
requis | nullable, à ajouter | non | created_at, updated_at |
ancre temporelle normalisée des faits Core et des projections dérivées |
k_sol_core_account_keys |
requis | via transaction | non | created_at |
duplication de block_time inutile |
k_sol_core_instructions |
requis | via transaction | non | created_at, updated_at |
lifecycle mutable, temps blockchain résolu par parent |
k_sol_core_logs |
requis | via transaction | non | created_at |
même ancre |
k_sol_core_balance_changes |
requis | via transaction | non | created_at |
même ancre |
k_sol_ops_processing_ledger |
non imposé | non | non | started_at, finished_at, created_at, updated_at |
temps de traitement uniquement |
k_sol_decode_events |
requis | via transaction | non | created_at, updated_at |
éviter duplication ; résolution par signature/Core |
k_sol_decode_coverage_declarations |
non | non | non | created_at, updated_at |
contrat de processeur, pas un fait blockchain |
k_sol_decode_coverage_observations |
requis | via transaction | non | created_at, updated_at |
observation de traitement |
k_sol_mat_outputs |
requis | via transaction | non | created_at, updated_at |
journal de matérialisation, pas source du temps on-chain |
6.3 Source de vérité de block_time
ks-lib::MdCanonicalTransaction.block_time: Option<i64> est la source canonique.
Règles :
RawTransactionInsert::from_canonical()doit transporter explicitement cette valeur ;CoreTransactionInsertdoit recevoir la même valeur pendant Core extraction ;NoneresteNULL;- aucune interpolation à partir du slot, du temps d'acquisition ou de
NOW(); - lorsqu'une transaction Core possède
raw_transaction_id, un test vérifie l'égalité des deuxblock_time; - le replay DTO
MdCoreInstructionReplayInputdoit évoluer vers un contrat incluantblock_time: Option<i64>afin que les matérialiseurs futurs puissent produire des séries temporelles sans requête secondaire.
7. Audit structurel table par table
7.1 kb_sol_raw_transactions -> k_sol_raw_transactions
Propriétaire : ks-store, domaine canonique Solana raw.
Rôle durable : conserver l'identité et le document source-indépendant canonique d'une transaction, ainsi que son état de rétention et sa progression globale.
Correction avant gel :
- ajouter
block_time BIGINT NULL; - transporter la valeur depuis
MdCanonicalTransaction; - documenter
created_atcomme première insertion en base etupdated_atcomme dernière mutation de lifecycle/reprocessing ; - conserver
signatureunique comme clé d'idempotence ; - conserver le hash/version canonique ;
- ne pas ajouter provider/endpoint : cette provenance appartient à
k_sol_obs_transaction_observations.
État : à corriger, puis stable/frozen.
7.2 kb_sol_obs_transaction_observations -> k_sol_obs_transaction_observations
Rôle durable : journaliser une observation d'acquisition sans conserver inutilement le payload provider-spécifique.
Le contrat temporel est déjà le plus riche du schéma. observation_key fournit l'idempotence ; raw_transaction_id relie éventuellement la transaction canonique ; provider/protocole/méthode/origin/session/filter et hash de payload fournissent la provenance d'acquisition.
Correction logique requise : aucune colonne identifiée. Le renommage du namespace, des contraintes/index et la documentation des sémantiques suffisent.
État : stable/frozen après migration de namespace.
7.3 kb_sol_core_transactions -> k_sol_core_transactions
Rôle durable : ancre normalisée minimale d'une transaction Core : identité, slot, temps on-chain, état d'exécution et lineage raw.
Correction avant gel :
- ajouter
block_time BIGINT NULL; - conserver
failed+err_json; - conserver
raw_transaction_idnullable pour lineage ; - ne pas élargir cette table avec toutes les métriques de runtime uniquement parce qu'elles existent dans le JSON canonique.
Les frais, compute units, cost units, rewards ou futures métriques peuvent être représentés par de nouvelles projections Core reliées à transaction_id si un besoin métier/query stable le justifie. Cette séparation évite de remodeler l'ancre gelée.
État : à corriger, puis stable/frozen.
7.4 kb_sol_core_account_keys -> k_sol_core_account_keys
Le contrat (signature, account_index) est stable et couvre les clés statiques et chargées. Les flags signer/writable et l'executable nullable sont conservés.
slot est une dénormalisation volontaire facilitant les requêtes ; sa cohérence avec la transaction parent doit être testée.
État : stable/frozen après renommage.
7.5 kb_sol_core_instructions + kb_sol_core_inner_instructions
La séparation physique est finalement conservée.
Elle représente deux sources canoniques différentes :
- les instructions du message transactionnel top-level ;
- les instructions internes/CPI produites pendant l'exécution et rapportées dans les metadata.
Dans un pipeline destiné à convertir un volume important de blockchain raw -> Core, cette séparation garde des tables et index plus petits pour les recherches top-level, évite une colonne parent nullable sur toutes les lignes et permet d'optimiser séparément les volumes CPI. Une table unique simplifierait certaines requêtes, mais ne fournit pas un gain de performance intrinsèque ; les lectures globales peuvent être exposées par une projection logique UNION ALL backend-interne ou par deux flux fusionnés derrière le repository générique.
La faiblesse actuelle n'est donc pas l'existence de deux tables. Elle est l'asymétrie de contrat :
- seules les top-level possèdent aujourd'hui
processing_state, processor et lifecycle ; - seules les top-level sont sélectionnées directement comme inputs de decode ;
- les CPI sont principalement chargées comme contexte ;
stack_height, déjà disponible dans le modèle canonique, n'est pas une colonne Core dédiée.
Avant gel :
k_sol_core_instructionsreste la table top-level ;k_sol_core_inner_instructionsreste la table CPI ;- les deux tables doivent pouvoir participer au niveau 3 avec un lifecycle/replay équivalent ;
stack_heightdoit être audité comme colonne Core nullable sur les deux contrats, car il existe déjà dansMdCanonicalCompiledInstructionet peut être utile à la reconstruction de l'invocation ;parent_instruction_pathreste spécifique aux CPI et doit pointer vers le parent CPI immédiat lorsque l'ordre +stack_heightpermettent de reconstruire la pile ; pour une CPI directement appelée par la top-level, il pointe vers cette top-level ;- l'identité unique reste
(signature, instruction_path)dans chaque table ; - un DTO/read-model générique peut exposer
instruction_kind,instruction_path,parent_instruction_path: Option<_>et les champs communs sans imposer une table physique unique.
Le repository public doit permettre :
- sélection top-level seule ;
- sélection CPI seule ;
- sélection logique de toutes les instructions ;
- pagination/ordre déterministe communs lorsque les deux catégories sont demandées.
Le choix de deux tables physiques est interne au backend. Un futur backend peut utiliser une représentation différente tant qu'il respecte le même contrat fonctionnel. Le terme historique outer ne crée aucune troisième catégorie : dans les nouveaux contrats de ks-store, il est remplacé par top-level.
La readiness des futurs décodeurs impose en plus que payload_json/le modèle Core transportent sans perte la donnée binaire canonique de l'instruction et que accounts_json préserve l'ordre exact des comptes résolus et les informations nécessaires pour retrouver leurs attributs signer/writable. Cet audit doit être fait sur les top-level et les CPI, sans ajouter de champ nommé d'après Anchor.
État : les deux tables sont à corriger, puis stable/frozen séparément.
7.6 kb_sol_core_logs -> k_sol_core_logs
Le log reste une observation ordonnée de transaction. instruction_path et program_id sont nullable car le lien ne doit être renseigné que lorsqu'il est déterminé de manière fiable.
Les logs sont aussi une donnée de décodage générique et ne doivent pas être traités comme de simples diagnostics jetables. Le canari future-decoder doit vérifier qu'ils restent suffisamment fidèles et ordonnés pour permettre notamment le décodage ultérieur d'événements produits dans les logs.
Aucune colonne block_time n'est dupliquée.
État : stable/frozen après renommage et validation de fidélité.
7.7 kb_sol_core_balance_changes -> k_sol_core_balance_changes
Le rôle reste une représentation Core générique des variations natives/token, pas une table métier de balances courantes ni de trades.
Les montants JSON permettent de conserver les représentations exactes actuelles sans imposer prématurément un schéma de trading. Les futurs facts token/portfolio utiliseront des tables additives avec types numériques explicitement choisis.
Correction physique identifiée : index partiel manquant sur owner, car les requêtes replay actuelles l'agrègent déjà.
État logique : stable/frozen après renommage. L'index owner n'est pas une modification du contrat logique.
7.8 kb_sol_ops_processing_ledger -> k_sol_ops_processing_ledger
Rôle durable : état courant d'idempotence d'un couple stage/processor/version/input, pas journal immuable de tous les essais historiques.
La contrainte unique actuelle est conservée. input_hash est mutable lors d'un nouveau traitement de la même identité ; attempt_count et les timestamps décrivent l'état agrégé courant.
Si un audit exhaustif des tentatives devient nécessaire, il devra utiliser une nouvelle table append-only processing_attempts ou équivalent, reliée au ledger, sans remodeler ce contrat.
Correction physique identifiée : un index par (stage, input_key, updated_at, id) est justifié par la requête de dernier état utilisée dans les candidats de replay.
État : stable/frozen après renommage et documentation.
7.9 kb_sol_decode_events -> k_sol_decode_events
La table possède déjà : processor/version, input key/hash, event key, signature/slot/path/program, protocol/surface/event, preuve, payload, état de transaction et commitment logique.
Elle est une projection versionnée remplaçable pour un même processor/version/input/event, pas un historique immutable de toutes les exécutions.
block_time est résolu par signature -> k_sol_core_transactions et n'est pas dupliqué.
État : stable/frozen après renommage.
7.10 kb_sol_decode_coverage_declarations -> k_sol_decode_coverage_declarations
La table décrit le contrat machine-readable d'un décodeur/version. Elle n'est pas temporelle blockchain.
L'identité fonctionnelle composite existante est conservée.
État : stable/frozen après renommage.
7.11 kb_sol_decode_coverage_observations -> k_sol_decode_coverage_observations
La table décrit la couverture réellement observée pour un processor/version/input et conserve hash, signature, slot, path, program, classification, compteurs et état transactionnel.
block_time est résolu via la transaction Core.
État : stable/frozen après renommage.
7.12 kb_sol_mat_events -> k_sol_mat_outputs
Le contenu actuel est un journal générique versionné de sorties de matérialisation, et non une table métier unique capable de servir toutes les futures requêtes.
Il doit être conservé pour :
- audit de sortie ;
- replay ;
- diagnostics ;
- validation des matérialiseurs ;
- transition vers des projections canoniques spécialisées.
Le nom mat_outputs est retenu avant gel car il correspond au DTO MaterializedOutputInsert et évite de qualifier abusivement toute projection de « event ».
La provenance source reste la composition :
- matérialiseur :
processor_name,processor_version,input_key,input_hash,output_key; - décodeur source :
source_decoder_name,source_decoder_version,source_decode_input_key,source_event_key; - transaction :
signature,slot; - famille canonique :
materialized_family.
Pas de block_time dupliqué : il est joignable par signature.
État : à corriger pour le nom/contrat de requête, puis stable/frozen comme journal générique. Les futures projections métier ne remplacent pas ce journal ; elles lui sont parallèles.
8. Matrice de gel structurel
Table 0.5.2 |
État avant travaux | Transformation | État final attendu |
|---|---|---|---|
kb_sol_raw_transactions |
à corriger | block_time + namespace |
k_sol_raw_transactions frozen |
kb_sol_obs_transaction_observations |
stable | namespace | frozen |
kb_sol_core_transactions |
à corriger | block_time + namespace |
frozen |
kb_sol_core_account_keys |
stable | namespace | frozen |
kb_sol_core_instructions |
à corriger | namespace + stack_height/lifecycle/replay cohérents |
table top-level frozen |
kb_sol_core_inner_instructions |
à corriger | namespace + stack_height + lifecycle/replay équivalents |
table CPI frozen |
kb_sol_core_logs |
stable | namespace | frozen |
kb_sol_core_balance_changes |
stable | namespace + index physique owner | frozen |
kb_sol_ops_processing_ledger |
stable | namespace + index physique replay | frozen |
kb_sol_decode_events |
stable | namespace | frozen |
kb_sol_decode_coverage_declarations |
stable | namespace | frozen |
kb_sol_decode_coverage_observations |
stable | namespace | frozen |
kb_sol_mat_events |
à corriger | renommage conceptuel mat_outputs |
k_sol_mat_outputs frozen journal |
Aucune table n'est déclarée frozen dans le code ou la documentation finale avant que les migrations, DTO, queries, tests PostgreSQL et snapshot de contrat ne concordent.
9. Politique d'évolution après gel
Le gel logique comprend :
- nom de table ;
- nom et sémantique des colonnes ;
- type SQL logique ;
- nullabilité ;
- PK ;
- FK ;
- contraintes uniques ;
- checks métier ;
- unité et source de vérité.
Ne font pas partie du gel logique :
- index non uniques ;
- statistiques PostgreSQL ;
- paramètres de vacuum/analyze ;
- taille du pool ;
- plans de requêtes ;
- options physiques de backend ne changeant pas le contrat de données.
Après 0.5.3, un nouveau besoin fonctionnel doit créer une nouvelle table ou projection reliée au contrat gelé. Toute exception au gel doit être considérée comme une migration de contrat majeure explicitement documentée, et non comme une correction opportuniste.
10. Provenance
10.1 Chaîne canonique
La résolution de provenance cible est :
observation d'acquisition
-> raw transaction canonique
-> transaction/instruction Core
-> observation décodée
-> sortie générique de matérialisation
-> projection métier additive éventuelle
10.2 Répartition de la provenance
| Information | Propriétaire canonique |
|---|---|
| provider/endpoint/protocole/méthode d'acquisition | k_sol_obs_transaction_observations |
| signature/slot/block time/document/hash/version canonique | k_sol_raw_transactions |
| relation normalisée de transaction | k_sol_core_transactions |
| program/path/payload/context | k_sol_core_instructions + tables Core liées |
| decoder/version/input hash/event/proof | k_sol_decode_events |
| couverture déclarée/observée | tables coverage |
| matérialiseur/version/input/output | k_sol_mat_outputs |
| source decoder d'une matérialisation | k_sol_mat_outputs via identité composite du décodeur |
| état courant d'un traitement | k_sol_ops_processing_ledger |
Aucun provider n'est ajouté aux tables decode/mat : il reste résoluble par la transaction et les observations d'acquisition.
11. Idempotence, replacement et replay
11.1 Identités actuelles à préserver
| Domaine | Identité |
|---|---|
| raw | signature unique |
| acquisition | observation_key unique |
| Core transaction | signature unique |
| Core account | signature + account index |
| instruction unifiée | signature + instruction path |
| log | signature + log index |
| balance change | signature + balance change index |
| ledger | stage + processor + processor version + input key |
| decode event | processor + version + input key + event key |
| coverage declaration | processor + version + program + surface + kind + code + discriminator |
| coverage observation | processor + version + input key |
| materialized output | processor + version + input key + output key |
11.2 Garanties actuelles
Les persistance Core/decode/materialization utilisent des transactions PostgreSQL et des remplacements déterministes. Le ledger stocke input_hash et processor_version, ce qui permet de déterminer si un résultat est courant pour une identité précise.
11.3 Lacunes de replay identifiées
Deux invalidations descendantes doivent être explicitement corrigées :
- replay Core forcé : le remplacement du graphe Core peut rendre des observations decode/materialization existantes obsolètes ;
- replay decode forcé : le remplacement des observations d'un décodeur peut laisser temporairement des sorties de matérialisation calculées sur l'ancienne observation.
Politique cible :
- le remplacement d'un étage invalide dans la même transaction les projections descendantes qui dépendent directement de l'identité remplacée ;
- le ledger descendant n'est considéré courant que si son
input_hashcorrespond au nouvel input ; - une instruction retirée par un replay Core ne doit pas conserver une projection materialized présentée comme courante ;
- le lifecycle de l'instruction doit pouvoir revenir de
materializedvers l'état cohérent avec le replay lorsque les sorties sont invalidées ; - les sorties d'anciennes versions de processors ne sont pas confondues avec les sorties de la version active ;
- aucune cascade n'est basée sur un parsing fragile de noms de Program IDs/protocoles.
Les détails d'historisation complète des tentatives ne sont pas ajoutés au ledger courant : une future table d'audit append-only restera possible.
12. Audit des index par rapport aux requêtes réelles
12.1 Index déjà correctement justifiés
- raw signature unique ;
- raw slot ;
- raw processing state ;
- hash canonique partiel ;
- observation signature/slot/provider+method/received/status ;
- account key ;
- instruction program ;
- log program/path ;
- balance mint/account ;
- decode signature/path, program/surface/event, famille/commit ;
- coverage program/processor ;
- ledger input hash.
12.2 Index manquants ou mal alignés identifiés
Les index suivants sont des candidats justifiés par des requêtes déjà présentes, à confirmer avec EXPLAIN (ANALYZE, BUFFERS) sur une base représentative :
k_sol_core_balance_changes(owner)partiel : les résumés replay interrogent déjàowner;k_sol_ops_processing_ledger(stage, input_key, updated_at DESC, id DESC): la requête de candidat cherche le dernier ledger par input ;k_sol_mat_outputs(processor_name, materialized_family, slot DESC, id DESC): la liste matérialisée filtre et trie déjà ainsi ;k_sol_raw_transactions(processing_state, slot, signature): sélection Core extraction ;- sur
k_sol_core_instructions, index de replay autour de(processing_state, slot, signature, instruction_path)et de(program_id, slot, signature, instruction_path); - sur
k_sol_core_inner_instructions, index équivalents pour lifecycle/replay CPI ; - index
(signature, parent_instruction_path)surk_sol_core_inner_instructionspour reconstruire efficacement un sous-arbre CPI.
12.3 Index non retenus en 0.5.3
- pas d'index JSONB générique sur tous les payloads ;
- pas d'index protocole/DEX spéculatif ;
- pas de trigram sur toutes les signatures/adresses par défaut ;
- pas d'index sur des champs de futures projections non encore produites.
Les recherches contains restent des surfaces de diagnostic bornées ; si elles deviennent volumineuses, un index spécialisé sera ajouté comme optimisation physique sans casser le gel logique.
13. Pagination et limites
13.1 Constat
PageRequestexpose actuellementlimit + offsetavec maximum 1 000 ;- la liste de matérialisations est limitée à 500 ;
- les filtres
PostgresReplay*autorisent jusqu'à 100 000 lignes ; - les grands offsets deviennent coûteux et sont instables face à des insertions concurrentes.
13.2 Cible
- aucune surface UI/diagnostic publique ne retourne 100 000 lignes en une requête ;
- les lectures interactives utilisent un maximum commun de 500 lignes ;
- les campagnes de traitement utilisent des batches explicitement bornés et répétés, distincts d'une API UI ;
- les listes paginées utilisent une continuation stable fondée sur une clé d'ordre canonique, par exemple
(slot, signature, instruction_path)pour les instructions ; - un éventuel token de continuation reste opaque pour le consommateur, afin de ne pas exposer un identifiant PostgreSQL comme contrat backend-agnostique ;
- l'ordre comprend toujours une clé de départage unique.
PageRequest.offset n'est pas retenu comme contrat frozen de long terme.
14. u64 et PostgreSQL BIGINT
14.1 Slots
Les DTO Solana restent en u64. PostgreSQL conserve BIGINT pour les slots avec :
- contrainte SQL
slot >= 0; - conversion checked
i64::try_from(u64)à l'entrée ; - conversion checked
u64::try_from(i64)à la sortie ; - erreur structurée si la valeur excède
i64::MAX; - tests aux bornes
0,i64::MAX,i64::MAX + 1.
Ce choix est accepté pour le slot : la limite i64::MAX est physiquement hors d'atteinte de la durée de vie réaliste de Solana, tout en permettant un mapping PostgreSQL simple et indexable.
14.2 Valeurs monétaires futures
Cette décision ne doit pas être copiée automatiquement aux montants SPL ou aux métriques financières. Un montant canonique u64 susceptible d'utiliser toute sa plage doit utiliser un type SQL capable de conserver l'intégralité de la valeur, par exemple NUMERIC(20,0), ou une représentation exacte explicitement documentée.
Aucun prix, volume ou montant financier durable ne doit être stocké en FLOAT/DOUBLE PRECISION par défaut.
15. Stratégie kb_sol_* -> k_sol_*
15.1 Principe
La migration ne fait pas un simple remplacement textuel. Elle doit couvrir :
- tables ;
- contraintes PK/FK/check ;
- index uniques et non uniques ;
- séquences
BIGSERIAL; - constantes Rust ;
- requêtes ;
- diagnostics ;
- fixtures ;
- maintenance SQL ;
- tests ;
- documentation active ;
- chaînes visibles du desktop.
15.2 Base vide
Une base vide est la cible normale de 0.5.3.
L'auto-initialisation PostgreSQL exécute l'orchestrateur des ressources SQL unitaires dans un ordre déterministe : tables, contraintes, index, puis vérification du snapshot logique. Le résultat final ne doit contenir aucun objet actif kb_sol_*.
15.3 Bases 0.5.2 Devnet/Mainnet/test
La décision de session est de supprimer et recréer les bases actuelles. 0.5.3 n'investit donc pas dans une migration in-place des données historiques.
Le comportement attendu est :
- si la base est vide, initialiser le nouveau schéma ;
- si le nouveau schéma
k_sol_*est déjà conforme, vérifier puis continuer ; - si des objets historiques
kb_sol_*sont détectés, refuser l'auto-initialisation avec un diagnostic non destructif demandant une reconstruction explicite ; - ne jamais mélanger silencieusement ancien et nouveau namespace.
Les opérations DROP/TRUNCATE restent disponibles comme maintenance backend-interne explicite ; elles ne sont pas déclenchées automatiquement par Store::open() sur une base non vide.
15.4 Conséquence sur les tests
Le test de migration 0.5.2 -> 0.5.3 n'est plus requis. Il est remplacé par :
- init d'une base vide ;
- réouverture idempotente d'une base déjà initialisée ;
- refus propre d'un schéma historique
kb_sol_*; - reconstruction explicite via les opérations de maintenance retenues ;
- snapshot exact du schéma final.
Cette stratégie est acceptable parce que les bases actuelles ne constituent pas encore des contrats de production à préserver.
16. Source de vérité des migrations PostgreSQL
16.1 Problème actuel
Le DDL existe aujourd'hui simultanément :
- dans
ks-store/migrations/*.sql; - dans
ks-store/src/postgres/migrations.rssous forme de chaînes Rust.
Les fichiers sont en outre PostgreSQL-spécifiques alors que leur emplacement ne l'indique pas.
16.2 Arborescence cible
Les ressources deviennent explicitement backend-private :
ks-store/migrations/postgres/
schema/
tables/
constraints/
indexes/
maintenance/
truncate/
drop/
Un futur backend aura son propre arbre (migrations/sqlite/, migrations/mysql/, etc.) sans modifier les consommateurs.
16.3 Une instruction SQL par fichier
Chaque ressource contient une seule instruction SQL. Le code Rust ne reconstruit pas le SQL et ne concatène pas du DDL.
Exemple de principe :
fn create_pg_table_if_not_exists_k_sol_raw_transactions_sql() -> &'static str {
return include_str!("../../../migrations/postgres/schema/tables/create_table_if_not_exists_k_sol_raw_transactions.sql");
}
La fonction d'exécution correspondante reste privée au backend et appelle la ressource. Le nom exact des fonctions sera ajusté aux règles de taille/naming du workspace pendant l'implémentation, mais le principe 1 ressource -> 1 fonction -> 1 opération est retenu.
16.4 Orchestration
Un initialiseur PostgreSQL privé :
- détient l'ordre d'exécution ;
- ouvre la transaction d'initialisation ;
- appelle les fonctions de tables/keys/index ;
- produit un rapport d'initialisation sans secrets ;
- vérifie le contrat final ;
- ne laisse pas de schéma partiellement initialisé après un échec transactionnel.
Les opérations DROP et TRUNCATE sont séparées de l'auto-init et ne sont jamais appelées implicitement sur une base existante.
16.5 Historique futur
Puisque 0.5.3 reconstruit le baseline avant gel, les anciens 0001 à 0004 n'ont plus à rester dans la chaîne active. Après gel, les futures évolutions seront additives et devront disposer d'un mécanisme backend-interne de version/schema state permettant de distinguer le baseline 0.5.3 des extensions ultérieures. Le choix précis de la table d'état interne est réalisé avant clôture sans l'exposer dans l'API publique.
17. Frontière backend-agnostique
17.1 État actuel
ks-store respecte déjà plusieurs bonnes frontières :
- les modules
contractsetpostgressont privés ; ks-pipelineorchestre déjà viaRawTransactionStore,CoreExtractionStoreetDecodePipelineStore;- aucune autre crate ne dépend directement de
sqlx; - aucun
sqlx::PgPooln'est utilisé hors deks-store.
Les fuites actuelles sont au crate-root de ks-store et dans les consommateurs de setup/diagnostic :
PostgresStore;PostgresStoreOptions;PostgresBackendDiagnostics;PostgresTableDiagnostics/PostgresTableStatistics;PostgresReplay*;PostgresStore::pool();PostgresStore::from_pool();- fonctions/constants explicitement PostgreSQL exposées.
Les principaux consommateurs sont ks-pipeline-demo-scenarios et kb-app-demo-desktop. ks-pipeline lui-même n'est pas à réécrire autour de PostgreSQL.
17.2 Façade cible
Le crate-root expose :
Store: handle clonable backend-agnostique ;StoreOpenOptions: configuration d'ouverture sansDebugrévélant des secrets ;StoreBackendDescriptor/StoreBackendDiagnosticsgénériques ;StoreTableDiagnostics/StoreTableStatisticsgénériques ;ReplayTransactionFilter,ReplayProgramFilter,ReplayEntityFilteret rows génériques ;- les repositories fonctionnels actuels réellement implémentés ;
- les DTO/models de persistance réellement nécessaires.
Store délègue en interne à un backend privé, par trait-object ou composition équivalente. Le choix exact doit éviter de dupliquer un match backend dans chaque consumer. Un enum de dispatch est acceptable à l'intérieur de ks-store, pas dans les crates consommatrices.
17.3 Interdictions finales
Hors ks-store, les canaris doivent interdire :
sqlx::;PgPool;PostgresStore;PostgresStoreOptions;PostgresReplay;postgres::dans un cheminks_store;- accès à un pool ou à une query SQL concrète.
18. Configuration et possession de la connexion
18.1 Constat actuel
DatabaseConfig dans ks-config contient simultanément :
backend;- une section
postgres; - une section
sqlite.
La validation exige actuellement que les deux sections soient valides, même lorsque l'une n'est pas sélectionnée. Les consommateurs construisent ensuite directement PostgresStoreOptions.
18.2 Contrat cible
Le document store doit représenter :
enabled
backend: <code>
backend_options: <objet opaque pour ks-config>
Principes :
ks-configcharge, compose, applique les overrides et conserve le bloc sélectionné sans en interpréter la connexion ;- le backend code est un texte non vide, pas un enum public devant être modifié à chaque nouveau moteur ;
ks-store::Store::open()reconnaît le backend et valide ses options ;- un backend inconnu produit une erreur structurée ;
- un backend connu mais incomplet produit une erreur structurée sans reproduire de secret ;
- PostgreSQL reste le seul backend opérationnel exigé en
0.5.3; - la présence d'un exemple SQLite ne signifie pas qu'il est implémenté ;
- le passage futur à MySQL/SQLite/RocksDB/Oracle ne change pas l'orchestration
ks-pipeline.
18.3 Sécurité
StoreOpenOptionsne dérive pasDebugsi son contenu peut révéler un secret ;- le backend interne PostgreSQL ne dérive pas un
Debugcontenant l'URL brute ; - le descriptor public expose uniquement un
masked_connection_descriptorgénérique, pasmasked_dsncomme nom de contrat global ; - les erreurs de connexion ne réinjectent jamais l'URL/DSN ou le password ;
KS_SECRET_POSTGRES_*reste la convention actuelle pour les URLs sensibles PostgreSQL ;- aucune fixture versionnée n'embarque de credential réel.
18.4 Résumé runtime public sans secrets
ks-store doit fournir aux consommateurs, notamment kb-app-demo-desktop, un résumé générique permettant d'afficher l'état réel sans connaître PostgreSQL.
Le contrat visé couvre au minimum :
- store activé/désactivé ;
- code backend actif ;
- descriptor de connexion masqué ou label public ;
- état de connexion/health ;
- auto-initialisation activée ou non ;
- version/identité du contrat de schéma ;
- état d'initialisation ;
- vérification par modèle logique (
Raw,Core,Processing/Decode,Materializationou équivalent final) ; - compteurs d'objets attendus, présents, créés et vérifiés, notamment tables et index lorsque le backend possède ces notions ;
- éventuelles actions de migration/init exécutées, identifiées sans SQL sensible ni paramètres secrets.
Le résumé public n'expose pas les noms physiques des tables, index ou contraintes. Ces détails restent backend-internes et peuvent être tracés sous le target canonique ks-store avec les champs structurés backend="postgres", domain="ks-store.pg" et action="migration". Un backend non relationnel peut produire le même résumé logique avec ses propres objets physiques.
Les noms exacts (StoreRuntimeSummary, StoreInitializationSummary, StoreModelVerificationSummary, etc.) seront figés lors de pre.002. Le desktop consomme uniquement ces DTO génériques. Les panneaux portent la nomenclature demo_store_* dès pre.002; leur présentation tabulaire finale sera refondue en pre.005. demo_config ne doit plus lire directement la configuration PostgreSQL/SQLite. Le même résumé logique alimente les informations store du splash.
18.5 Logging PostgreSQL
Le target Rust reste l’unique crate::TRACING_TARGET de la crate, soit ks-store, conformément aux règles du workspace. Le backend PostgreSQL est distingué par des champs structurés stables : backend = "postgres", domain = "ks-store.pg" et action = ....
Règles :
- toute exécution SQL PostgreSQL est traçable sous
target: crate::TRACING_TARGETavecbackend = "postgres"etdomain = "ks-store.pg"; info/debugjournalisent l'identité d'opération, le temps d'exécution, le nombre de lignes et l'issue ;- le texte SQL brut peut être journalisé à
tracepour le diagnostic ; - les valeurs bindées ne sont pas journalisées par défaut ;
- DSN, password, tokens et secrets ne sont jamais inclus ;
- l'abstraction publique
Storepeut conserver un targetks-storegénérique pour les événements indépendants du backend.
19. API générique : matrice cible
| DTO/model fonctionnel | Contrat public | Implémentation 0.5.3 |
|---|---|---|
| raw transaction / observation | RawTransactionStore |
adapter PostgreSQL privé |
| Core transaction/graph | CoreTransactionStore |
adapter PostgreSQL privé |
| sélection/persistence Core extraction | CoreExtractionStore |
adapter PostgreSQL privé |
| lecture logique top-level/CPI | read-model/repository Core générique | UNION ALL ou flux fusionnés internes PostgreSQL |
| decode/coverage/materialization | DecodePipelineStore |
adapter PostgreSQL privé |
| santé/init/diagnostics | StoreHealthStore + résumés génériques |
adapter PostgreSQL privé |
| candidats replay | repository/operations génériques de replay | requêtes PostgreSQL privées |
| ouverture/connexion | Store::open(StoreOpenOptions) |
factory interne PostgreSQL |
Le consumer ne reçoit jamais un type de connexion concret.
19.1 Architecture de flux à préserver
Le stockage doit rester compatible avec la séparation future :
Worker 1 / acquisition
-> observations + raw canonique (niveau 1)
Worker 2 / Core extraction
-> Core exhaustif et source-indépendant (niveau 2)
Dispatcher / decode + materialization
-> observations décodées + `k_sol_mat_outputs` et autres journaux génériques (niveau 3)
Projection / specialization
-> modèles queryables par domaine et fait canonique (niveau 4)
Conséquences :
- le niveau 1 privilégie fidélité, provenance d'acquisition et replay ;
- le niveau 2 doit permettre de reconstruire/inspecter efficacement toute transaction sans dépendre du provider initial ;
- les instructions top-level et CPI appartiennent toutes deux au Core, même si elles restent dans deux tables physiques ;
- le niveau 3 peut traiter top-level ou CPI comme inputs autonomes via l'API logique de
ks-storeet conserve les sorties génériques/versionnées nécessaires à l'audit et au replay ; k_sol_mat_outputsest une fondation N3 durable et n'est jamais supprimée au profit d'une projection spécialisée ;- le niveau 4 contient les projections spécialisées/queryables (metadata, futurs faits de trading, staking spécialisé, compliance, etc.) et reste plus évolutif car de nouveaux Program IDs peuvent révéler de nouveaux invariants ;
- les projections métier ne doivent pas contaminer les tables raw/Core avec des colonnes spécifiques à un DEX, une metadata ou un domaine applicatif.
19.2 Politique de gel par niveau
Après 0.5.3, les niveaux N1, N2 et N3 forment le socle de persistance généraliste et sont fortement gelés : colonnes, types, nullabilités, clés et sémantiques ne changent plus dans le cycle normal. Une évolution n'est acceptable qu'en cas d'urgence, de corruption possible ou d'omission structurelle majeure découverte après clôture, et doit alors être traitée comme une migration exceptionnelle explicitement documentée.
Le niveau N4 n'est pas soumis au même degré de rigidité. Une projection spécialisée peut nécessiter des colonnes, contraintes ou tables supplémentaires lorsque l'arrivée d'un nouveau décodeur/materializer révèle un invariant jusque-là inconnu. L'évolution N4 doit néanmoins rester contrôlée : préférer l'additif, versionner les projecteurs, préserver la provenance N3 et maintenir un chemin de replay permettant de reconstruire la projection.
Le gel logique N1-N3 n'interdit pas l'ajout d'index physiques justifiés, conformément à la politique d'index de ce plan.
19.3 Contrats de replay inter-niveaux
Les chemins suivants sont obligatoires et indépendants :
N1 raw/acquisition
-> N2 Core
N2 Core
-> N3 decode/materialization générique
N3 outputs génériques
-> N4 projections spécialisées
Chaque transition doit disposer d'une identité d'input, d'un processor/projector et de sa version, d'un hash d'entrée lorsque pertinent, d'un état courant et d'un comportement explicite de replacement. Un replay forcé d'un niveau invalide seulement les descendants qui dépendent du résultat remplacé.
Le replay N3 -> N4 doit permettre de reconstruire une projection spécialisée après évolution de son modèle sans redécoder toute la blockchain lorsque le journal N3 contient déjà l'information canonique nécessaire. Si N3 est lui-même insuffisant ou obsolète, le replay repart de N2 ; de même, N2 peut être reconstruit depuis N1.
19.4 Canari de readiness des futurs décodeurs
Le gel N1-N3 n'est autorisé que si un nouveau mécanisme de décodage peut être ajouté sans remodeler normalement ces niveaux. Anchor/IDL est le premier canari concret connu, mais la règle est volontairement générique : N1/N2 doivent conserver les faits Solana, N3 la provenance de décodage/materialization ; aucun niveau ne doit dépendre d'une technologie de decoder particulière.
L'audit 0.5.3 doit vérifier au minimum cinq axes :
- instruction readiness — program id, bytes/data bruts de l'instruction, comptes dans leur ordre exact et résolution de leurs propriétés signer/writable ;
- CPI readiness — mêmes données pour les inner instructions, ordre déterministe,
stack_height, chemin et parent CPI immédiat ; - event readiness — logs ordonnés/fidèles, données CPI et
returnDataSolana lorsqu'elle est réellement disponible dans l'entrée canonique ; aucunereturnDatane doit être synthétisée ; - account readiness — capacité à conserver/rejouer une observation ou un état de compte Solana avec ses bytes de données et sa provenance lorsqu'une source d'acquisition de comptes est utilisée, afin qu'un futur decoder de layouts/discriminators ne dépende pas d'un schéma connu à l'avance ;
- schema provenance N3 — possibilité d'identifier le decoder, sa version et le schéma/IDL/version/hash ayant produit une observation lorsque ce mécanisme est applicable, sans faire de l'IDL une dépendance du modèle Core.
La baseline 0.5.2 possède 13 tables actives, mais elle ne contient pas nécessairement tous les contrats génériques nécessaires à ces cinq axes. En particulier, 0.5.3 doit auditer explicitement la conservation de returnData et des observations/états de comptes : si ces faits ne sont pas représentés de manière générique et rejouable, une nouvelle table N1/N2 canonique peut être ajoutée avant gel. Son nom et son contrat doivent décrire le fait Solana (account observation, account state, transaction return data, etc.), jamais Anchor ou une IDL.
Ce canari ne doit pas dériver vers l'implémentation de 0.6.x : aucun parser Anchor, chargement d'IDL, discriminator Anchor ou table anchor_* n'est ajouté en 0.5.3. Il s'agit uniquement de prouver que les informations génériques nécessaires ne sont pas perdues.
Conséquence sur le snapshot frozen : 13 est le nombre de tables existantes auditées au départ, pas une limite finale. Le snapshot machine-readable de clôture porte le nombre réel de contrats N1/N2/N3 stabilisés après le canari future-decoder.
20. Audit des domaines de faits Solana
Les surfaces déjà implémentées ou réservées dans ks-lib montrent que le store doit rester généraliste. Les domaines observés comprennent notamment :
- transaction/Core ;
- lifecycle ;
- token accounts, mints et extensions SPL/Token-2022 ;
- metadata Metaplex, Token-2022 et Solana Program Metadata ;
- administration/autorités ;
- fees/rewards ;
- staking ;
- compliance/audit ;
- risk ;
- governance ;
- bridge ;
- lending ;
- oracle ;
- NFT ;
- vault ;
- pool/liquidity ;
- orderbook ;
- trades ;
- routing ;
- perpetuals.
Plusieurs de ces matérialiseurs sont encore réservés. Leur présence ne justifie donc pas la création anticipée de tables.
Le registre ks-program-ids contient également de nombreux Program IDs futurs. Un Program ID enregistré n'est pas une preuve d'implémentation et ne devient jamais automatiquement un préfixe de table.
21. Modèles canoniques futurs et niveau de maturité
21.1 Modèles de forte stabilité conceptuelle
Les concepts suivants sont suffisamment stables pour guider l'API future, mais pas encore pour figer toutes leurs colonnes SQL en 0.5.3 :
- identité d'actif/mint ;
- état de token account ;
- metadata d'actif ;
- relation d'autorité ;
- état de programme/upgrade authority ;
- stake/vote state ;
- observation de risque/compliance ;
- marché/venue ;
- pool de liquidité ;
- order book ;
- swap/trade execution ;
- réserve/liquidité ;
- position ;
- observation de prix ;
- candle/OHLC ;
- route multi-hop.
21.2 Metadata : N3 générique et deux projections N4 prévues
Les travaux 0.4.6/0.4.7/0.4.8 ont déjà introduit des matérialiseurs metadata, notamment Metaplex Token Metadata, Token-2022 embedded metadata et Solana Program Metadata. Ils produisent aujourd'hui des sorties de famille Metadata persistables dans le journal générique de matérialisation.
Deux responsabilités doivent rester distinctes :
- N3 — journal matérialisé versionné :
k_sol_mat_outputsconserve impérativement la sortie exacte du matérialiseur, sa provenance, son processor/version et son payload ; - N4 — projections canoniques queryables : tables spécialisées destinées aux lectures métier efficaces et reconstructibles depuis N3.
La cible conceptuelle retient deux familles metadata N4, car les invariants ne sont pas les mêmes :
- asset/token metadata : projection commune alimentable par Metaplex Token Metadata et Token-2022, destinée notamment aux lectures mint/asset, nom, symbole, URI, autorités, mutabilité, collection/standard et autres champs communs ou extensions normalisées ;
- program metadata : projection distincte pour Solana Program Metadata (SPM), dont le sujet est un programme et non un asset/token.
0.5.3 ne crée pas encore ces tables. Leur premier contrat sera défini seulement lorsque la tranche de projection spécialisée disposera de données et d'usages suffisamment vérifiés ; contrairement à N1-N3, ces projections N4 ne seront pas considérées comme immuables au même degré et pourront évoluer de façon contrôlée si de nouveaux décodeurs révèlent des manques. Leur séparation conceptuelle est néanmoins décidée et k_sol_mat_outputs reste la source N3 de replay N3 -> N4. Des tables N4 additionnelles (edition, collection state, group state, etc.) restent possibles si leurs invariants justifient réellement une structure séparée.
21.3 Pourquoi aucune nouvelle table trading n'est créée maintenant
Un schéma de trading gelé avant les DEX créerait des risques de sur-généralisation :
- certains marchés sont pools, d'autres carnets ;
- certains pools ont deux actifs, d'autres structures peuvent être plus générales ;
- CLMM/DLMM introduisent ticks, bins et positions ;
- un router peut produire une route composée de plusieurs exécutions ;
- la sémantique base/quote n'est pas identique à l'ordre token A/token B ;
- les frais et montants nécessitent des unités exactes ;
- une candle doit préciser la source, le quote et l'intervalle.
Créer ces tables en 0.5.3 sans données DEX réelles contredirait le principe de gel. Le résultat de 0.5.3 est donc une frontière prête à recevoir ces tables de manière additive, pas un schéma DEX imaginaire.
22. Priorité trading : contrats conceptuels à préserver
Lorsque les matérialiseurs DEX arrivent, le modèle devra favoriser :
22.1 Marché/paire
Un marché ou venue doit avoir une identité canonique indépendante du nom du protocole. L'ordre des actifs et le rôle base/quote ne doivent pas être supposés universels.
22.2 Pool AMM/liquidity
Un pool porte son identité, ses actifs, réserves et paramètres génériques lorsque ces invariants sont réellement communs. Les courbes/ticks/bins restent dans des tables spécialisées par concept, pas par DEX.
22.3 Order book
Un carnet n'est pas forcé dans le contrat d'un AMM. Si ses invariants sont différents, il possède une table canonique séparée avec niveaux/ordres dans des tables additives appropriées.
22.4 Swap/trade
Un trade canonique conserve au minimum l'identité de la transaction/instruction, les actifs d'entrée/sortie, les quantités exactes, la venue/pool/market lorsque connue et la provenance programme/version. Les noms amount_in, quoteAmount, coin_amount ou équivalents protocole sont normalisés par le matérialiseur.
22.5 Candles/OHLC
Une candle n'est créée qu'après définition stable de :
- l'identité de marché/paire ;
- l'asset quote ;
- l'intervalle ;
- la source de trades ;
- les unités/précisions ;
- le traitement des intervalles vides ;
- la politique de correction après reorg/replay.
23. Matrice de normalisation programme -> fait canonique
| Source / Program ID | Observation décodée | Matérialiseur | DTO canonique futur | Table métier cible |
|---|---|---|---|---|
| System / loaders / native | transfert, create, assign, upgrade, authority | lifecycle/admin | lifecycle/authority/program state | tables par fait, pas system_* |
| SPL Token | mint/account/transfer/burn/authority | token/accounts, lifecycle, admin | token account, mint, token movement, authority | tables token canoniques |
| Token-2022 | mêmes faits + extensions | token/accounts, metadata, risk, fees | mêmes modèles + extensions conceptuelles | tables token/metadata/risk additives |
| Metaplex Token Metadata | metadata/edition/collection/pNFT | metadata | asset metadata/edition/collection state | tables metadata par invariants |
| Stake/Vote | stake/vote actions/state | staking | stake/vote facts | tables staking/vote |
| programmes admin/metadata natifs | program metadata/authority | metadata/admin | program metadata/authority | tables programme/autorité |
| futur Raydium/Meteora/Orca/Pump | pool/swap/liquidity/position | pool/liquidity/trades | market/pool/swap/reserve/position | tables trading canoniques |
| futur orderbook | market/order/fill/level | orderbook/trades | order book/order/fill | tables orderbook/trade |
| futur Jupiter/router | route/quote/execution hops | routing/trades | route + hops/executions | tables routing/trade |
| oracle | price/update | oracle | price observation | table oracle/price canonique |
| governance | proposal/vote/authority | governance/admin | governance facts | tables governance |
| compliance/risk | annotation/risk signal | compliance/risk | audit/risk observation | tables audit/risk |
La colonne « source » reste une provenance. Elle ne détermine pas le nom de la table cible.
24. Interdiction des familles de tables par protocole
Un canari documentaire et de code doit empêcher l'introduction automatique de noms actifs :
meteora_*
raydium_*
pump_*
orca_*
jupiter_*
La même règle s'applique à tout autre protocole : une table nommée d'après un programme n'est acceptable que si le fait lui-même est réellement spécifique et impossible à exprimer par un concept réutilisable.
25. Tests 0.5.3 obligatoires
25.1 Contrat de schéma
- initialisation sur base vide ;
- réouverture idempotente d'une base déjà initialisée ;
- refus propre d'une base contenant encore des objets
kb_sol_*; - absence finale de tables, contraintes, index et séquences
kb_sol_*après reconstruction ; - présence des 13 tables de baseline, plus des éventuels contrats N1/N2/N3 génériques explicitement retenus après le canari future-decoder ;
- snapshot exact des colonnes/types/nullabilités/PK/FK/uniques/checks frozen ;
- présence et contrat séparé des tables top-level/CPI ;
- test de lecture logique combinée top-level+CPI sans perte, doublon ni ordre instable ;
- test
block_time = NULLlorsque absent ; - test de conservation de
block_timelorsqu'il existe ; - test d'égalité raw/Core du
block_timelorsque les deux lignes sont reliées ; - canari vérifiant qu'un fichier SQL PostgreSQL actif contient exactement une instruction du type prévu et qu'il est référencé par l'orchestrateur attendu.
25.2 Idempotence/replay
- insertion raw répétée ;
- observation acquisition répétée ;
- Core extraction répétée ;
- Core replay forcé avec input hash différent ;
- decode replay répété ;
- decode replay forcé invalidant les sorties materialized dépendantes ;
- materialization répétée ;
- replay explicite N1 -> N2 sans duplication ;
- replay explicite N2 -> N3 avec remplacement/version de processor ;
- replay explicite N3 -> N4 avec version de projector et reconstruction d'une projection spécialisée ;
- invalidation descendante bornée : N1 remplace N2/N3/N4 dépendants, N2 remplace N3/N4 dépendants, N3 remplace N4 dépendant sans effacer les niveaux amont ;
- suppression des sorties devenues absentes après remplacement ;
- rollback transactionnel injecté après chaque étage critique ;
- aucun état lifecycle présenté
materializedouprojectedsi la sortie/projection courante correspondante a été invalidée.
25.3 Requêtes/index/pagination
- signature exacte ;
- range de slots ;
- program id top-level et CPI ;
- processor/version/state ;
- account/address ;
- mint ;
- owner ;
- pagination stable sans doublon/saut sur dataset figé ;
- limites refusant 0 et toute valeur au-dessus du maximum de surface ;
- borne
u64 -> BIGINT; - plans
EXPLAINenregistrés pour les index ajoutés lorsque le dataset de test est suffisamment représentatif.
25.4 Frontière backend
- aucune dépendance
sqlxhorsks-store; - aucune référence
PostgresStore*horsks-store; - aucune référence
PgPoolhors adapter privé ; - exports crate-root uniquement backend-agnostiques ;
- unknown backend refusé proprement ;
- backend PostgreSQL incomplet refusé proprement ;
Storedélègue les repositories actuels à PostgreSQL sans changement de comportement.
25.5 Sécurité
Utiliser un secret sentinelle dans les tests et vérifier qu'il n'apparaît jamais dans :
Debugpublic ;Displayd'erreur ;- diagnostics ;
- backend descriptor ;
- erreur de connexion ;
- erreur de validation d'options.
25.6 PostgreSQL réel
Avec KS_SECRET_POSTGRES_TEST_URL local :
- connexion/health ;
- migrations ;
- transaction rollback ;
- replay ;
- diagnostics ;
- introspection du contrat frozen ;
- reconstruction propre d'une base de test et refus contrôlé d'un schéma historique
kb_sol_*.
26. Snapshot de contrat frozen
Le snapshot 0.5.3 couvre les 13 tables existantes de baseline, plus toute structure N1/N2/N3 générique dont le canari future-decoder démontre la nécessité avant gel, ainsi que les tables transverses de processing/decode qui assurent leur idempotence et leur journalisation. Les futures projections N4 disposent de contrats/version propres, mais ne sont pas ajoutées à ce snapshot frozen fondamental.
Une fixture machine-readable sera créée avant clôture, par exemple :
test-fixtures/store/v0.5.3/frozen_schema_contract.json
Elle décrit pour chaque table frozen :
- colonnes dans un ordre canonique ;
- type logique ;
- nullabilité ;
- PK ;
- FK ;
- unique ;
- checks métier ;
- sémantique/unité ;
- source de vérité temporelle/provenance.
Les index non uniques sont consignés séparément et ne provoquent pas une violation de gel lorsqu'une optimisation justifiée est ajoutée plus tard.
27. Documentation cible à la clôture
La dernière prerelease doit réconcilier au minimum :
docs/architecture/STORAGE_ARCHITECTURE.md;docs/architecture/PIPELINE_ARCHITECTURE.mdsi la frontière visible change ;docs/architecture/CRATE_MAP.md/SURFACE_CRATE_MATRIX.mdsi nécessaire ;docs/guides/POSTGRES_STORAGE.md;ks-store/README.md;ks-store/USAGE.md;ks-store/TODO.md;ks-store/CHANGELOG.md;ROADMAP.md;- rapport final
0.5.3; - prompt
0.5.4; - archive du présent plan et du prompt
032.
28. Plan de prereleases borné
Le découpage suivant est une trajectoire fonctionnelle. Il ne force pas la clôture au numéro pre.007. Si une tranche révèle un manque structurel, une ou plusieurs prereleases sont insérées et la prerelease de clôture est décalée.
0.5.3-pre.001 — audit/plan
- inventaire complet ;
- matrices de temporalité/provenance/idempotence/index ;
- matrice de gel ;
- décision de conservation de deux tables Core instructions top-level/CPI avec API logique commune ;
- stratégie reconstruction namespace/migrations ;
- stratégie façade/configuration backend ;
- stratégie de résumé runtime desktop et logging
ks-store.pg; - modèle N1 raw -> N2 Core -> N3 materialization générique -> N4 projections spécialisées, avec gel fort N1-N3 et replay inter-niveaux ;
- canari future-decoder/Anchor-readiness imposant la conservation générique des bytes d'instruction, comptes ordonnés, CPI, logs, return data et états/observations de comptes avant gel ;
- décision metadata N4 : asset/token metadata commune Metaplex + Token-2022, program metadata séparée pour SPM ;
- plan des faits canoniques ;
- aucun changement SQL de production.
0.5.3-pre.002 — façade générique, configuration, diagnostics et sécurité
- introduire
StoreetStoreOpenOptionsbackend-agnostiques ; - rendre
PostgresStore/pool/options privés ; - renommer diagnostics et replay DTO publics sans préfixe backend ;
- définir le résumé runtime/init sans secrets consommable par le desktop ;
- supprimer les quatre traits/DTO historiques non implémentés ;
- remodeler le bloc store config en backend + options sélectionnées/opaques ;
- déplacer toute validation/interprétation PostgreSQL dans
ks-store; - introduire l’instrumentation PostgreSQL sous le
TRACING_TARGETcanonique avecbackend="postgres",domain="ks-store.pg"et les canaris backend/secret ; - migrer les helpers de connexion sans changer encore les tables ;
- préparer une façade
Storepersistante dans l’état applicatif afin que les commandes desktop ne recréent pas de connexion backend.
0.5.3-pre.003 — ressources SQL PostgreSQL et nouveau baseline Core
- remplacer l'arborescence active par
migrations/postgres/; - une instruction SQL par fichier ;
- fonctions privées
include_str!et orchestrateur d'init transactionnel ; - reconstruire directement les tables sous
k_sol_*; - ajouter
block_timeraw/Core ; - conserver séparément top-level/CPI et rendre leurs contrats de lifecycle/replay cohérents ;
- transporter/auditer
stack_heightet reconstruire le parent CPI immédiat lorsque l’information canonique le permet ; - exécuter le canari future-decoder sur N1/N2 : fidélité des bytes d'instruction, comptes ordonnés/flags, CPI, logs,
returnDataet observations/états de comptes ; ajouter avant gel uniquement les contrats Solana génériques réellement manquants ; - normaliser les nouveaux contrats sur le terme
top-levelet ne pas introduire de troisième familleouter; - renommer
mat_events -> mat_outputs; - remplacer les scripts de maintenance par opérations unitaires drop/truncate ;
- supprimer le DDL Rust dupliqué.
0.5.3-pre.004 — repositories, replay, idempotence, index et pagination
- adapter toutes les queries aux tables finales ;
- fournir une lecture logique commune top-level/CPI sans fusion physique ;
- corriger l'invalidation descendante et formaliser les replays N1 -> N2, N2 -> N3 et la frontière future N3 -> N4 ;
- remplacer les gros offsets/limites par pagination stable ;
- ajouter les index justifiés ;
- couvrir les bornes BIGINT ;
- vérifier atomicité et reprise après erreur ;
- instrumenter les requêtes PostgreSQL sous
target: crate::TRACING_TARGET,backend="postgres",domain="ks-store.pg"etactionexplicite.
0.5.3-pre.005 — finalisation demo_store, consommateurs et validation PostgreSQL réelle
- supprimer les derniers
Postgres*deks-pipeline-demo-scenarios; - finaliser les surfaces
demo_store_*dekb-app-demo-desktopautour de résultats tabulaires génériques indépendants du backend ; la nomenclature technique a déjà été migrée enpre.002; - refaire
demo_configafin qu'elle n'expose/interprète plus les champs PostgreSQL/SQLite et consomme uniquement le résumé store sanitisé ; - refaire les informations store du splash autour d'une vérification par modèle logique et de compteurs (tables/index attendus, présents, créés), sans afficher les noms physiques ;
- confirmer que
ks-pipelinereste inchangé dans son rôle d'orchestration ; - tests base vide + réouverture + refus ancien schéma ;
- validations PostgreSQL réelles ;
- lancer Tauri parce que les diagnostics/surfaces desktop auront changé.
0.5.3-pre.006 — gel de contrat et réconciliation technique
- produire le snapshot machine-readable frozen avec le nombre réel de tables N1/N2/N3 après canari future-decoder, sans imposer artificiellement la baseline de 13 ;
- canari exact du schéma logique ;
- audit workspace anti-
kb_sol_actif ; - audit anti-PostgreSQL hors
ks-store; - audit secrets/logging ;
- confirmer qu'aucune table trading/DEX ni projection metadata N4 non auditée n'a été introduite ;
- ajouter un canari de gel prouvant qu'un futur decoder Anchor/IDL peut exploiter N1/N2/N3 sans changement structurel normal : instructions/CPI lossless, logs, return data, account observations et provenance de schéma ;
- figer explicitement les contrats N1/N2/N3 tout en documentant la politique d'évolution plus souple de N4 ;
- produire un rapport de validation technique candidat.
0.5.3-pre.007 — premier candidat de clôture documentaire
- réconcilier README/USAGE/TODO/changelogs/guides/architectures ;
- supprimer les TODO terminés ;
- mettre à jour le ROADMAP ;
- finaliser le rapport de validation ;
- archiver ce plan et le prompt
032sousolddocs/archivekbot3/; - préparer le prompt
0.5.4scénarios/exécuteurs/fixtures/validations ; - validation standard finale candidate.
Si un manque apparaît à pre.006, pre.007 ou pendant les validations finales, la clôture est décalée vers pre.008, pre.009, etc. Le numéro n'est jamais considéré comme plus important que la complétude du contrat.
Un delta-fix-XXX corrige la prerelease courante sans changer son numéro documentaire et repart à fix-001 pour chaque prerelease.
29. Validation standard par tranche
Après chaque delta Rust ou SQL significatif :
cargo fmt --all
cargo check --workspace
cargo clippy --all-targets
python3 scripts/audit_rust_workspace_rules.py
cargo test --workspace
Quand PostgreSQL réel est requis :
export KS_SECRET_POSTGRES_TEST_URL='...'
cargo test -p ks-store
Les commandes exactes des tests opt-in existants seront reprises depuis le code de la tranche concernée plutôt que supposées dans ce plan.
Le desktop n'est lancé qu'après une modification d'une surface qu'il consomme :
cargo tauri dev -c kb-app-demo-desktop/tauri.conf.json
30. Critères de sortie 0.5.3
La version ne peut être clôturée que si :
- il n'existe plus d'objet actif
kb_sol_*; - les 13 tables de baseline et toute table N1/N2/N3 générique ajoutée après le canari future-decoder correspondent exactement au snapshot frozen documenté ;
block_timeest conservé lorsqu'il existe et resteNULLsinon ;- les top-level et CPI restent physiquement séparées mais peuvent être sélectionnées séparément ou comme flux logique commun pour le decode ;
- replay et invalidation descendante sont cohérents et transactionnels, avec contrats N1 -> N2, N2 -> N3 et frontière N3 -> N4 documentée/testable ;
- les tables N1/N2/N3 sont marquées fortement gelées et la politique d'évolution N4 est explicitement séparée ;
- le canari future-decoder confirme que l'ajout d'Anchor/IDL ou d'un autre decoder futur ne nécessite pas de modification structurelle normale de N1/N2/N3, avec couverture explicite des instructions/CPI, logs,
returnData, observations/états de comptes et provenance de schéma ; k_sol_mat_outputsreste la source N3 générique durable même en présence de futures projections N4 ;- les limites/paginations sont bornées et stables ;
- les conversions unsigned/PostgreSQL sont checked ;
- les index ajoutés correspondent à des requêtes réelles ;
- aucune API publique de
ks-storen'expose PostgreSQL ou SQLx ; - aucune crate externe n'interprète les paramètres PostgreSQL ;
- PostgreSQL reste entièrement fonctionnel comme backend actif, avec ses SQL ressources sous
migrations/postgres/et ses logs sous le target canoniqueks-storeavec domaine structuréks-store.pg; - aucun secret n'apparaît dans diagnostics/erreurs/résumés runtime/logs ;
- les tables futures sont guidées par des faits canoniques, pas par des Program IDs ;
- aucune table DEX spéculative n'a été gelée sans matérialiseur réel ;
- les deux projections metadata N4 futures (asset/token Metaplex + Token-2022, program metadata SPM) sont documentées comme extensions additives et rejouables depuis N3 ;
- le desktop ne dépend plus d'un diagnostic/config PostgreSQL concret et son splash ne divulgue pas les noms physiques des objets store ;
- le plan/prompt sont archivés et
0.5.4est préparé.
31. Points validés / ajustés avant pre.002
Les retours de revue de pre.001 fixent désormais les points suivants :
- Cargo utilise
0.5.3-pre.1, tandis que la numérotation documentaire/archive reste0.5.3-pre.001; - baseline de 13 tables conservées, avec top-level et CPI physiquement séparées ; le nombre final peut augmenter uniquement si le canari future-decoder démontre un manque Solana générique avant gel ;
- les deux familles d'instructions doivent avoir des capacités lifecycle/replay équivalentes et une lecture logique commune ;
block_timereste porté par raw/Core transaction et résolu par relation pour les tables filles ;- conservation du journal générique sous le nom
k_sol_mat_outputs; aucune table metadata métier n'est ajoutée sans besoin queryable stable ; - aucune table trading/DEX n'est créée en
0.5.3; les futures tables sont génériques par concept/fait ; - les bases Devnet/Mainnet/test
0.5.2seront supprimées : reconstruction propre, pas migration in-place ; - SQL PostgreSQL sous
ks-store/migrations/postgres/, une instruction par fichier, fonctions privéesinclude_str!, tables/keys/index/drop/truncate séparés et orchestrateur d'init ; - façade publique
Storebackend-agnostique et PostgreSQL entièrement privé ; - configuration backend opaque jusqu'à
ks-store, sans dépendanceks-store -> ks-config; - résumé runtime/init sans secrets exposé à
kb-app-demo-desktopet renommage structurel immédiat des surfacesdemo_sql_*versdemo_store_*; - logging PostgreSQL sous le target canonique
ks-storeavecbackend="postgres"etdomain="ks-store.pg", sans secrets ni valeurs bindées par défaut ; - pagination cursorisée/bornée et distinction gel logique/index physiques ;
- architecture future préservée et précisée : N1 Worker 1/raw, N2 Worker 2/Core, N3 decode/materialization générique avec
k_sol_mat_outputs, N4 projections spécialisées/queryables ; - les contrats N1/N2/N3 sont fortement gelés après
0.5.3sauf urgence ou omission structurelle majeure ; les tables/modèles N4 restent plus souples et évolutifs ; - replays explicites et indépendants N1 -> N2, N2 -> N3 et N3 -> N4, avec idempotence, version de processor/projector et invalidation descendante ;
- aucune troisième table
outer: les instructions historiquement dites outer sont les top-level ; les nouveaux contrats utilisenttop-levelet la table CPI conservestack_heightainsi que le parent CPI immédiat reconstructible ; - metadata N4 prévue en deux projections : asset/token metadata commune Metaplex + Token-2022 et program metadata distincte pour SPM, toutes deux reconstructibles depuis N3 ;
demo_store_*devient la nomenclature technique dèspre.002; sa présentation finale évolue vers des résultats tabulaires backend-agnostiques,demo_configcesse d'exposer PostgreSQL/SQLite, et le splash vérifie des modèles logiques avec compteurs sans noms de tables/index ;- la façade
Storeest destinée à être ouverte une fois et conservée dansAppState, les commandes desktop réutilisant cette capacité générique ; pre.007est un premier candidat de clôture, jamais une obligation : la clôture est décalée si des manques apparaissent ;- Anchor reste hors périmètre fonctionnel de
0.5.3, mais devient un canari explicite du gel : N1/N2/N3 doivent conserver sans perte les informations génériques nécessaires aux futurs decoders (instructions/CPI, comptes ordonnés et flags, logs,returnDataréelle, observations/états de comptes, temporalité/provenance) et N3 doit pouvoir tracer le decoder + schéma/IDL/version/hash utilisés ; - les 13 tables sont la baseline existante, pas un quota final : si ce canari révèle une omission Solana générique,
0.5.3peut ajouter avant gel un contrat N1/N2/N3 canonique non nommé d'après Anchor.
Ces décisions remplacent les propositions initiales de fusion des instructions et de conservation immuable des migrations 0001 à 0004. pre.002 peut démarrer sur cette base ; aucune migration SQL massive n'est encore incluse dans pre.001.
32. État réalisé de 0.5.3-pre.002
La tranche 0.5.3-pre.002 réalise la première rupture effective de dépendance au backend concret, sans modifier le schéma SQL historique qui reste volontairement réservé à pre.003.
32.1 Façade et possession de la connexion
ks-storeexpose désormaisStoreetStoreOpenOptionscomme frontière publique d’ouverture et d’utilisation du stockage ;PostgresStore,PostgresStoreOptions, le pool SQLx et les helpers PostgreSQL restent internes àks-store;Storeimplémente les contrats fonctionnels déjà consommés par les pipelines et délègue au backend actif sans faire remonter le dispatch dans les crates consommatrices ;- le desktop conserve un
Storeouvert une seule fois dansAppStateviatokio::sync::OnceCell, puis les commandes réutilisent cette capacité ; ks-pipeline-demo-scenariosutilise également la façade générique et ne construit plus le backend PostgreSQL.
32.2 Configuration backend opaque
Le contrat runtime de ks-config conserve désormais uniquement :
enabled
backend
backend_options
backend_options reste un objet JSON opaque pour ks-config. La sélection, la validation des paramètres PostgreSQL, la création de la connexion et l’auto-initialisation sont interprétées par ks-store. ks-store ne dépend pas de ks-config.
PostgreSQL est le seul backend opérationnel de cette tranche. Un backend inconnu ou incomplet est refusé par une erreur structurée qui ne recopie pas ses options sensibles.
32.3 Diagnostics et résumé runtime génériques
La surface publique fournit des DTO backend-agnostiques pour :
- configuration sanitisée ;
- backend descriptor masqué ;
- health ;
- état de migration ;
- ressources logiques et statistiques ;
- vérification par modèle logique ;
- résumé d’initialisation ;
- résumé runtime complet.
Le résumé d’initialisation regroupe actuellement les ressources du schéma historique sous les modèles logiques raw, core, processing, decode et materialization. Il expose des compteurs de ressources attendues/disponibles/créées sans exposer les noms physiques. Une liste générique de catégories d’objets backend expose en plus les compteurs physiques sans noms : PostgreSQL fournit déjà la catégorie table; pre.003 ajoutera la catégorie index lors de la reconstruction/introspection du nouveau baseline. Un futur backend pourra fournir d’autres catégories sans modifier le contrat du desktop.
La constante STORE_SCHEMA_CONTRACT_VERSION identifie explicitement cette étape comme un schéma legacy transitoire ; elle ne constitue pas le snapshot frozen final.
32.4 Replay et API publique
Les DTO publics de diagnostic et de replay ne portent plus le préfixe Postgres. Le vocabulaire nouveau utilise top-level au lieu de outer. Les quatre anciens traits publics sans implémentation PostgreSQL réelle et leurs DTO historiques sont supprimés au lieu d’être figés.
La structure SQL et les requêtes restent encore basées sur les tables kb_sol_* de 0.5.2 : leur reconstruction sous k_sol_*, l’alignement top-level/CPI et le nouveau baseline appartiennent toujours à pre.003.
32.5 Desktop
Les commandes et payloads nécessaires au desktop ne construisent plus un backend concret. demo_config reçoit un résumé store sanitisé ; les diagnostics SQL utilisent désormais des ressources logiques génériques et un descriptor de connexion masqué. Le splash utilise le résumé d’initialisation par modèle et des compteurs, sans afficher de noms physiques de tables/index.
Les noms historiques de fenêtres/commandes demo_sql_* sont supprimés dès pre.002 et remplacés par demo_store_*. pre.005 conserve uniquement la refonte fonctionnelle finale de l’expérience tabulaire, de demo_config et du splash autour du nouveau baseline.
32.6 Logging et sécurité
Le backend PostgreSQL utilise l’unique target canonique de la crate :
target: crate::TRACING_TARGET // "ks-store"
backend = "postgres"
domain = "ks-store.pg"
action = "connection | migration | query | health | replay | ..."
Un canari workspace interdit désormais les types/symboles PostgreSQL/SQLx dans les crates consommatrices et vérifie que l’implémentation PostgreSQL utilise target: crate::TRACING_TARGET avec les champs structurés backend="postgres" et domain="ks-store.pg". Des tests sentinelles vérifient également qu’un backend inconnu, un descriptor masqué et les résumés publics ne recopient pas des options contenant un secret.
Le nettoyage fin de l’instrumentation des requêtes (durées, nombres de lignes et suppression systématique de toute valeur bindée des traces normales) reste couplé à la réécriture des repositories de pre.004, conformément au plan.
32.7 Limites volontaires de la tranche
pre.002 ne :
- renomme aucune table
kb_sol_*; - ne crée aucun contrat N1/N2/N3 supplémentaire ;
- ne restructure pas encore
migrations/postgres/; - ne change pas
block_time,stack_height, la hiérarchie CPI ouk_sol_mat_outputs; - ne crée aucune projection N4 metadata/trading ;
- n’implémente pas Anchor.
Ces travaux restent affectés aux tranches pre.003+ prévues par le présent plan.