# Plan v0.3.10 — RAW Transaction commune + Worker d’ingestion multi-source ## 1. But de la version La version `0.3.10` doit matérialiser deux responsabilités successives et indépendantes du Job historique : ```text 1. extraire la canonicalisation RAW Transaction v1 dans ksp-raw-transaction-lib ; 2. construire ksp-worker-raw-transaction-ingest-lib comme Worker continu multi-source. ``` Le centre du système reste Store : ```text sources live -> matériau transaction complet ou discovery/hydration -> canonicalisation RAW v1 unique -> RawTransaction + RawTransactionObservation -> persistence par le producteur concret via ksp-store-lib ``` Le Worker et le Job Backfill sont deux producteurs autonomes du même modèle durable. Aucun appel, lifecycle, orchestration, checkpoint ou contrat fonctionnel ne doit relier les deux. ## 2. Base autoritaire et vérification d’ouverture Base fournie et vérifiée le 5 septembre 2026 : ```text archive KSP : khadhroony-solana-project-v0.3.9.zip workspace.package.version avant ouverture : 0.3.9 delta stable : deltas/0.3.9/rel.001.md prompt : prompts/029-V0_3_10_START_PROMPT.md ``` Preuves d’archive : ```text SHA-256 = 41ef2589a11410aabfb329d387f0008e116006da60ada0802e5f72bc30d3466f ZIP bytes = 7859118 ZIP entries = 1855 unzip -t = PASS files extraits = 1674 directories = 183 ``` Contrôles structurels du ZIP : ```text entrée absolue/traversal = 0 symlink = 0 .git/ = 0 target/ = 0 node_modules/ = 0 Cargo.lock = 0 .env = 0 *.pem/*.key/*.p12/*.pfx = 0 ``` Le ZIP stable ne contient pas `.git`; le tag `v0.3.9` n’est donc pas réinspectable localement. L’archive est néanmoins admissible comme base fournie explicitement : version Cargo stable `0.3.9`, `CHANGELOG` ouvert sur `0.3.9`, `ROADMAP` cohérente et `deltas/0.3.9/rel.001.md` présent. ### 2.1 Workspace réel Le workspace stable contient 19 membres : ```text ksp-app-backfill-desk ksp-app-config-desk ksp-app-solprices-desk ksp-app-store-desk ksp-app-wallet-desk ksp-config-lib ksp-core-lib ksp-interface-lib ksp-job-api ksp-job-backfill-lib ksp-logging-lib ksp-offchain-transport-lib ksp-onchain-transport-lib ksp-program-api ksp-store-api ksp-store-lib ksp-store-postgres-lib ksp-wallet-lib ksp-worker-api ``` Il ne contient encore ni `ksp-raw-transaction-lib` ni `ksp-worker-raw-transaction-ingest-lib`, ce qui correspond au point de départ attendu. ### 2.2 Baseline statique avant modification Exécuté sur l’archive inchangée : ```text python3 scripts/audit_rust_workspace_rules.py General Rust rule audit: clean Rust export completeness audit: 0 candidate(s) KSP workspace Rust rule audit: clean python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas Markdown table audit: clean (332 table(s), 750 file(s)) ``` Le binaire `cargo` n’est pas présent dans l’environnement d’assemblage. Aucun `cargo fmt/check/clippy/test/tree` de cette session n’est donc déclaré PASS. Les preuves opérateur de la stable `0.3.9` sont utiles comme baseline externe mais ne sont pas requalifiées en exécution locale. ## 3. Sources internes auditées Les sources obligatoires du prompt ont été relues avant décision : ```text RULES.md ROADMAP.md CHANGELOG.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/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/008-RAW_STORE_AND_REPLAY_PIPELINE.md docs/architecture/009-ACQUISITION_WORKERS_AND_JOBS.md docs/architecture/010-APPS_SERVICES_SCENARIOS_AND_CONTROL.md docs/architecture/011-RAW_TRANSACTION_ACQUISITION.md crates/ksp-worker-api/ docs/plans/030-V0_3_9_WORKER_API_RAW_TRANSACTION_AUDIT_PLAN.md docs/validation/026-V0_3_9_WORKER_API_RAW_TRANSACTION_AUDIT.md crates/ksp-job-backfill-lib/ docs/plans/027-V0_3_6_JOB_API_BACKFILL_PLAN.md docs/validation/023-V0_3_6_JOB_API_BACKFILL.md crates/ksp-store-api/ crates/ksp-store-lib/ crates/ksp-onchain-transport-lib/ crates/ksp-config-lib/ config/std.transport.json config/schemas/std.transport.schema.json config/examples/std.transport.example.json .env.example ``` Le prompt `029` ne demande pas de nouvelle inspection kbot3 pour `pre.001`. L’audit fonctionnel historique est déjà consigné dans `011`; kbot3 n’est donc pas ouvert et aucune source de code kbot3 n’est utilisée. ## 4. Décisions structurantes de `pre.001` Les décisions de fermeture du gate sont : ```text RAW v1 reste unique ; aucun RAW v2. mainnet reste l’identité KSP canonique ; mainnet-beta reste alias externe/legacy. ksp-raw-transaction-lib devient l’unique owner de la canonicalisation RAW v1. La common crate dépend des modèles `ksp-store-api` conformément à `DEP-PIPE-006`, jamais de la façade runtime Store ni de Transport/Config/Job/Worker. RawObservationKey reste producer-owned comme le contrat Store l’indique déjà. Le Backfill conserve son observation-key Job-owned et ses métadonnées de campagne. Le Worker possède son runtime concret, ses source IDs, ses observation keys et sa continuité de run. Aucun edge Job <-> Worker. Aucun backend PostgreSQL direct. Les sources full ne sont admises en direct que si leur projection produit le même RAW canonique. Sinon la source devient discovery/hydration sans faux claim de complétude. Un reconnect/resubscribe n’est jamais assimilé à un replay. La réparation est bornée au frontier du run courant. Les duplicates multi-source sont normaux ; les conflits de contenu sont des fautes d’intégrité. ``` Un point critique est ajouté au plan : la parité **byte-for-byte** entre les représentations full HTTP, WS/Helius et Yellowstone doit être prouvée avant d’autoriser une voie à persister directement. Le fait qu’un provider qualifie son message de « full » ne suffit pas à prouver l’identité canonique KSP. ## 5. Audit exact de l’extraction `ksp-raw-transaction-lib` ### 5.1 Contrat RAW v1 gelé Le canari Backfill actuel est confirmé indépendamment : ```text format_id = ksp.solana.raw_transaction format_version = 1 bytes = {"transaction":["AQID","base64"],"meta":{"a":{"x":null,"y":true},"z":1},"version":"legacy","transactionIndex":7} byte_len = 112 sha256 = 220792d2b15d262fda242cb220774ee9ddeffebf04dcfadabcf8ef76a9b1a7c3 ``` La migration vers la common crate doit conserver exactement ce vecteur et les règles déjà prouvées : ```text transaction binaire Base64 uniquement pour le chemin HTTP actuel ordre top-level transaction -> meta -> version -> transactionIndex tri récursif des clés des objets JSON meta ordre des arrays conservé omitted != null != value version legacy ou entier u8 transactionIndex u32 côté HTTP actuel block_time hors payload RAW, converti vers RawTimestamp signature canonique = exactement 64 octets slot = u64 sans narrowing SHA-256 calculé sur les bytes canoniques exacts ``` ### 5.2 Items à déplacer et items à conserver | Élément actuel dans Backfill | Owner après extraction | Décision | |:-----------------------------------------------------|:-----------------------|:----------------------------------------------------------------| | `RAW_TRANSACTION_FORMAT_ID` | common RAW | déplacer sans changer la valeur | | `RAW_TRANSACTION_FORMAT_VERSION` | common RAW | déplacer sans changer `1` | | canonical JSON writer | common RAW | déplacer | | SHA-256 du payload canonique | common RAW | déplacer | | conversion block time -> `RawTimestamp` | common RAW | déplacer | | décodage Base58 exact vers signature 64 bytes | common RAW | déplacer comme primitive générique | | matériau transaction source-neutral | common RAW | créer autour des invariants RAW v1 | | assemblage `RawTransaction` | common RAW | déplacer | | assemblage observation depuis key + provenance sûres | common RAW | déplacer sans décider la key | | `BackfillRawAcquisition` public | Backfill | conserver comme wrapper/compatibilité autour du résultat common | | `BackfillHydrationOutcome` | Backfill | conserver | | appel `get_transaction_observed` | Backfill | conserver | | choix `Base64`, commitment et max version HTTP | Backfill/Transport | conserver hors common | | garde request network == candidate network | Backfill | conserver | | `BackfillSignature` admission 64..88 + scope | Backfill | conserver ; conversion exacte délègue à common | | `job_id`, `scope_fingerprint` | Backfill | conserver | | observation key Backfill | Backfill | conserver strictement | | origin `Backfill` | Backfill | conserver | | capture session `job_id` | Backfill | conserver | | provider/endpoint observed HTTP | Backfill adapter | conserver comme entrée de provenance common | | lifecycle/checkpoint/discovery/execution/persistence | Backfill | conserver | Le point `RawObservationKey` est non négociable : Store le documente déjà comme « producer-owned deterministic observation key ». La common crate ne doit donc pas inventer une clé universelle qui fusionnerait des observations de producteurs différents. ### 5.3 Surface publique minimale cible de la common crate La surface à matérialiser est volontairement petite : ```text RAW_TRANSACTION_FORMAT_ID RAW_TRANSACTION_FORMAT_VERSION MIN_RAW_TRANSACTION_SIGNATURE_TEXT_BYTES = 64 MAX_RAW_TRANSACTION_SIGNATURE_TEXT_BYTES = 88 RawTransactionMaterial RawTransactionWireField RawTransactionVersion RawTransactionAcquisition parse_raw_transaction_signature(...) canonicalize_raw_transaction(...) assemble_raw_transaction_acquisition(...) ERROR_CODE_RAW_TRANSACTION_MATERIAL_INVALID ERROR_CODE_RAW_TRANSACTION_CANONICALIZATION_INVALID ERROR_CODE_RAW_TRANSACTION_SIGNATURE_INVALID ``` Sémantique : ```text RawTransactionMaterial network: RawNetworkId signature: RawTransactionSignature slot: u64 block_time: Option transaction: représentation source-neutral capable de fournir le body canonique meta: omitted/null/value version: omitted/null/value transaction_index: omitted/null/value canonicalize_raw_transaction(material) -> RawTransaction assemble_raw_transaction_acquisition(transaction, observation_key, provenance) -> vérifie que l’observation référence exactement la transaction -> RawTransactionAcquisition { transaction, observation } ``` `RawTransactionWireField` est distinct du type Transport afin que common n’ait aucun edge vers Transport. Les adapters Worker/Backfill projettent explicitement les types Transport vers ce contrat. La représentation du body doit admettre au moins le chemin binaire canonique déjà prouvé. L’extension structurée nécessaire à Yellowstone est ajoutée uniquement avec une preuve de sérialisation/parité ; elle ne doit pas être simulée par un `serde_json::Value` opaque si cela permettrait des divergences non détectées. ### 5.4 Dépendances exactes de la common crate Cible retenue : ```text ksp-raw-transaction-lib -> ksp-core-lib -> ksp-store-api -> serde_json -> sha2 ``` Justification : - `ksp-core-lib` est nécessaire aux codes/erreurs KSP propres à la canonicalisation ; - `ksp-store-api` fournit directement les modèles canoniques `RawTransaction`, `RawPayload`, provenance et primitives, conformément à `DEP-PIPE-006` ; - `serde_json` reste requis par RAW v1 pour canonicaliser `meta` et préserver les états wire ; - `sha2` reste requis pour le digest canonique exact. Interdits : ```text ksp-store-lib ksp-onchain-transport-lib ksp-config-lib ksp-job-api ksp-job-backfill-lib ksp-worker-api ksp-worker-raw-transaction-ingest-lib tokio/futures ksp-store-postgres-lib provider SDK ``` ### 5.5 Erreurs et redaction Les erreurs common doivent être statiques et ne jamais recopier : ```text signature hostile meta/payload source transaction bytes hash/signature dans Debug provider URL endpoint URL secret/token/header ``` Les contextes publics admis sont uniquement des codes sûrs et bornés tels que `field`, `variant`, `actual_len`, `maximum_len` lorsque ces valeurs ne transportent aucun matériau source. ### 5.6 Gate de parité cross-source Avant qu’un adapter « full » ne persiste directement : ```text même transaction fixture -> HTTP getTransaction/base64 -> WS blockSubscribe ou Helius full lorsque applicable -> Yellowstone transaction/blocks -> RawTransactionMaterial -> canonicalize_raw_transaction -> mêmes bytes -> même content_hash ``` Si la parité n’est pas démontrable pour une famille, la voie reste admise comme signal de discovery et utilise hydration HTTP. Ce fallback est fonctionnellement préférable à un conflit canonique artificiel. La priorité `pre.001` n’est pas d’affirmer que la parité existe déjà ; elle est d’en faire un gate d’implémentation explicite. ## 6. Audit Transport implementation-ready ### 6.1 Matrice capabilities Worker V1 | Capability | Méthode / protocole | Surface KSP `0.3.9` | Gap exact | Full / hydration | Provenance disponible | Continuité / replay | Preuve accessible | Adaptation Transport | Adaptation Config | Fixture | Live smoke | |:-------------------------------|:-----------------------------|:-------------------------------------------------|:--------------------------------------------------------------------|:----------------------------------------|:---------------------------------------------------------------------|:--------------------------------------------------------------|:----------------------------------------------------------|:-----------------------------------------|:--------------------------------------------------|:------------------------|:-----------------------------| | HTTP transaction hydration | `getTransaction` JSON-RPC | `get_transaction_observed` | aucun gap de provenance ; projection vers common à extraire | full après réponse non-null | provider + endpoint gagnant | aucune continuité native | Mainnet/Devnet/Testnet publics | non, hors adapter common | non | oui | oui opt-in | | HTTP live blocks | `getSlot` + `getBlock` | wrappers typés présents | `TR-B`: pas de `get_block_observed` | full par bloc | actuellement insuffisante en pool | polling depuis run frontier ; repair borné | publics | oui `get_block_observed` | rôle HTTP worker si profil dédié nécessaire | oui | oui opt-in | | WS logs + hydration | `logsSubscribe` + HTTP | wrapper logs + sessions | composer discovery + hydration sans perdre le statut de source | hydration requise | WS session safe + HTTP observed ; Store garde endpoint full-material | resubscribe seulement, aucun replay WS | Solana public/Helius standard | adapter Worker + TR-D, pas nouveau actor | profils existants suffisants pour première preuve | oui | oui opt-in | | WS signature + hydration | `signatureSubscribe` + HTTP | wrapper one-shot présent | utile ciblé mais pas source globale principale | hydration requise | idem | one-shot ; aucun replay | publics | aucun P0 spécifique | non | oui | secondaire | | WS blockSubscribe | `blockSubscribe` standard | wrapper + `SolanaConfirmedBlock` | projection transactions de block vers common + parité | full si parité prouvée | session/provider logique ; pas de winner HTTP | reconnect/resubscribe, pas replay | validator public selon support ; Helius explicitement non | TR-C adapter/parité | aucune exigée si endpoint supporte | oui | opt-in, capability-dependent | | Helius full transaction WS | `transactionSubscribe` | typed provider envelope, nested transaction JSON | transformer le nested payload en matériau common sans provider leak | full si parité prouvée, sinon hydration | provider/session/filter sûrs | reconnexion, pas historique garanti WSS | clé/tier opérateur requis selon plan | TR-C + TR-D | profil Helius éventuellement nécessaire plus tard | oui | opt-in/tier-dependent | | Yellowstone transactions | `Subscribe.transactions` | DTO full KSP, stream/reconnect présents | projection body+meta structurés vers RAW v1 byte-identique | full si parité prouvée | provider + endpoint settings + filter + created_at disponibles | `from_slot`, replay info, reconnect snapshot | OrbitFlare Devnet / PublicNode selon accès | TR-C + TR-D + TR-E | profils V3 déjà présents | oui | oui opt-in | | Yellowstone blocks | `Subscribe.blocks` | DTO block full + transactions | même gate de projection ; préserver slot/index/block_time | full si parité prouvée | idem | `from_slot`; upstream a corrigé replay blocks en juillet 2026 | mêmes providers | TR-C + TR-D + TR-E | non P0 | oui | oui opt-in | | Yellowstone status + hydration | `transactions_status` + HTTP | DTO status + HTTP observed | composer signal + hydration | hydration requise | status filter/created_at + HTTP winner | replay selon provider + HTTP repair | mêmes providers | TR-D + TR-E | non P0 | oui | oui opt-in | | Yellowstone block meta / slots | `blocks_meta`, `slots` | DTO + stream présents | aucun RawTransaction direct ; alimenter continuity tracker | non, auxiliaire | filter/slot/status/created_at | continuity evidence ; replay provider-specific | mêmes providers | TR-E côté Worker | non | oui | oui opt-in | | EARLY source | provider/shred adapter | aucune surface générique unique | `TR-F`, uniquement protocole réellement implémenté | hydration sauf preuve meta complète | provider-specific | provider-specific | généralement payant | différé | différé | seulement si implémenté | seulement si accessible | ### 6.2 `TR-B` — `get_block_observed` Statut : **confirmé P0**. `HttpObservedValue` existe et `get_transaction_observed` l’utilise déjà. `getBlock` doit obtenir la surface symétrique afin que le provider et l’endpoint réellement gagnants du pool soient conservés. Ne pas sélectionner un endpoint deux fois et ne pas reconstruire la provenance après coup. Cible : ```text HttpTransportPool::get_block_observed(role, slot, config) -> Result>> ``` Aucun URL/header/raw body dans le résultat ou Debug. ### 6.3 `TR-C` — projection full source-neutral Statut : **confirmé, mais scindé**. La découpe retenue évite un mauvais edge Transport -> common : ```text TR-C1 : common possède RawTransactionMaterial et les invariants RAW. TR-C2 : Worker possède les adapters explicites Transport DTO -> RawTransactionMaterial. TR-C3 : Transport n’est modifié que si un DTO actuel ne permet pas une projection fidèle/bornée. TR-C4 : chaque adapter full doit fermer un golden de parité cross-source avant persistence directe. ``` Ainsi `ksp-onchain-transport-lib` ne dépend jamais de `ksp-raw-transaction-lib` et la common crate ne dépend jamais de Transport. ### 6.4 `TR-D` — métadonnées d’acquisition live Statut : **confirmé, projection en deux niveaux**. Niveau durable Store : ```text provider protocol acquisition_method origin = Live / Repair / Replay selon phase received_at observed_at si source fiable endpoint_id logique du producteur du matériau complet filter_id/source-id logique capture_session_id = worker run id sûr commitment source payload hash/size seulement si réellement disponible et borné ``` Pour un chemin discovery + hydration, le provider/endpoint durable correspond au producteur du **matériau complet** (par exemple HTTP observed). `acquisition_method` et `filter_id` indiquent le chemin composite de manière sûre. La V1 ne surcharge pas un champ Store unique avec deux endpoints différents. Niveau runtime Worker : la source de discovery conserve sa propre santé/counters/continuity dans le snapshot concret. Une observation Store ne doit pas prétendre qu’un message logs incomplet était une transaction complète. ### 6.5 `TR-E` — replay/from_slot Statut : **confirmé sans second moteur Yellowstone**. Le moteur KSP expose déjà `from_slot`, `SubscribeReplayInfo` et ses snapshots de reconnect. Le Worker doit seulement : ```text mémoriser son frontier durable par source/couverture qualifier le replay window disponible reconnecter avec from_slot lorsque le gap est dans la fenêtre prouvée dédupliquer les messages replayés réconcilier jusqu’au frontier live refuser le claim lossless si replay/couverture n’est pas prouvé ``` Important : le changelog upstream du 22 juillet 2026 documente un correctif où `from_slot` avec filtre `blocks` pouvait auparavant être accepté mais livrer zéro block replay avant reprise live. Un smoke de replay **blocks** est donc obligatoire ; un smoke transactions ne couvre pas ce risque. ### 6.6 `TR-F` — EARLY Statut : **réservé, non implémenté par anticipation**. Aucune source shred/Jetstream/preprocessed ne rentre dans le P0 si elle ne peut pas fournir les métadonnées d’exécution nécessaires au RAW canonique. Elle peut ultérieurement fournir un signal EARLY + hydration via un adapter spécialisé. La disparition programmée de Jito ShredStream le 5 septembre 2026 confirme qu’encoder une source vendor-specific dans la surface centrale serait une mauvaise frontière. ## 7. Fraîcheur externe revalidée le 5 septembre 2026 ### 7.1 Solana standard Les contrats utiles restent cohérents avec l’implémentation KSP : ```text getBlock : bloc confirmé avec transactions selon transactionDetails/encoding blockSubscribe : méthode unstable, activation validator requise logsSubscribe : signature + err + logs, donc hydration pour RAW complet signatureSubscribe : ciblé/one-shot, pas une transaction full ``` Décision : aucun nouveau client standard ; réutiliser les wrappers actuels et fermer seulement les gaps de provenance/projection. ### 7.2 Yellowstone upstream/proto `yellowstone-grpc-proto` courant est `12.7.0`, publié le 29 août 2026. Le workspace `0.3.9` déclare déjà `^12.7`; **aucun bump de dépendance n’est justifié en ouverture**. Le repo upstream affiche des releases Geyser 14.x plus élevées ; elles ne doivent pas être confondues avec la version de la crate proto. La compatibilité se juge sur les messages réellement consommés par KSP. Le changelog récent impose deux canaris : ```text replay blocks from_slot après correctif 2026-07-22 Transaction V1 / champ config : proto >= 12.6 nécessaire, donc 12.7 courant est admissible ``` ### 7.3 Provider/access matrix revalidée | Provider / source | État courant utile | Accès de preuve KSP | Décision `0.3.10` | |:---------------------|:----------------------------------------------------------------------------------------------------|:-----------------------------------------------------|:-----------------------------------------------------------------------| | Solana public RPC/WS | HTTP/WS standards accessibles ; blockSubscribe reste capability validator-dependent | sans secret, opt-in live | P0 HTTP/WS | | PublicNode | Mainnet/Testnet RPC/WS/Yellowstone affichés ; archive access proposé, profondeur replay non prouvée | profils KSP + smokes ignored existants | P0 Yellowstone live ; replay seulement après preuve | | OrbitFlare | Free/Developer : gRPC Devnet ; Yellowstone full fidelity ; Mainnet gRPC payant/add-on | profil Devnet + x-token operator | P0 Devnet live ; Mainnet non requis | | Helius standard WSS | tx extension disponible ; blockSubscribe explicitement non supporté ; 10 min inactivity | dépend de clé/tier opérateur | txSubscribe adapter fixture + smoke opt-in si accès | | Helius LaserStream | reconnect + replay jusqu’à 24 h documentés | Mainnet/tiers selon compte ; ne pas supposer gratuit | architecture compatible, live non bloquant pour release si tier absent | | QuickNode | Yellowstone Scale+ ; port 443/x-token ; port 10000 legacy sunset 1 octobre 2026 | pas de tier gRPC actuel | bloqué tier ; aucun profil 10000 à introduire | | Alchemy | Yellowstone Mainnet/Devnet PAYG/Enterprise | pas de tier actuel | bloqué ; replay non constant | | Jito ShredStream | shutdown annoncé pour le 5 septembre 2026, migration DoubleZero recommandée | aucun besoin | `SUNSET`, ne pas implémenter TR-F dessus | | EARLY autres | protocoles/prix variables | variable | adapter seulement après protocole+accès+RAW role prouvés | Alchemy reste volontairement `À REVALIDER` sur la profondeur replay : sa page dédiée Historical Replay annonce environ `432000` slots / `48 h`, tandis que son overview/SubscribeRequest mentionne encore `6000` slots. KSP ne doit encoder **aucune** constante Alchemy ; le runtime se fie à une capability/replay info observée ou à un paramètre configuré/prouvé. ## 8. Surface publique minimale du Worker concret La V1 cible la surface suivante, sans paramètre historique métier : ```text RAW_TRANSACTION_INGEST_WORKER_KIND_CODE RawTransactionIngestSettings RawTransactionSourceId RawTransactionSourceSettings RawTransactionSourceCapability RawTransactionSourceRole RawTransactionIngestWorker RawTransactionIngestHandle RawTransactionIngestSnapshot RawTransactionIngestSnapshotSource RawTransactionSourceSnapshot RawTransactionContinuityState ERROR_CODE_RAW_TRANSACTION_INGEST_SETTINGS_INVALID ERROR_CODE_RAW_TRANSACTION_INGEST_RUNTIME_INVALID ERROR_CODE_RAW_TRANSACTION_INGEST_SOURCE_FAILED ERROR_CODE_RAW_TRANSACTION_INGEST_CONTINUITY_LOST ERROR_CODE_RAW_TRANSACTION_INGEST_CONTENT_CONFLICT ``` Le start reçoit uniquement des réglages techniques et ressources préparées : ```text network worker identity liste de sources activées safe source/role/filter ids priorités / required-vs-optional bounds techniques queues/concurrency/poll/retry Transport runtime/settings déjà résolus Store façade déjà ouverte/cohérente réseau ``` Interdit au start : ```text signature program_id address before/after slot range historique historical limit BackfillRequest BackfillCheckpoint JobId ``` ## 9. Ownership runtime et supervision ### 9.1 Executor et tâches Décision : le caller fournit un runtime Tokio actif ; le Worker **n’instancie pas son propre runtime**. Le Worker possède les tâches qu’il spawn sur cet executor et ne rend jamais leurs `JoinHandle` publics. Schéma : ```text start -> lifecycle Starting -> supervisor task -> source tasks N -> central admission/persistence task -> continuity/repair coordination -> latest-value publisher -> lifecycle Running ``` `RawTransactionIngestHandle` expose seulement : ```text request_stop() snapshot_source() worker_snapshot_source()/projection commune wait_terminal() ou équivalent borné par le caller ``` La surface exacte de wait doit rester async et ne pas exposer Tokio nominalement si une future abstraction simple suffit ; l’implémentation privée peut utiliser Tokio. ### 9.2 Start/stop Séquence normale : ```text Created -> Starting -> Running -> Stopping -> Stopped ``` Fault : ```text Starting/Running/Stopping -> Faulted(static ErrorCode) ``` Stop : ```text request_stop idempotent stop admissions nouvelles signaler source tasks half-close/close streams avec les primitives Transport existantes drainer seulement le travail déjà admis dans une borne explicite persister les dernières acquisitions admises publier snapshot terminal terminer supervisor ``` Aucune source ne doit survivre à la terminaison du Worker. ### 9.3 Source supervisor Une source possède au minimum : ```text source_id sûr capability role alternative/complementary/redundant/specialized required/optional priority health activity continuity class counters retry/reconnect state borné ``` Le supervisor isole une source défaillante sans arrêter automatiquement les sources saines. Il recalcule `WorkerHealth` : ```text Healthy = toutes les garanties requises sont satisfaites Degraded = au moins une source/garantie optionnelle ou réparable est dégradée Unhealthy = garantie requise non satisfaite mais policy de recovery encore active Faulted = décision terminale après budget/policy ou conflit d’intégrité ``` ## 10. Multi-source concurrency et backpressure ### 10.1 Pipeline interne ```text source task -> bounded acquisition channel -> classify full / discovery / continuity -> normalize or hydrate -> canonicalize common RAW -> Store atomic acquisition -> update durable frontier/counters -> latest-value snapshot ``` Aucune queue unbounded. Les bornes doivent être worker-owned et validées, avec defaults conservateurs et maximums publics seulement lorsqu’ils constituent une vraie limite de sécurité. Une saturation de queue n’est jamais un drop silencieux : ```text si upstream peut backpressure -> attendre dans deadline bornée sinon -> marquer gap/saturation -> lancer repair si possible si repair impossible et garantie requise -> Degraded/Unhealthy puis Faulted selon policy ``` ### 10.2 Admission et dedup Identité de convergence : ```text (network, signature) ``` Règles : ```text une acquisition full par source produit son observation propre le Store décide entity new/idempotent/content-conflict une observation identique est idempotente via sa producer-owned key une observation d’une autre source ne doit pas être supprimée parce que l’entité existe déjà un signal discovery incomplet ne devient pas une RawTransactionObservation mensongère les hydrations duplicate d’une même signature peuvent être coalescées en mémoire pour réduire l’I/O, mais uniquement si la provenance durable du matériau complet reste exacte ``` Le cache de coalescence est borné et optimisation-only. La correction reste garantie par Store. ### 10.3 Content conflict `même (network, signature) + payload canonique divergent` est une faute d’intégrité, pas une concurrence normale. Policy V1 : ```text arrêter les nouvelles admissions conserver le code statique de conflit ne pas choisir un provider gagnant ne pas réécrire l’entité publier Faulted(ERROR_CODE_RAW_TRANSACTION_INGEST_CONTENT_CONFLICT) ``` Le payload/signature/hash divergent ne doit pas apparaître dans logs/snapshots publics. ## 11. Continuité et gap repair ### 11.1 Frontier de run Le Worker fixe au démarrage un `run_start_frontier`. Il ne répare jamais arbitrairement avant cette borne à la demande d’un caller. La continuité est suivie **par source et par classe de couverture**, car toutes les sources ne prouvent pas la même chose : ```text block-complete coverage filtered transaction coverage signal/discovery coverage best-effort live coverage ``` Un simple `last_seen_slot` ne prouve pas l’absence de transactions manquantes. ### 11.2 Ordre de repair Pour un gap né pendant le run : ```text 1. replay natif adressable qualifié (Yellowstone from_slot) 2. source redondante dont la couverture prouve la plage 3. scan HTTP des slots/blocs manquants 4. hydration HTTP des signatures découvertes 5. gap explicite non résolu si aucune preuve suffisante ``` Le Worker reprend un statut Healthy seulement lorsque la preuve de couverture attendue est rétablie. ### 11.3 Bornes Le runtime doit imposer : ```text nombre maximal de gaps ouverts largeur maximale d’un repair HTTP par incident concurrency de repair retry/backoff max replay start >= run_start_frontier sauf bootstrap technique immédiat décidé au start pas de boucle infinie autour d’un slot indisponible ``` Une fenêtre replay provider est une donnée de capability, pas une constante provider hardcodée dans le Worker. ### 11.4 Reconnect ≠ replay ```text WS reconnect + resubscribe -> connexion rétablie -> continuity UNKNOWN jusqu’à preuve/repair Yellowstone reconnect avec from_slot prouvé -> replay demandé -> continuity REPAIRING -> continuity PROVEN seulement après rattrapage/validation ``` ## 12. Snapshots et notifications concrètes `ksp-worker-api` reste inchangé. Le Worker concret publie deux projections latest-value : ```text WorkerSnapshot commun RawTransactionIngestSnapshot concret ``` Le snapshot concret minimal contient uniquement des données sûres : ```text worker id/kind/state/health/activity/sequence run start frontier last durable frontier global lorsqu’il est significatif source count healthy/degraded/unhealthy/failed source counts received signal count full material count hydration requested/completed/missing counts entity new/idempotent counts observation new/idempotent counts content conflict count gaps detected/repaired/unresolved counts repair active bool/count per-source: source_id, capability code, health, activity, continuity state, safe counters, slots/frontiers ``` Pas de : ```text signature transaction bytes/meta/logs endpoint URL API key/token/header raw remote error text provider payload filter/account values sensibles ``` Pour les rates, la V1 publie des compteurs monotones et des timestamps/sequence sûrs suffisants pour calculer des rates sans introduire de float métier ni event bus. Un rate pré-calculé n’entre dans l’API que si une mesure concrète l’exige pendant l’implémentation. Le publisher est latest-value/coalescent : lecteur lent ou absent n’influence jamais le lifecycle. ## 13. Décision Config/composition ### 13.1 Aucun edge Worker -> Config La décision du prompt est conservée : ```text ksp-worker-raw-transaction-ingest-lib -X-> ksp-config-lib ``` Le Worker reçoit des settings source-neutral validés et des ressources Transport/Store préparées. ### 13.2 Pas de nouveau document métier Worker en `pre.001` `std.transport` V3 possède déjà : ```text network/cluster endpoint id provider HTTP roles + priority + limits WS protocol kind + session settings gRPC protocol + metadata publique/secrète + session settings ``` Cela suffit pour matérialiser et tester la bibliothèque Worker programmatiquement. `0.3.10` n’ajoute pas par défaut un `std.raw_transaction_ingest` qui forcerait `ksp-config-lib` à dépendre du Worker concret. Le choix utilisateur/composition de « quelles sources activer » appartient naturellement au futur `ksp-app-raw-transaction-ingest-desk` `0.3.11`. Si `0.3.10` découvre un manque **Transport** indispensable (par exemple un profil Helius de preuve ou un rôle HTTP live), l’adaptation Config reste limitée au standard Transport et ne crée pas de dépendance inversée. ### 13.3 Secrets ```text Config résout les secrets Transport settings les reçoivent sous wrappers redacted Worker ne lit jamais std::env/.env Worker ne logge jamais endpoint/token/header source snapshot n’expose que source_id/capability/health ``` `KSP_SECRET_HELIUS_API_KEY` reste l’unique secret Helius lorsqu’un profil Helius est ajouté ; ne pas multiplier des secrets par méthode. ## 14. Threat model `0.3.10` | Risque | Impact | Garde prévue | |:--------------------------------------------|:------------------------------------|:------------------------------------------------------------------------------| | divergence RAW entre sources | corruption logique / conflict flood | parity golden cross-source avant direct persist ; conflict terminal explicite | | provider payload hostile/oversized | mémoire/CPU | bornes Transport existantes + material/common bounds | | queue saturation | perte silencieuse | aucun drop silencieux ; gap + repair/degraded | | source lente bloquant toutes les autres | arrêt global | source tasks indépendantes + bounded central admission | | duplicate storm replay/multi-source | I/O/CPU | Store idempotence + cache de coalescence borné | | replay trop ancien | faux lossless | replay info/capability + run frontier + fallback HTTP ou gap explicite | | reconnect pris pour continuity | données manquantes | états distincts reconnect/resubscribe/replay/repair | | full provider incomplet | RAW invalide | completeness/parity gate ; hydration fallback | | content conflict | first-provider-wins | terminal conflict, aucune overwrite | | Store indisponible | backlog infini | pause/admission bornée + retry budget + fault | | source endpoint/secret dans Debug | fuite | wrappers redacted + security tests + static scans | | observation key collision inter-producteurs | perte de provenance | key producer-owned avec domaine Worker distinct du Backfill | | Job semantics dans Worker | couplage architectural | tests de dependency/public surface ; aucun request historique | | Worker semantics dans common | cycle/couplage | common sans Worker/Job/runtime | | provider-specific enum fermé | dette extension | capability-oriented settings ; provider reste metadata Transport | | transaction V1 protobuf perdu | faux canonical | proto 12.7 courant + fixture V1 + parity | | Yellowstone block replay regression | gap silencieux | smoke block replay dédié après fix upstream | | alias `mainnet-beta` réintroduit | split identity Store | mainnet canonique aux frontières KSP ; alias seulement adapter externe | ## 15. Stratégie de preuves ### 15.1 Tests déterministes Common : ```text golden RAW v1 exact bytes/hash existant omitted/null/value meta/version/index canonical JSON object ordering arrays ordering string escaping/numbers block time negative/overflow/max signature Base58 malformed/length/64-byte exact payload max bounds Debug/error redaction external consumer crate-root only manifest/dependency boundary ``` Transport/adapters : ```text get_block_observed winner provenance HTTP block -> material WS block -> material Helius full -> material Yellowstone transaction -> material Yellowstone block -> N materials status/logs -> hydration candidate only Transaction V1 config fixture cross-source parity golden ``` Worker : ```text start/stop state machine multi-source concurrency slow/failing source isolation bounded channel saturation dedup/coalescence multiple observations same entity content conflict terminal path Store retry/failure path gap detection native replay state redundant-source repair HTTP block repair unresolved gap degraded/fault policy shutdown drain bound latest-value slow/late listeners snapshot redaction no historical start parameters ``` ### 15.2 Integration locale Prévoir des fake sources/fixtures sans réseau pour prouver : ```text source -> common -> Store façade fake multi-source same entity + distinct observations conflicting entity out-of-order slots replay duplicates repair succeeds/fails stop while source/hydration/store future pending ``` Aucun PostgreSQL physique requis pour ces tests unitaires/integration de bibliothèque. ### 15.3 Store persistence proof Le gate final doit inclure au moins un smoke opt-in sur un Store réel disponible : ```text une transaction nouvelle réacquisition idempotente observation additionnelle autre source content conflict explicitement rejeté shutdown propre ``` Le Worker dépend uniquement de `ksp-store-lib` ; le test live peut activer le backend via feature/composition supérieure. ### 15.4 Smokes live accessibles Priorité : ```text Solana public Devnet HTTP getTransaction/getBlock Solana public Devnet WS logs PublicNode Mainnet/Testnet Yellowstone si credentials KSP disponibles OrbitFlare Devnet Yellowstone avec licence x-token opérateur Helius standard/transactionSubscribe uniquement si clé+tier disponibles ``` Tous les tests réseau, secrets ou tiers sont opt-in/ignored. ### 15.5 Replay/continuity proofs Séparer obligatoirement : ```text Yellowstone transaction replay from_slot Yellowstone block replay from_slot replay duplicate dedup replay out-of-window failure reconnect sans replay -> continuity unknown/degraded HTTP repair d’un gap de run contrôlé ``` Une preuve sur `transactions` ne valide pas automatiquement `blocks`. ## 16. Graphe de dépendances cible ```text ksp-raw-transaction-lib -> ksp-core-lib ksp-raw-transaction-lib -> ksp-store-api -> ksp-core-lib ksp-job-backfill-lib -> ksp-raw-transaction-lib ksp-job-backfill-lib -> ksp-store-lib -> ksp-store-api ksp-job-backfill-lib -> ksp-onchain-transport-lib ksp-job-backfill-lib -> ksp-job-api ksp-worker-raw-transaction-ingest-lib -> ksp-raw-transaction-lib ksp-worker-raw-transaction-ingest-lib -> ksp-store-lib -> ksp-store-api ksp-worker-raw-transaction-ingest-lib -> ksp-onchain-transport-lib ksp-worker-raw-transaction-ingest-lib -> ksp-worker-api ksp-worker-raw-transaction-ingest-lib -> ksp-logging-lib ``` Lecture des flèches du schéma : les consumers dépendent des lower layers ; aucun lower layer ne dépend d’un consumer. `ksp-raw-transaction-lib` dépend directement de `ksp-store-api` comme pipeline RAW réutilisable ; `ksp-store-lib` reste la façade runtime utilisée séparément par les producteurs concrets pour la persistence. Edges exacts attendus : ```text ksp-raw-transaction-lib -> ksp-core-lib -> ksp-store-api -> serde_json -> sha2 ksp-job-backfill-lib -> ksp-raw-transaction-lib -> ksp-job-api -> ksp-onchain-transport-lib -> ksp-store-lib default-features=false -> dépendances runtime existantes ksp-worker-raw-transaction-ingest-lib -> ksp-worker-api -> ksp-onchain-transport-lib -> ksp-raw-transaction-lib -> ksp-store-lib default-features=false -> ksp-logging-lib -> tokio/futures uniquement si l’implémentation concrète les exige ``` `ksp-interface-lib` reste absent sauf nécessité passive démontrée. ## 17. Sizing et trajectoire recalibrée La prévision initiale du prompt est trop dense pour certaines tranches si chacune doit rester autour de 15–20 minutes effectives. Le plan retenu sépare canonicalisation, parité, runtime et sources : ### `pre.001` — audit/sizing/plan Présente tranche : archive/règles, common extraction, capability matrix, fraîcheur, runtime, continuity, threat model, preuves et graphe. Aucune implémentation lourde. ### `pre.002` — common RAW foundation **Statut : réalisé.** `ksp-raw-transaction-lib` matérialise les types source-neutral minimaux, le parser Base58 strict vers 64 octets, la canonicalisation JSON RAW v1, SHA-256 et l’assemblage transaction + observation producer-owned. Le golden `112` bytes / `220792...a7c3` est verrouillé par test. Aucun Worker ni migration Backfill n’est ouvert. La matérialisation a révélé une correction normative du sizing `pre.001` : `DEP-PIPE-006` impose `ksp-store-api` au pipeline RAW et interdit `ksp-store-lib`. Le graphe et la section 5.4 sont donc corrigés dans cette tranche sans changer le contrat fonctionnel RAW. La validation opérateur du 2026-09-06 confirme `cargo check --workspace`, les audits statiques et les tests de la crate, mais révèle un échec du gate Clippy `-D warnings` limité aux tests d’intégration : quatre crates de test sans rustdoc de crate et un `clippy::collapsible_if`. `pre.002-fix.001` corrige uniquement ces canaris, sans modifier le contrat RAW ni commencer `pre.003`; le signal Cargo devient `0.3.10-pre.2.fix.1`. ### `pre.003` — migration Backfill vers common Remplacer la canonicalisation privée du Backfill par common, conserver son observation key/provenance/campagne et prouver zéro changement bytes/hash/comportement de campagne. ### `pre.004` — HTTP observed block + block material Ajouter `get_block_observed`, fixtures provenance, extraction transaction par transaction et adapters HTTP block vers matériau common. Aucun Worker live complet encore. ### `pre.005` — parité WS/Helius full Fermer projection `blockSubscribe` + Helius `transactionSubscribe` vers matériau common, golden parity ou fallback hydration explicitement qualifié. ### `pre.006` — parité Yellowstone transactions/blocks Fermer les adapters structurés Yellowstone transaction/block, Transaction V1, meta et cross-source parity. Aucune persistance directe si le gate byte-identical échoue. ### `pre.007` — Worker foundation/runtime Créer la crate Worker concrète, settings, handle, lifecycle, supervisor, source abstraction interne, bounded channel, common/concrete snapshots et shutdown sans source live complexe. ### `pre.008` — Yellowstone live + continuity Brancher transactions/blocks/status, source health, from_slot/replay info, run frontier et tests déterministes de replay/duplicates. ### `pre.009` — WS/HTTP live Brancher logs+hydration, blockSubscribe, HTTP live block polling/hydration et Helius transactionSubscribe selon capability/access. ### `pre.010` — multi-source persistence/dedup/hardening Convergence Store, observations multiples, content conflicts, coalescence bornée, backpressure, source failure isolation et adversarial tests. ### `pre.011` — gap repair complet Replay natif, redondance, HTTP block scan, hydration, unresolved gaps, policy degraded/unhealthy/faulted et shutdown pendant repair. ### `pre.012` — Config/profiles + provider smokes accessibles Ajouter seulement les profils/roles Transport réellement nécessaires, exécuter les smokes gratuits/credentials disponibles et documenter clairement les tiers bloqués. Pas d’edge Worker -> Config. ### `pre.013` — EARLY si prouvé, sinon consolidation N’ajouter un adapter EARLY que si protocole, accès et rôle RAW sont prouvés. Sinon consacrer la tranche aux dettes/hardening découvertes ; ne pas créer de faux support. ### `pre.014` — gate technique/live final Workspace complet, all-targets/all-features, cargo trees, duplicate tree, smokes pertinents, continuity repair et Store proof. ### `pre.015` — réconciliation documentaire finale README/USAGE/architecture/plan/validation uniquement ; aucun rattrapage fonctionnel. ### `pre.016` — préparation de publication Prompt `0.3.11`, CHANGELOG, ROADMAP et fichiers mécaniques de version/delta uniquement. ### `rel.001` — publication stable Publication mécanique `v0.3.10` sans rattrapage code/doc. Cette trajectoire reste souple : une tranche réellement petite peut être fusionnée, mais aucune tranche > environ 20 minutes ne doit être artificiellement conservée monolithique. ## 18. Gates Cargo futurs obligatoires Dès création des nouvelles crates et à la fermeture technique : ```bash cargo fmt --all python3 scripts/audit_rust_workspace_rules.py python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas cargo check --workspace cargo clippy --workspace --all-targets --all-features -- -D warnings cargo test --workspace --all-targets --all-features cargo tree -p ksp-raw-transaction-lib --edges normal cargo tree -p ksp-worker-raw-transaction-ingest-lib --edges normal cargo tree -p ksp-worker-raw-transaction-ingest-lib -e features cargo tree --duplicates ``` Les smokes live restent séparés et opt-in. ## 19. Critères de sortie `pre.001` | Critère du prompt | Fermeture | |:------------------------------------------|:--------------------------------------------------------------| | boundary exacte common crate | définie sections 5.2–5.5 | | migration Backfill sans changement RAW v1 | golden + ownership décidés sections 5.1–5.3 | | surface publique Worker concrète | définie section 8 | | runtime/source supervision | définis sections 9–10 | | capability matrix implementation-ready | section 6.1 | | TR-B..TR-F réévalués | sections 6.2–6.6 | | Config/source settings sans secret leak | section 13 | | continuity/gap repair bornés | section 11 | | snapshot concret | section 12 | | multi-source dedup/provenance/persistence | section 10 | | provider/access/proof matrix | section 7 | | tests/smokes | section 15 | | dependency graph | section 16 | | forecast détaillé dimensionné | section 17 | | aucune implémentation lourde avant plan | respecté : seulement docs + version prerelease dans `pre.001` | ## 20. Hors périmètre confirmé ```text ksp-app-raw-transaction-ingest-desk # 0.3.11 nouvelles stratégies historiques Backfill # 0.3.12 paramètres métier historiques dans Worker Backfill checkpoint dans Worker RawAccountState ingest worker Program/DEX/materialization backend PostgreSQL direct provider SDK control plane global RAW v2 source Jito ShredStream sunset ``` ## 21. Sources externes de fraîcheur Consultées le 5 septembre 2026 uniquement pour les faits pouvant changer et influer sur l’implémentation : ```text https://solana.com/docs/rpc/http/getblock https://solana.com/docs/rpc/websocket/blocksubscribe https://solana.com/docs/rpc/websocket/logssubscribe https://solana.com/docs/rpc/websocket/signaturesubscribe https://docs.rs/crate/yellowstone-grpc-proto/latest https://github.com/rpcpool/yellowstone-grpc/blob/master/CHANGELOG.md https://github.com/rpcpool/yellowstone-grpc/blob/master/yellowstone-grpc-proto/proto/geyser.proto https://www.helius.dev/docs/api-reference/rpc/websocket/transactionsubscribe https://www.helius.dev/docs/api-reference/rpc/websocket/blocksubscribe https://www.helius.dev/laserstream https://solana-yellowstone-grpc.publicnode.com/ https://orbitflare.com/pricing https://docs.orbitflare.com/data-streaming/yellowstone https://www.quicknode.com/docs/solana/solana-grpc/overview https://www.alchemy.com/docs/reference/yellowstone-grpc-overview https://www.alchemy.com/docs/reference/yellowstone-grpc-historical-replay https://docs.jito.wtf/lowlatencytxnfeed/ ``` Les prix, tiers, profondeurs de replay et disponibilités provider restent des données d’audit datées, jamais des constantes métier du Worker.