56 KiB
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 :
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 :
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 :
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 :
SHA-256 = 41ef2589a11410aabfb329d387f0008e116006da60ada0802e5f72bc30d3466f
ZIP bytes = 7859118
ZIP entries = 1855
unzip -t = PASS
files extraits = 1674
directories = 183
Contrôles structurels du ZIP :
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 :
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 :
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 :
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 :
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 :
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 :
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 :
RAW_TRANSACTION_FORMAT_ID
RAW_TRANSACTION_FORMAT_VERSION
MIN_RAW_TRANSACTION_SIGNATURE_TEXT_BYTES = 64
MAX_RAW_TRANSACTION_SIGNATURE_TEXT_BYTES = 88
RawTransactionMaterial
RawTransactionWireField<T>
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 :
RawTransactionMaterial
network: RawNetworkId
signature: RawTransactionSignature
slot: u64
block_time: Option<i64>
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<T> 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 :
ksp-raw-transaction-lib
-> ksp-core-lib
-> ksp-store-api
-> serde_json
-> sha2
Justification :
ksp-core-libest nécessaire aux codes/erreurs KSP propres à la canonicalisation ;ksp-store-apifournit directement les modèles canoniquesRawTransaction,RawPayload, provenance et primitives, conformément àDEP-PIPE-006;serde_jsonreste requis par RAW v1 pour canonicalisermetaet préserver les états wire ;sha2reste requis pour le digest canonique exact.
Interdits :
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 :
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 :
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<T> 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 :
HttpTransportPool::get_block_observed(role, slot, config)
-> Result<HttpObservedValue<Option<SolanaConfirmedBlock>>>
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 :
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 :
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 :
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 :
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 :
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 :
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 :
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 :
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 :
start
-> lifecycle Starting
-> supervisor task
-> source tasks N
-> central admission/persistence task
-> continuity/repair coordination
-> latest-value publisher
-> lifecycle Running
RawTransactionIngestHandle expose seulement :
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 :
Created -> Starting -> Running -> Stopping -> Stopped
Fault :
Starting/Running/Stopping -> Faulted(static ErrorCode)
Stop :
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 :
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 :
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
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 :
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 :
(network, signature)
Règles :
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 :
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 :
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 :
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 :
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
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 :
WorkerSnapshot commun
RawTransactionIngestSnapshot concret
Le snapshot concret minimal contient uniquement des données sûres :
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 :
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 :
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à :
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
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 :
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 :
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 :
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 :
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 :
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é :
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 :
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
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 :
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 :
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é
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 :
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.