28 KiB
Plan v0.3.7 — Backfill Desk
1. But de la version
La v0.3.7 crée ksp-app-backfill-desk, application Tauri spécialisée qui compose Config, Transport, Store et ksp-job-backfill-lib afin de lancer, observer, annuler et reprendre un backfill historique borné.
L'application reste une couche de composition et d'interface. Elle ne possède ni découverte RPC, ni retry/rate-limit, ni persistance, ni frontier/checkpoint, ni navigation Store générale.
Chaîne cible :
Config composite/profile
-> Logging
-> Transport HTTP
-> Store
-> AppState Rust
-> BackfillRequest
-> BackfillJobRuntime
-> BackfillJobHandle
-> BackfillSnapshotSource
-> DTO Tauri sûrs
-> UI latest-value
2. Base et audits de pre.001
Base autoritaire : archive stable v0.3.6, workspace.package.version = 0.3.6, avec deltas/0.3.6/rel.001.md, ksp-job-api, ksp-job-backfill-lib et les trois Desks existants.
Les deux archives ont été testées intégralement avant utilisation et extraites dans des arbres séparés. kbot3 reste une référence fonctionnelle/UX ; aucun code, DTO, commande Tauri ou algorithme n'en est repris.
Sources internes relues : règles racine et docs/rules/, architectures Layers/Dependencies/Components/Jobs/Apps, clôture complète de v0.3.6, surfaces Rust Job/Backfill/Config/Transport/Store et gabarits des Desks Config, Wallet et SOL Prices.
L'audit externe du 2026-09-02 confirme que le socle Tauri v2, son modèle capabilities/runtime authority et les versions courantes restent compatibles avec les contraintes existantes. Le workspace résout déjà tauri 2.11.5, tauri-build 2.6.3, tauri-plugin-tracing 0.3.4 et ts-rs 12.0.1; les manifests frontend utilisent @tauri-apps/api ^2.11, @tauri-apps/cli ^2.11, TypeScript ^7.0 et Vite ^8.2. Une version plus récente de Vite existe, mais aucun besoin fonctionnel ne justifie une mise à niveau en v0.3.7.
3. Surface Backfill réellement disponible
ksp-job-backfill-lib expose déjà les quatre scopes LatestAddress, BeforeAddress, AfterAddress et ExplicitSignatures, les commitments Confirmed/Finalized, les bornes publiques, BackfillRequest, BackfillCheckpoint, BackfillJobRuntime, BackfillJobHandle et BackfillSnapshotSource.
Le snapshot sûr expose phase, scope kind, boundary, candidats selected/admitted/finished, entities inserted/existing/purged, missing, conflicts, observations inserted/existing, cancelled candidates, holes, maximum in-flight, contiguous completed, présence d'un checkpoint et failure code. L'UI ne fabrique aucun pourcentage global lorsque le dénominateur n'est pas connu.
Bornes backend-owned :
| Paramètre | Borne publique |
|---|---|
| page size | 1..=1000 |
| max pages | 1..=10000 |
| max candidates | 1..=10000 |
| hydration concurrency | 1..=64 |
| signature Base58 | 64..=88 octets texte, validation Rust exacte |
min_context_slot est admis pour les scopes adresse et rejeté pour ExplicitSignatures par BackfillRequest.
4. Matrice kbot3
| Capacité historique | Preuve fonctionnelle kbot3 | Équivalent KSP v0.3.6 | Décision |
|---|---|---|---|
| Formulaire de campagne | demo_backfill.html + request TS/Rust |
BackfillRequest explicite et borné |
REPRENDRE FONCTIONNELLEMENT |
| Signatures explicites | textarea + mode explicit_signatures |
BackfillScope::explicit_signatures |
REPRENDRE FONCTIONNELLEMENT |
| Adresse + direction | address, anchor, before/after | Latest/Before/After address distincts | REDESSINER POUR KSP |
| Commitment | sélecteur confirmed/finalized | BackfillCommitment |
REPRENDRE FONCTIONNELLEMENT |
| Rôle logique | rôle HTTP sélectionnable | HttpRoleName + Transport |
REDESSINER POUR KSP |
| Provider/endpoint affichés | options et summary | Transport possède routage physique | REJETER |
| Retry opérateur | maxRetries UI |
Transport possède retry/pacing | REJETER |
| Page size/max pages/limit | inputs bornés | constantes Backfill publiques | REPRENDRE FONCTIONNELLEMENT |
| Concurrency | input opérateur | hydration_concurrency |
REPRENDRE FONCTIONNELLEMENT |
| Program/token/pool modes | accordéons dédiés | scopes génériques adresse/signatures | REJETER comme types KSP |
| Progression live | événements textuels + log | snapshots latest-value structurés | REDESSINER POUR KSP |
| Annulation | bool atomique + commande | BackfillJobHandle::cancel() |
REDESSINER POUR KSP |
| Résumé terminal | payload summary | notification/snapshot terminal | REPRENDRE FONCTIONNELLEMENT |
| Compteurs inserted/existing/missing/failed/observations | summary | compteurs BackfillJobSnapshot |
REDESSINER POUR KSP |
| Cursor older-history | resume_before_signature |
BackfillCheckpoint opaque |
REDESSINER POUR KSP |
| Reprise durable | cursor texte retourné | aucun checkpoint sérialisable public | REPORTER |
| Single-run UI | flag running |
runtime single-run + handle | REPRENDRE FONCTIONNELLEMENT |
| Store browsing | demo desktop monolithique | futur Store Desk v0.3.8 | REJETER |
5. Screen map
Un seul écran principal responsive suffit en V1, en plus du splash commun.
main
├─ header : Backfill Desk + statut runtime/config
├─ readiness : profil composite, réseau, rôle(s) compatibles, Store/Transport ready
├─ campaign form
│ ├─ scope kind
│ ├─ address / anchor / signatures selon scope
│ ├─ commitment + rôle logique
│ └─ page size / max pages / max candidates / concurrency / min_context_slot
├─ active job
│ ├─ lifecycle + phase
│ ├─ boundary/frontier
│ ├─ compteurs structurés
│ ├─ checkpoint présent + contiguous completed
│ └─ Start / Cancel selon état
├─ terminal summary
│ ├─ outcome/lifecycle
│ ├─ failure code sûr
│ └─ Resume / New campaign selon checkpoint et scope
└─ diagnostics sûrs : codes, jamais secrets/URL/RAW
États UI explicites : booting, ready, starting, running, cancelling, completed, cancelled, failed, resume-ready. cancelling ne prétend jamais que la persistance déjà soumise est interrompue.
6. DTO et command map
Les DTOs sont possédés par l'application et dérivés TS-RS uniquement à cette frontière.
| Élément | Direction | Contenu | Validation / source de vérité | Sensibilité |
|---|---|---|---|---|
BackfillDeskOptionsDto |
Rust -> UI | réseau, rôles compatibles, commitments, scopes, bornes, readiness | Config + Transport + constantes Backfill | sûre |
BackfillStartRequestDto |
UI -> Rust | scope inputs, commitment, rôle, bornes, optional min context slot | conversion stricte vers types KSP | métier borné |
BackfillRunStatusDto |
Rust -> UI | job state, phase, compteurs, boundary, checkpoint_present, contiguous_completed, failure code | JobNotification<BackfillJobSnapshot> |
sûre |
BackfillCancelResponseDto |
Rust -> UI | accepted + état courant | BackfillJobHandle |
sûre |
BackfillResumeResponseDto |
Rust -> UI | accepted/new job status | checkpoint Rust détenu par AppState | sûre |
SafeAppErrorDto |
Rust -> UI | domain/code + message app maîtrisé | mapping applicatif | sûre |
Commandes prévues :
backfill_options() -> BackfillDeskOptionsDto
backfill_status() -> BackfillRunStatusDto
backfill_start(request) -> BackfillRunStatusDto
backfill_cancel() -> BackfillCancelResponseDto
backfill_resume() -> BackfillResumeResponseDto
backfill_reset() -> BackfillRunStatusDto
Le monitoring continu ne dépend pas du parsing de logs. Le backend possède un bridge latest-value qui attend JobSnapshotSource::wait_for_change, projette le snapshot et émet un événement Tauri borné, par exemple backfill-status-changed. backfill_status reste le chemin de resynchronisation initiale/explicite.
Aucun BackfillCheckpoint, Store, HttpTransportPool, sender/receiver Tokio ni payload RAW n'est sérialisé vers le frontend.
7. Config et composition
Le composite cible est config/composite.ksp-app-backfill-desk.json avec trois profils cohérents :
| Profil composite | Logging | Transport | Store | Réseau |
|---|---|---|---|---|
devnet |
supertrace |
devnet_public |
devnet |
devnet |
mainnet |
supertrace |
mainnet_backfill_pool |
mainnet |
mainnet-beta |
testnet |
console_info |
publicnode_testnet |
testnet |
testnet |
Le default V1 est désormais mainnet, conformément à la finalité lecture seule du Backfill Desk. devnet et testnet restent sélectionnables par reconfiguration du composite. Pendant le développement de 0.3.7, Mainnet conserve le profil Logging supertrace. Aucun std.job_backfill.json n'est créé : les paramètres de campagne restent opérationnels et backend-validés.
Au bootstrap, l'application charge le composite, résout Logging/Transport/Store via ksp-config-lib, construit HttpTransportPool et ouvre ksp_store_lib::Store. Elle compare le réseau Store au(x) cluster(s) HTTP activés et refuse le readiness si une composition mélange des réseaux.
Les routes HTTP présentées à l'UI sont dérivées des snapshots/settings Transport, dédupliquées, puis validées côté Rust comme capables de sélectionner à la fois getSignaturesForAddress et getTransaction. Sur Mainnet, backfill_pool regroupe Solana Public et PublicNode à priorité égale, tandis que backfill_solana_public et backfill_publicnode ciblent un provider unique. Le frontend ne reçoit que le rôle logique et les labels provider sûrs ; URL, credentials, rate-limit interne et retry restent possédés par Transport.
Le packaging Config suit le contrat atomique déjà détenu par ksp-config-lib::prepare_packaged_runtime() : il traverse l'intégralité de ConfigFileRegistry::defaults(). L'ajout du composite Backfill Desk fait donc passer le registre de 13 à 14 descriptors et impose que les quatre applications Desk qui utilisent ce packaging embarquent les 14 resources Config/schema du registre. Backfill Desk embarque ainsi également les documents Wallet/offchain sans les consommer au runtime ; leur présence satisfait le contrat de packaging partagé et n'ouvre aucune dépendance, permission frontend ou responsabilité métier supplémentaire.
8. Dependency map
Dépendances Rust normales envisagées :
ksp-app-backfill-desk
├─ ksp-core-lib # erreurs, Pubkey/app DTO si requis
├─ ksp-logging-lib # façade logging
├─ ksp-config-lib # composition et packaging Config
├─ ksp-job-api # JobState/JobSnapshotSource consommés directement
├─ ksp-job-backfill-lib # request/runtime/handle/snapshot
├─ ksp-onchain-transport-lib # construction HttpTransportPool + role/method readiness
├─ ksp-store-lib # Store::open et ressource runtime
├─ serde # DTO IPC
├─ tauri
├─ tauri-plugin-tracing
├─ tokio # spawn/synchronisation app si gabarit Tauri l'exige
└─ ts-rs # bindings DTO applicatifs
ksp-job-backfill-lib dépend déjà de Store avec default-features = false. Le Desk devra cependant ouvrir le Store concret ; la feature backend de ksp-store-lib doit donc être explicitement auditée au scaffold. Le pattern cible est une dépendance sur ksp-store-lib uniquement, avec le backend compilé par la feature de façade adéquate, jamais une dépendance directe sur ksp-store-postgres-lib.
Dépendances interdites : ksp-store-api direct, ksp-store-postgres-lib, tokio-postgres, reqwest, tonic, yellowstone-grpc-proto, SDK provider.
9. Single-run lifecycle et races
AppState conserve au plus : ressources runtime stables, un slot de run actif, le dernier snapshot terminal et le dernier checkpoint sûr associé à la requête/backend state nécessaires à une reprise en session.
Règles :
- start atomique : refus si un run est
starting/running/cancelling; - le handle est installé avant spawn afin que Cancel ne perde pas la course au démarrage ;
- Cancel répété est idempotent au niveau UX, mais le retour expose si la demande a été acceptée ;
- un terminal publié gagne sur un Cancel tardif ;
- la source latest-value est conservée jusqu'à projection du terminal ;
- reset/new campaign n'est autorisé qu'en absence de run actif ;
- fermeture de l'application déclenche une demande d'annulation coopérative best-effort puis laisse la destruction du process terminer les ressources ; aucune promesse de drain crash-safe n'est faite ;
- aucun registry multi-job générique n'est introduit.
10. Reprise et checkpoint
V1 conserve le dernier BackfillCheckpoint uniquement dans Rust. L'UI voit checkpoint_present et contiguous_completed, jamais les octets/identité internes du checkpoint.
Resume reconstruit une requête sémantiquement identique avec un nouveau lifecycle contrôlé et réattache le checkpoint via BackfillRequest::with_checkpoint. Le backend doit préserver les éléments participant au scope fingerprint ; toute modification de scope/commitment/bornes sémantiques force une nouvelle campagne et invalide Resume.
Aucun JSON, fichier ou table checkpoint n'est inventé. La reprise après crash/redémarrage est reportée à une évolution explicite du contrat Backfill si un besoin durable est démontré.
11. Threat model
| Menace | Surface | Contrôle v0.3.7 |
|---|---|---|
| fuite URI Store | Config/backend | jamais projetée en DTO/log frontend |
| fuite URL/token provider | Transport/Config | rôle + labels provider sûrs uniquement ; aucune URL/credential |
| fuite RAW transaction | snapshots/IPC | snapshot existant ne contient aucun payload ; aucun Store read général |
| fuite liste signatures | request/logging | input transmis au command, non journalisé ; DTO retour n'en contient pas |
| fuite adresse | instrumentation | ne pas journaliser la valeur ; seulement scope kind/action code |
| frontend falsifie bornes | IPC | validation Rust + BackfillRequest autoritaire |
| rôle incompatible | Config/Transport | inventory + select_for_method pour les deux RPC avant Start |
| réseau incohérent | Config | rejet bootstrap/readiness avant création de request |
| double Start | AppState | admission atomique single-run |
| Cancel/terminal race | AppState/runtime | handle installé avant spawn, terminal autoritaire |
| checkpoint forgé | frontend | checkpoint jamais traverse IPC |
| permissions Tauri excessives | capabilities | core:default + tracing:default, aucune permission fs/dialog/network frontend sans besoin |
| browser storage de secrets | frontend | interdit ; aucun secret ne doit atteindre JS |
Tauri capabilities restent le mécanisme de frontière IPC. La capability par défaut doit cibler seulement splash et main et ne pas ajouter de plugin filesystem/dialog/network sans besoin réel.
12. Gate fonctionnel/live
Obligatoire pendant la release : tests déterministes Rust/desktop/security/composition, gates workspace et cargo tauri build final lancé depuis crates/ksp-app-backfill-desk.
Un smoke Config -> Transport -> Store -> Backfill peut exister comme test ignoré/opt-in uniquement si l'opérateur dispose d'un PostgreSQL de test et d'un endpoint public sûr. Aucun compte payant, secret fournisseur ou ressource non reproductible n'est requis pour clôturer la release.
13. Prévision recalibrée
pre.001 — audit et planification
Archives/règles/surfaces, matrice kbot3, screen map, DTO/commands, Config/composition, dépendances, threat model, lifecycle single-run, role/network/resume et sizing. Aucun scaffold lourd.
pre.002 — scaffold desktop minimal
Créer package lib+bin, shell/splash/tracing/capability, frontend minimal, ports Vite/HMR 1436/1437 stricts et tests desktop de structure. Le scaffold réutilise strictement le gabarit des Desk existantes : mêmes assets splash/logo/font, mêmes familles SASS/Bootstrap/SimpleBar, mêmes dépendances frontend minimales et mêmes devDependencies. Le bridge Logging frontend trace dès cette tranche les clics/onglets/transitions significatives ; le bootstrap Logging standard reste transitoire jusqu'au composite Backfill Desk de pre.003.
pre.003 — Config composite et packaging
Ajouter le composite Backfill Desk et l'enregistrer dans Config, mettre à jour atomiquement les quatre bundles Desk vers les 14 resources du registre, puis brancher le bootstrap Config/Logging et ses canaries sans encore ouvrir Transport/Store réel. Pendant le développement de l'application, devnet sélectionne le profil Logging supertrace et le fallback local Backfill Desk reste au niveau trace; ce choix est local au Desk et devra être resserré avant publication stable.
Le gate opérateur de pre.003 ouvre pre.003-fix.001 pour quatre corrections strictement locales : ordre RUST-FMT-104 d'un bloc de constantes, return explicite dans un canari Clippy, réconciliation de la fixture composite Backfill avec les profils réellement disponibles dans le corpus de fixtures Config, et correction d'un canari de dépendance qui doit reconnaître la syntaxe path utilisée par les crates KSP internes. Le composite de production, son profil supertrace et les frontières runtime de pre.003 restent inchangés.
Le replay opérateur de pre.003-fix.001 est ensuite intégralement propre : audits Rust/Markdown, cargo check, Clippy, suites Config/Config Desk/Wallet Desk/SOL Prices Desk/Backfill Desk et arbres de dépendances Backfill. Ce gate ouvre pre.004.
pre.004 — Transport readiness
Construire HttpTransportPool, dériver l'inventaire sûr des réseaux/rôles, vérifier le support simultané de getSignaturesForAddress et getTransaction, puis exposer backfill_options() sans provider, endpoint physique ni URL. BackfillDeskOptionsDto apparaît ici comme sous-contrat Transport seulement (configured_networks, rôles compatibles, diagnostic sûr, readiness) ; pre.007 l'étendra avec commitments, scopes et bornes après stabilisation du choix HTTP de pre.006. Une composition Transport avec plusieurs clusters HTTP actifs ou sans rôle compatible reste non-ready. Une erreur de résolution/construction Transport est conservée comme diagnostic sûr afin que le shell puisse rester ouvrable.
Le replay opérateur de pre.004 est acquis : audits, cargo check --workspace, Clippy, toutes les suites ciblées Backfill Desk et les deux arbres de dépendances passent. Le lancement cargo tauri dev confirme également le profil Logging supertrace, le profil Transport devnet_public, un réseau HTTP unique, un rôle compatible et transport_ready = true; les clics de tabs frontend apparaissent bien au niveau DEBUG.
pre.005 — Store readiness
Ouvrir uniquement ksp-store-lib::Store depuis le profil Store sélectionné par le composite. La Desk résout d'abord les settings Store, dérive leur réseau logique et exige un unique réseau Transport identique avant Store::open; aucun URI, backend physique ni détail de pool n'est projeté vers l'IPC. Après ouverture, un unique Store::health() borné produit store_ready, tandis que network_coherent et composition_ready rendent la chaîne Transport + Store explicite. Une erreur Config, ouverture ou health conserve le shell utilisable avec un diagnostic sûr, sans inventer de readiness.
Le Store ouvert reste détenu côté Rust et doit être fermé explicitement via Store::close() lors d'une demande de fermeture de la fenêtre principale. La Desk intercepte cette fermeture une seule fois, empêche la destruction immédiate, lance le close borné puis quitte l'application. Aucun BackfillRequest, BackfillJobRuntime, accès backend PostgreSQL direct ou capability Tauri supplémentaire n'est admis dans cette tranche.
pre.006 — Mainnet par défaut + sélection route HTTP
Faire de mainnet le profil composite par défaut, conserver supertrace pendant le développement et ajouter un profil mainnet_backfill_pool HTTP-only. Exposer dans backfill_options() des routes sûres dérivées des rôles Transport : backfill_pool pour Solana Public + PublicNode, backfill_solana_public et backfill_publicnode pour un ciblage explicite. Le frontend rend ce choix avant Start et trace les changements ; aucune URL/clé ne traverse IPC et ksp-job-backfill-lib n'est pas encore ouvert par la Desk.
pre.007 — DTO/request mapping
Ajouter ksp-job-api et ksp-job-backfill-lib comme dépendances backend directes de la Desk, sans ouvrir encore BackfillJobRuntime. Les DTO TS-RS app-owned exposent les quatre scopes HTTP existants, les commitments, min_context_slot sous forme de texte décimal, les bornes publiques Job et des defaults applicatifs bornés (page_size=100, max_pages=10, max_candidates=1000, hydration_concurrency=4). Le request DTO ne contient ni réseau, ni provider, ni endpoint, ni URL, ni JobId.
Le mapping Rust dérive le réseau du Store configuré, revalide http_role contre BackfillDeskOptionsDto::http_routes, parse adresse/signatures et délègue l'autorité finale des bornes/cohérences à BackfillRequest::new. Une commande backfill_validate_request utilise exactement ce mapping pour produire une projection sûre réduite à présence/comptage et paramètres non sensibles ; elle ne lance aucun Job. Le frontend matérialise le formulaire de campagne et le bouton Valider la requête ; adresse, ancre, signatures explicites et min_context_slot ne sont jamais journalisés.
pre.008 — runtime + Start
Ouvrir le Start réel en réutilisant strictement le mapping pre.007 : JobId généré côté backend, état single-active-run, construction BackfillJobRuntime, handle installé atomiquement avant tout spawn, puis exécution non bloquante sur le pool HTTP courant et le Store déjà ouvert. Le Store est temporairement prêté au run puis remis dans le slot applicatif après terminal ; aucune dépendance backend Store supplémentaire n'est ouverte. La réponse Start reste minimale (job_id, état initial) et aucun snapshot détaillé n'est encore projeté. La fermeture pendant un run reste une limite transitoire jusqu'au couloir Cancel/races de pre.010.
pre.009 — monitoring latest-value
Bridge de snapshots, status DTO, événements coalescés, compteurs complets, resynchronisation et absence de log parsing.
pre.010 — Cancel et races terminales
Cancel, état cancelling, idempotence UX, concurrence Start/Cancel/terminal, sémantique de drain Store et fermeture app.
pre.011 — checkpoint/frontier et Resume
Projection sûre, conservation Rust-only du checkpoint, reprise in-session et invariants par scope.
pre.012 — frontend fonctionnel et polish
Formulaire complet, états responsive, instrumentation sûre, summary terminal, erreurs, boutons contextuels, aucun secret/browser storage. L'adresse reste une saisie libre, mais reçoit un autocomplete HTML via datalist alimenté depuis le registre canonique ksp-core-lib::entries()/ProgramIdEntry : aucune copie locale de Program IDs, et une valeur arbitraire valide reste toujours acceptée. Le comportement fonctionnel recherché est celui de l'ancien dataset ks-program-ids de kbot3, sans reprise de son code.
pre.013 — hardening et complétude
Tests desktop/security/dependency/composition/release completeness, canaries DTO et scan des dépendances/interdictions.
pre.014 — gate technique final
cargo fmt, audits, check/clippy/tests workspace, arbres Cargo utiles, cargo tauri build, smoke Mainnet lecture seule opt-in si environnement sûr disponible.
pre.015 — réconciliation documentaire
README/USAGE/plan/validation et architecture seulement si la surface finale l'exige. Aucun nouveau runtime.
pre.016 — préparation publication
Prompt suivant + CHANGELOG + ROADMAP uniquement avec le bump prerelease/delta requis.
rel.001 — publication stable
Mécanique de publication v0.3.7, sans rattrapage.
14. Hors périmètre
Store Desk/navigation RAW, Worker API/service daemon, scheduler/registry générique Job, checkpoint durable, retry Job, sélection provider physique, nouveau protocole Transport, migration Store, decoding/materialization, application globale/control desk.
15. Report transport gRPC
Le Backfill 0.3.7 reste HTTP-only. Une généralisation de ksp-job-backfill-lib et de Backfill Desk vers Yellowstone gRPC est reportée à une version ultérieure, avec 0.3.12+ comme cible indicative à réévaluer. WebSocket reste hors Backfill tant qu'un besoin historique concret ne le justifie pas.