From 0e05c660c6d612be910a798f2990508eaef7faf6 Mon Sep 17 00:00:00 2001 From: SinuS Von SifriduS Date: Mon, 10 Aug 2026 11:21:07 +0200 Subject: [PATCH] v0.5.1-pre.008 --- Cargo.toml | 4 +- README.md | 4 +- config/README.md | 30 +- .../example.execution.config.json | 0 ...le.kb-app-demo-desktop.default.config.json | 12 +- .../example.listeners.config.json | 0 .../example.logging.config.json | 0 .../{ => exemples}/example.store.config.json | 0 .../example.transport.config.json | 0 .../{ => exemples}/example.wallet.config.json | 0 .../kb-app-demo-desktop.default.config.json | 36 +- config/schemas/composition.config.schema.json | 43 +- ...emo-desktop.application.config.schema.json | 43 ++ .../schemas/resolved.app.config.schema.json | 42 +- docs/DEVNET_EXECUTION_GUIDE.md | 2 +- docs/architecture/CRATE_MAP.md | 24 +- .../KHADHROONY_SOLANA_NAMESPACE_POLICY.md | 22 +- docs/guides/CONFIGURATION.md | 27 +- ...HROONY_SOLANA_NAMESPACE_AND_CONFIG_PLAN.md | 659 +++++++++--------- docs/rules/RULES_SPECIFIC_KHADHROONY.md | 18 +- kb-app-demo-desktop/CHANGELOG.md | 14 +- kb-app-demo-desktop/README.md | 6 +- kb-app-demo-desktop/frontend/demo_config.html | 10 +- .../frontend/ts/demo_config.ts | 10 +- .../frontend/ts/demo_execution_spl.ts | 4 +- kb-app-demo-desktop/frontend/ts/demo_http.ts | 6 +- kb-app-demo-desktop/frontend/ts/demo_ws.ts | 6 +- kb-app-demo-desktop/src/app_state.rs | 53 +- kb-app-demo-desktop/src/demo_config.rs | 555 ++++++++++++++- kb-app-demo-desktop/src/demo_http.rs | 66 +- .../src/demo_spl_token_2022.rs | 39 +- kb-app-demo-desktop/src/demo_transport.rs | 52 ++ kb-app-demo-desktop/src/demo_ws.rs | 92 ++- kb-app-demo-desktop/src/desktop_config.rs | 96 +++ kb-app-demo-desktop/src/lib.rs | 36 +- kb-app-demo-desktop/src/tauri.rs | 9 +- ks-config/CHANGELOG.md | 13 +- ks-config/Cargo.toml | 3 +- ks-config/README.md | 10 +- ks-config/TODO.md | 11 +- ks-config/USAGE.md | 12 +- ks-config/src/composition.rs | 38 +- ks-config/src/environment.rs | 6 +- ks-config/src/execution.rs | 17 +- ks-config/src/lib.rs | 22 +- ks-config/src/listeners.rs | 17 +- ks-config/src/schema.rs | 56 ++ ks-config/src/sensitivity.rs | 160 +++++ ks-config/src/settings.rs | 221 +----- ks-config/src/store.rs | 16 +- ks-config/src/transport.rs | 25 +- ks-config/src/wallet.rs | 16 +- ks-config/tests/external_composition_api.rs | 31 +- ks-config/tests/external_sensitivity_api.rs | 18 + ks-lib/CHANGELOG.md | 7 +- ks-lib/Cargo.toml | 3 +- ks-lib/README.md | 4 +- ks-lib/USAGE.md | 4 +- ks-lib/src/executor/api/execution.rs | 112 +-- ks-lib/src/executor/api/executor.rs | 22 +- .../metaplex_token_metadata/intent.rs | 33 +- .../solana_program_metadata/intent.rs | 64 +- ks-lib/src/executor/safety/evaluation.rs | 22 +- ks-lib/src/executor/solana/core/intent.rs | 82 +-- .../spl/associated_token_account/intent.rs | 23 +- .../executor/spl/elgamal_registry/intent.rs | 22 +- ks-lib/src/executor/spl/memo/intent.rs | 28 +- ks-lib/src/executor/spl/token/intent.rs | 40 +- .../executor/spl/token_2022/confidential.rs | 58 +- ks-lib/src/executor/spl/token_2022/intent.rs | 58 +- ks-lib/src/model/decoded.rs | 22 +- ks-lib/src/model/materialized.rs | 16 +- ks-lib/src/model/nomenclature.rs | 40 +- ks-lib/src/model/observation.rs | 10 +- ks-lib/src/model/solana.rs | 32 +- ks-logging/CHANGELOG.md | 8 +- ks-logging/TODO.md | 5 +- ks-logging/src/document.rs | 7 +- ks-onchain-transport/CHANGELOG.md | 9 +- ks-onchain-transport/src/client.rs | 34 +- ks-onchain-transport/src/http_client.rs | 105 ++- ks-onchain-transport/src/http_pool.rs | 4 +- ks-onchain-transport/src/ws_client.rs | 65 +- ks-onchain-transport/src/ws_pool.rs | 4 +- ks-onchain-transport/src/ws_session.rs | 54 +- ...PRE_003_STORE_SCENARIOS_EXECUTION_AUDIT.md | 80 +-- ..._khadhroony_solana_namespace_and_config.md | 13 +- scripts/audit_khadhroony_workspace_rules.py | 269 ++++++- .../config/example.resolved.app.config.json | 8 - test-fixtures/config/resolved.app.config.json | 24 - 90 files changed, 2490 insertions(+), 1613 deletions(-) rename config/{ => exemples}/example.execution.config.json (100%) rename config/{ => exemples}/example.kb-app-demo-desktop.default.config.json (72%) rename config/{ => exemples}/example.listeners.config.json (100%) rename config/{ => exemples}/example.logging.config.json (100%) rename config/{ => exemples}/example.store.config.json (100%) rename config/{ => exemples}/example.transport.config.json (100%) rename config/{ => exemples}/example.wallet.config.json (100%) create mode 100644 config/schemas/kb-app-demo-desktop.application.config.schema.json create mode 100644 kb-app-demo-desktop/src/demo_transport.rs create mode 100644 kb-app-demo-desktop/src/desktop_config.rs create mode 100644 ks-config/src/schema.rs create mode 100644 ks-config/src/sensitivity.rs create mode 100644 ks-config/tests/external_sensitivity_api.rs diff --git a/Cargo.toml b/Cargo.toml index cd211c4..008bdda 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ # file: Cargo.toml -# version: 51 +# version: 52 [workspace] resolver = "3" @@ -18,7 +18,7 @@ members = [ ] [workspace.package] -version = "0.5.1-pre.7" +version = "0.5.1-pre.8" edition = "2024" license = "MIT" repository = "https://git.sasedev.com/Sasedev/khadhroony-bot3" diff --git a/README.md b/README.md index 12e8fa2..573fb54 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ - + # Khadhroony Bot3 @@ -23,6 +23,8 @@ Le workspace contient onze crates : La série `0.5.x` prépare la séparation explicite entre le domaine applicatif `khadhroony-bot` et les bibliothèques généralistes `khadhroony-solana`. Depuis `0.5.1-pre.002`, les dix crates généralistes utilisent les noms `ks-*` / `ks_*`, tandis que `kb-app-demo-desktop` conserve son nom et reste une application du workspace Bot. Le workspace, le dépôt et le répertoire racine restent nommés `khadhroony-bot3` pendant cette migration et ne doivent pas être renommés avant `1.0` ou une version ultérieure explicitement dédiée. La politique de migration est définie dans [`docs/decisions/KHADHROONY_SOLANA_NAMESPACE_POLICY.md`](docs/decisions/KHADHROONY_SOLANA_NAMESPACE_POLICY.md). +Depuis `0.5.1-pre.008`, les contrats de configuration source/runtime susceptibles de contenir des secrets restent backend-only et ne sont ni sérialisables ni `Debug` par défaut. Les crates `ks-config` et `ks-lib` ne génèrent plus de bindings TS-RS ; les surfaces TypeScript/Tauri sont possédées par les applications via des DTO explicites. + Références : - [`docs/architecture/ARCHITECTURE.md`](docs/architecture/ARCHITECTURE.md) ; diff --git a/config/README.md b/config/README.md index b0529cf..81fa1f0 100644 --- a/config/README.md +++ b/config/README.md @@ -1,5 +1,5 @@ - + # Configuration locale @@ -36,15 +36,17 @@ config/ ├── store.config.json ├── wallet.config.json ├── execution.config.json -├── example.kb-app-demo-desktop.default.config.json -├── example.logging.config.json -├── example.transport.config.json -├── example.listeners.config.json -├── example.store.config.json -├── example.wallet.config.json -├── example.execution.config.json +├── exemples/ +│ ├── example.kb-app-demo-desktop.default.config.json +│ ├── example.logging.config.json +│ ├── example.transport.config.json +│ ├── example.listeners.config.json +│ ├── example.store.config.json +│ ├── example.wallet.config.json +│ └── example.execution.config.json └── schemas/ ├── composition.config.schema.json + ├── kb-app-demo-desktop.application.config.schema.json ├── logging.config.schema.json ├── transport.config.schema.json ├── listeners.config.schema.json @@ -54,7 +56,7 @@ config/ └── resolved.app.config.schema.json ``` -`resolved.app.config.schema.json` décrit uniquement le contrat transitoire `AppConfig/ProfileConfig` reconstruit en mémoire. Aucun binaire ne charge ce schéma comme document source applicatif. +`resolved.app.config.schema.json` décrit uniquement le contrat transitoire `AppConfig/ProfileConfig` reconstruit en mémoire. Aucun binaire ne charge ce schéma comme document source applicatif. La section `application` d'une composition reste opaque pour `ks-config`; `kb-app-demo-desktop` la valide avec `config/schemas/kb-app-demo-desktop.application.config.schema.json`. ## Defaults et overrides @@ -110,14 +112,20 @@ Les defaults portent les timeouts, capacités de channels et `auto_reconnect`. U `store.config.json` possède la sélection et les paramètres PostgreSQL/SQLite. Le split reste structurel : aucune migration SQL n'est introduite par `0.5.1`. +## Frontière source/runtime/public/diagnostic + +Les documents source et le contrat runtime résolu peuvent contenir des valeurs sensibles après résolution des placeholders. Ils restent backend-only et ne sont ni sérialisables ni `Debug` par défaut. Les applications construisent explicitement leurs DTO publics et diagnostics bornés, sans sérialiser puis masquer un `AppConfig/ProfileConfig` complet. + +Une valeur composée hérite de la sensibilité la plus forte de ses placeholders (`Secret > Internal > Public`). Une URL ou un DSN incorporant un `KS_SECRET_*`/`KB_SECRET_*` est donc secret même si le champ final ne porte pas le mot `SECRET`. + ## Variables d'environnement Les composants `ks-*` utilisent `KS_SECRET_*`, `KS_PUBLIC_*` ou `KS_*`. Les besoins réellement spécifiques à une application `kb-*` utilisent `KB_SECRET_*`, `KB_PUBLIC_*` ou `KB_*`. -Les secrets ne sont jamais écrits en clair dans le dépôt. La séparation source/runtime/public/diagnostic et le camouflage systématique sont traités après ce split. +Les secrets ne sont jamais écrits en clair dans le dépôt. Depuis `0.5.1-pre.008`, les contrats source/runtime sensibles restent backend-only, tandis que les applications exposent uniquement des DTO publics ou diagnostics explicitement bornés. ## Exemples et schémas -Les exemples sous `config/` sont des références conformes et ne sont pas chargés automatiquement. Tous les schémas actifs résident exclusivement sous `config/schemas/`. +Les exemples sous `config/exemples/` sont des références conformes et ne sont pas chargés automatiquement. Tous les schémas actifs résident exclusivement sous `config/schemas/`. Les fichiers historiques `app.config.json`, `example.app.config.json`, `schemas/app.config.schema.json`, `example.config.json`, `schema.config.json` et `ks-pipeline-demo-scenarios.default.config.json` sont obsolètes dans l'architecture courante. diff --git a/config/example.execution.config.json b/config/exemples/example.execution.config.json similarity index 100% rename from config/example.execution.config.json rename to config/exemples/example.execution.config.json diff --git a/config/example.kb-app-demo-desktop.default.config.json b/config/exemples/example.kb-app-demo-desktop.default.config.json similarity index 72% rename from config/example.kb-app-demo-desktop.default.config.json rename to config/exemples/example.kb-app-demo-desktop.default.config.json index aed9066..4e211ec 100644 --- a/config/example.kb-app-demo-desktop.default.config.json +++ b/config/exemples/example.kb-app-demo-desktop.default.config.json @@ -11,13 +11,13 @@ "profiles": [ { "name": "local_devnet", - "app": { + "application": { "name": "khadhroony-bot3", - "environment": "development" - }, - "demo": { - "live_demo_enabled": true, - "trading_demo_enabled": false + "environment": "development", + "demo": { + "live_demo_enabled": true, + "trading_demo_enabled": false + } } } ] diff --git a/config/example.listeners.config.json b/config/exemples/example.listeners.config.json similarity index 100% rename from config/example.listeners.config.json rename to config/exemples/example.listeners.config.json diff --git a/config/example.logging.config.json b/config/exemples/example.logging.config.json similarity index 100% rename from config/example.logging.config.json rename to config/exemples/example.logging.config.json diff --git a/config/example.store.config.json b/config/exemples/example.store.config.json similarity index 100% rename from config/example.store.config.json rename to config/exemples/example.store.config.json diff --git a/config/example.transport.config.json b/config/exemples/example.transport.config.json similarity index 100% rename from config/example.transport.config.json rename to config/exemples/example.transport.config.json diff --git a/config/example.wallet.config.json b/config/exemples/example.wallet.config.json similarity index 100% rename from config/example.wallet.config.json rename to config/exemples/example.wallet.config.json diff --git a/config/kb-app-demo-desktop.default.config.json b/config/kb-app-demo-desktop.default.config.json index 6bfabbe..40ce581 100644 --- a/config/kb-app-demo-desktop.default.config.json +++ b/config/kb-app-demo-desktop.default.config.json @@ -11,24 +11,24 @@ "profiles": [ { "name": "local_devnet", - "app": { + "application": { "name": "khadhroony-bot3", - "environment": "development" - }, - "demo": { - "live_demo_enabled": true, - "trading_demo_enabled": false + "environment": "development", + "demo": { + "live_demo_enabled": true, + "trading_demo_enabled": false + } } }, { "name": "mainnet_research", - "app": { + "application": { "name": "khadhroony-bot3", - "environment": "research" - }, - "demo": { - "live_demo_enabled": true, - "trading_demo_enabled": false + "environment": "research", + "demo": { + "live_demo_enabled": true, + "trading_demo_enabled": false + } }, "logging_profile": "mainnet_research", "transport_profile": "mainnet_research", @@ -39,13 +39,13 @@ }, { "name": "mainnet", - "app": { + "application": { "name": "khadhroony-bot3", - "environment": "mainnet" - }, - "demo": { - "live_demo_enabled": true, - "trading_demo_enabled": false + "environment": "mainnet", + "demo": { + "live_demo_enabled": true, + "trading_demo_enabled": false + } }, "logging_profile": "mainnet", "transport_profile": "mainnet", diff --git a/config/schemas/composition.config.schema.json b/config/schemas/composition.config.schema.json index 111fb93..2ba6705 100644 --- a/config/schemas/composition.config.schema.json +++ b/config/schemas/composition.config.schema.json @@ -29,38 +29,6 @@ "type": "string", "minLength": 1 }, - "app_section": { - "type": "object", - "additionalProperties": false, - "required": [ - "name", - "environment" - ], - "properties": { - "name": { - "$ref": "#/$defs/non_empty_string" - }, - "environment": { - "$ref": "#/$defs/non_empty_string" - } - } - }, - "demo": { - "type": "object", - "additionalProperties": false, - "required": [ - "live_demo_enabled", - "trading_demo_enabled" - ], - "properties": { - "live_demo_enabled": { - "type": "boolean" - }, - "trading_demo_enabled": { - "type": "boolean" - } - } - }, "sources": { "type": "object", "additionalProperties": false, @@ -97,19 +65,14 @@ "type": "object", "additionalProperties": false, "required": [ - "name", - "app", - "demo" + "name" ], "properties": { "name": { "$ref": "#/$defs/non_empty_string" }, - "app": { - "$ref": "#/$defs/app_section" - }, - "demo": { - "$ref": "#/$defs/demo" + "application": { + "type": "object" }, "logging_profile": { "$ref": "#/$defs/non_empty_string" diff --git a/config/schemas/kb-app-demo-desktop.application.config.schema.json b/config/schemas/kb-app-demo-desktop.application.config.schema.json new file mode 100644 index 0000000..87a992f --- /dev/null +++ b/config/schemas/kb-app-demo-desktop.application.config.schema.json @@ -0,0 +1,43 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://khadhroony.local/schemas/kb-app-demo-desktop.application.config.schema.json", + "title": "Khadhroony Bot desktop application composition fragment", + "type": "object", + "additionalProperties": false, + "required": [ + "name", + "environment", + "demo" + ], + "properties": { + "name": { + "type": "string", + "minLength": 1 + }, + "environment": { + "type": "string", + "minLength": 1 + }, + "demo": { + "$ref": "#/$defs/demo" + } + }, + "$defs": { + "demo": { + "type": "object", + "additionalProperties": false, + "required": [ + "live_demo_enabled", + "trading_demo_enabled" + ], + "properties": { + "live_demo_enabled": { + "type": "boolean" + }, + "trading_demo_enabled": { + "type": "boolean" + } + } + } + } +} diff --git a/config/schemas/resolved.app.config.schema.json b/config/schemas/resolved.app.config.schema.json index 544b121..9ef2b43 100644 --- a/config/schemas/resolved.app.config.schema.json +++ b/config/schemas/resolved.app.config.schema.json @@ -30,20 +30,15 @@ "additionalProperties": false, "required": [ "name", - "app", "database", "solana", "wallet", - "execution", - "demo" + "execution" ], "properties": { "name": { "$ref": "#/$defs/non_empty_string" }, - "app": { - "$ref": "#/$defs/app_section" - }, "database": { "$ref": "#/$defs/database" }, @@ -55,25 +50,6 @@ }, "execution": { "$ref": "#/$defs/execution" - }, - "demo": { - "$ref": "#/$defs/demo" - } - } - }, - "app_section": { - "type": "object", - "additionalProperties": false, - "required": [ - "name", - "environment" - ], - "properties": { - "name": { - "$ref": "#/$defs/non_empty_string" - }, - "environment": { - "$ref": "#/$defs/non_empty_string" } } }, @@ -608,22 +584,6 @@ "type": "boolean" } } - }, - "demo": { - "type": "object", - "additionalProperties": false, - "required": [ - "live_demo_enabled", - "trading_demo_enabled" - ], - "properties": { - "live_demo_enabled": { - "type": "boolean" - }, - "trading_demo_enabled": { - "type": "boolean" - } - } } } } diff --git a/docs/DEVNET_EXECUTION_GUIDE.md b/docs/DEVNET_EXECUTION_GUIDE.md index 9ddcb1d..720588c 100644 --- a/docs/DEVNET_EXECUTION_GUIDE.md +++ b/docs/DEVNET_EXECUTION_GUIDE.md @@ -559,7 +559,7 @@ Dans **Décodeur spl_token — TransferChecked** : | Compte source | `$KS_DEVNET_CLASSIC_SOURCE_ATA` | | Mint | `$KS_DEVNET_CLASSIC_MINT` | | Compte destination | `$KS_DEVNET_CLASSIC_DESTINATION_ATA` | -| Autorité simple | `$KS_PUBLIC_DEVNET_WALLET_ADDRESS` | +| Autorité simple | `$KS_PUBLIC_DEVNET_WALLET_ADDRESS` | | Montant brut exact | `1000000000` pour 1 token avec 9 décimales | | Decimals | `9` | diff --git a/docs/architecture/CRATE_MAP.md b/docs/architecture/CRATE_MAP.md index c34afca..35d2b79 100644 --- a/docs/architecture/CRATE_MAP.md +++ b/docs/architecture/CRATE_MAP.md @@ -5,19 +5,19 @@ ## 1. Inventaire -| Crate | Type | Responsabilité principale | État documentaire | -|------------------------------|------------------------|--------------------------------------------------------------------|------------------------------------------| -| `ks-core` | bibliothèque | erreurs, résultat et identité de module partagés | README/TODO/USAGE/CHANGELOG présents | +| Crate | Type | Responsabilité principale | État documentaire | +|------------------------------|------------------------|--------------------------------------------------------------------------------------|------------------------------------------| +| `ks-core` | bibliothèque | erreurs, résultat et identité de module partagés | README/TODO/USAGE/CHANGELOG présents | | `ks-config` | bibliothèque | compositions binaires, documents JSON partagés, environnement, validation et profils | migration/restructuration en `0.5.1` | -| `ks-lib` | bibliothèque | modèles, décodeurs, exécuteurs et matérialisateurs consolidés | README/TODO/USAGE/CHANGELOG présents | -| `ks-logging` | bibliothèque | initialisation du logging et du tracing | README/TODO/USAGE/CHANGELOG présents | -| `ks-program-ids` | bibliothèque | registre des programmes et comptes Solana connus | README/TODO/USAGE/CHANGELOG présents | -| `ks-pipeline` | bibliothèque | backfill, extraction, replay, stateful, préflight et orchestration | README/TODO/USAGE/CHANGELOG présents | -| `ks-pipeline-demo-scenarios` | bibliothèque + binaire | scénarios Devnet réutilisables et CLI | nom `ks-*` actif ; audit `0.5.4` | -| `ks-onchain-transport` | bibliothèque | transports RPC HTTP/WebSocket et pools d’endpoints | README/TODO/USAGE/CHANGELOG présents | -| `ks-store` | bibliothèque | contrats de stockage et adaptateur PostgreSQL | nom `ks-*` actif ; normalisation `0.5.3` | -| `ks-wallet` | bibliothèque | wallet temporaire et frontière de signataire | nom `ks-*` actif ; refonte `0.5.2` | -| `kb-app-demo-desktop` | bibliothèque + binaire | application de démonstration Tauri | documentée ; réconciliation en `0.5.4` | +| `ks-lib` | bibliothèque | modèles, décodeurs, exécuteurs et matérialisateurs consolidés | README/TODO/USAGE/CHANGELOG présents | +| `ks-logging` | bibliothèque | initialisation du logging et du tracing | README/TODO/USAGE/CHANGELOG présents | +| `ks-program-ids` | bibliothèque | registre des programmes et comptes Solana connus | README/TODO/USAGE/CHANGELOG présents | +| `ks-pipeline` | bibliothèque | backfill, extraction, replay, stateful, préflight et orchestration | README/TODO/USAGE/CHANGELOG présents | +| `ks-pipeline-demo-scenarios` | bibliothèque + binaire | scénarios Devnet réutilisables et CLI | nom `ks-*` actif ; audit `0.5.4` | +| `ks-onchain-transport` | bibliothèque | transports RPC HTTP/WebSocket et pools d’endpoints | README/TODO/USAGE/CHANGELOG présents | +| `ks-store` | bibliothèque | contrats de stockage et adaptateur PostgreSQL | nom `ks-*` actif ; normalisation `0.5.3` | +| `ks-wallet` | bibliothèque | wallet temporaire et frontière de signataire | nom `ks-*` actif ; refonte `0.5.2` | +| `kb-app-demo-desktop` | bibliothèque + binaire | application de démonstration Tauri | documentée ; réconciliation en `0.5.4` | La table décrit les noms physiques actifs depuis `0.5.1-pre.002`. La migration a renommé les dix crates généralistes en `ks-core`, `ks-config`, `ks-lib`, `ks-logging`, `ks-program-ids`, `ks-pipeline`, `ks-pipeline-demo-scenarios`, `ks-onchain-transport`, `ks-store` et `ks-wallet`. `kb-app-demo-desktop` conserve son nom parce qu’il appartient au domaine applicatif Bot. Voir [`../decisions/KHADHROONY_SOLANA_NAMESPACE_POLICY.md`](../decisions/KHADHROONY_SOLANA_NAMESPACE_POLICY.md). diff --git a/docs/decisions/KHADHROONY_SOLANA_NAMESPACE_POLICY.md b/docs/decisions/KHADHROONY_SOLANA_NAMESPACE_POLICY.md index c18e8b6..bb3b475 100644 --- a/docs/decisions/KHADHROONY_SOLANA_NAMESPACE_POLICY.md +++ b/docs/decisions/KHADHROONY_SOLANA_NAMESPACE_POLICY.md @@ -25,18 +25,18 @@ Le renommage des bibliothèques internes ne renomme pas le workspace, le dépôt La migration `0.5.1-pre.002` renomme les dix crates généralistes suivantes : -| Nom `0.5.0` | Nom cible | -|---|---| -| `kb-core` | `ks-core` | -| `kb-config` | `ks-config` | -| `kb-lib` | `ks-lib` | -| `kb-logging` | `ks-logging` | -| `kb-program-ids` | `ks-program-ids` | -| `kb-pipeline` | `ks-pipeline` | +| Nom `0.5.0` | Nom cible | +|------------------------------|------------------------------| +| `kb-core` | `ks-core` | +| `kb-config` | `ks-config` | +| `kb-lib` | `ks-lib` | +| `kb-logging` | `ks-logging` | +| `kb-program-ids` | `ks-program-ids` | +| `kb-pipeline` | `ks-pipeline` | | `kb-pipeline-demo-scenarios` | `ks-pipeline-demo-scenarios` | -| `kb-onchain-transport` | `ks-onchain-transport` | -| `kb-store` | `ks-store` | -| `kb-wallet` | `ks-wallet` | +| `kb-onchain-transport` | `ks-onchain-transport` | +| `kb-store` | `ks-store` | +| `kb-wallet` | `ks-wallet` | Les identifiants Rust correspondants migrent dans le même delta vers `ks_*` : par exemple `kb_config` devient `ks_config` et `kb_pipeline_demo_scenarios` devient `ks_pipeline_demo_scenarios`. diff --git a/docs/guides/CONFIGURATION.md b/docs/guides/CONFIGURATION.md index e9aad58..9ad0499 100644 --- a/docs/guides/CONFIGURATION.md +++ b/docs/guides/CONFIGURATION.md @@ -1,5 +1,5 @@ - + # Guide de configuration @@ -22,7 +22,7 @@ Documents partagés : - `config/wallet.config.json` ; - `config/execution.config.json`. -Chaque document possède un exemple `example.*.config.json`. Les schémas correspondants résident sous `config/schemas/`. +Chaque document possède un exemple sous `config/exemples/` avec un nom `example.*.config.json`. Les schémas correspondants résident sous `config/schemas/`. `.env`, ou le fichier sélectionné par `KS_ENV_FILE`, fournit les valeurs non versionnées. @@ -145,22 +145,29 @@ appartiennent à `execution.config.json`, avec les limites de dépense, frais, s ## Contrat runtime transitoire -Pendant `0.5.1`, `ks-config` reconstruit encore `AppConfig/ProfileConfig` afin de préserver les consommateurs existants. Il s'agit d'une projection runtime, pas d'un document source. +Pendant `0.5.1`, `ks-config` reconstruit encore `AppConfig/ProfileConfig` afin de préserver les consommateurs backend existants. Il s'agit d'une projection runtime, pas d'un document source ni d'une surface de sortie. Depuis `pre.008`, ces contrats et les documents spécialisés susceptibles de contenir des valeurs résolues ne dérivent ni `serde::Serialize` ni `Debug`. Les fixtures de compatibilité sont sous `test-fixtures/config/`. Elles ne doivent pas être chargées en production. +## Frontière publique et diagnostic + +Une application ne transmet jamais `AppConfig/ProfileConfig` directement. Elle construit : + +- une projection publique explicitement typée, limitée aux champs autorisés ; +- un diagnostic borné contenant au plus des états tels que `configured`/`missing` pour les URLs, DSN et chemins sensibles ; +- aucune projection construite par sérialisation du runtime suivie d'un masquage a posteriori. + +La sensibilité suit `Secret > Internal > Public`. Une valeur composée hérite de la sensibilité la plus forte des placeholders qu'elle contient ; une URL incorporant `${KS_SECRET_HELIUS_API_KEY}` est donc secrète même si son champ final est simplement `url`. + +La section `application` d'une composition est opaque à `ks-config`. Le desktop la valide avec son schéma `config/schemas/kb-app-demo-desktop.application.config.schema.json`; un futur worker pourra posséder son propre schéma sans modifier `ks-config`. + ## Frontière TS-RS -L'audit `pre.007` confirme que le frontend du desktop n'importe directement aucun binding généré depuis `ks-config` ou `ks-lib`. Les dérivations TS-RS des crates généralistes seront donc réévaluées dans la prerelease suivante : les DTO réellement destinés à Tauri doivent être définis/wrappés dans l'application, sauf contrat TypeScript générique explicitement justifié. +Depuis `pre.008`, `ks-config` et `ks-lib` ne dépendent plus de TS-RS et ne possèdent plus de bindings TypeScript générés. Les DTO traversant Tauri appartiennent à `kb-app-demo-desktop` ou à la future application concernée. Une exception dans une crate `ks-*` exige un contrat TypeScript générique indépendant de Tauri explicitement justifié et audité. ## Étape suivante -La prochaine prerelease traite ensemble : - -- représentation source / runtime / publique / diagnostic ; -- propagation `SECRET` / `PUBLIC` / interne ; -- suppression de l'exposition Tauri de la configuration runtime complète ; -- réduction des bindings TS-RS dans les crates généralistes et wrappers applicatifs nécessaires. +`pre.009` est une prerelease de clôture : réconciliation documentaire, audits finaux, nettoyage des TODO, archivage du plan/prompt et préparation de `0.5.2`. ## Invariants diff --git a/docs/plans/V0_5_1_KHADHROONY_SOLANA_NAMESPACE_AND_CONFIG_PLAN.md b/docs/plans/V0_5_1_KHADHROONY_SOLANA_NAMESPACE_AND_CONFIG_PLAN.md index c3111cc..75e7171 100644 --- a/docs/plans/V0_5_1_KHADHROONY_SOLANA_NAMESPACE_AND_CONFIG_PLAN.md +++ b/docs/plans/V0_5_1_KHADHROONY_SOLANA_NAMESPACE_AND_CONFIG_PLAN.md @@ -1,13 +1,13 @@ - + # Plan temporaire `0.5.1` — namespace Khadhroony Solana et configuration sûre ## 1. Statut et règle de cette prerelease -Ce document est le plan temporaire de `0.5.1`. Les prereleases `pre.001` à `pre.004` ont fermé l'inventaire puis migré crates, identités techniques et variables d'environnement. `pre.005` a séparé le logging, `pre.006` a introduit les compositions de binaires et extrait transport/listeners, et `pre.007` termine maintenant la décomposition des responsabilités partagées avec store/wallet/execution, valeurs globales hors profils et defaults autonomes. `kb-app-demo-desktop` et le workspace racine restent nommés comme avant. +Ce document est le plan temporaire de `0.5.1`. Les prereleases `pre.001` à `pre.004` ont fermé l'inventaire puis migré crates, identités techniques et variables d'environnement. `pre.005` à `pre.007` ont terminé le split logging/transport/listeners/store/wallet/execution, les compositions de binaires et les defaults autonomes. `pre.008` ferme maintenant les frontières source/runtime/public/diagnostic, la non-divulgation et TS-RS. `kb-app-demo-desktop` et le workspace racine restent nommés comme avant. -Les contrôles Rust et le démarrage Tauri de `pre.006` ont été validés par l'opérateur le 10 août 2026. Le démarrage confirme le chargement de `kb-app-demo-desktop.default.config.json`, du document logging séparé, des profils transport et du store, avec DSN PostgreSQL masqué dans les logs. `pre.007` doit préserver le comportement runtime tout en changeant l'ownership source des paramètres. +Après `pre.007-delta-fix-002`, l'opérateur a validé `cargo fmt`, `cargo check --workspace`, `cargo clippy --all-targets`, l'audit workspace, `cargo test --workspace` et le démarrage Tauri le 10 août 2026. `pre.008` peut donc modifier uniquement les frontières de sortie/configuration sans rouvrir le split structurel déjà validé. ## 2. Invariant du workspace racine @@ -32,18 +32,18 @@ Les bibliothèques internes Solana utilisent `ks-*` / `ks_*` et leurs variables ## 4. Inventaire des crates et ordre de migration -| Package actuel | Identifiant Rust actuel | Package cible | Identifiant Rust cible | Dépendances workspace directes actuelles | Ordre | -|---|---|---|---|---|---:| -| `kb-core` | `kb_core` | `ks-core` | `ks_core` | — | 1 | -| `kb-program-ids` | `kb_program_ids` | `ks-program-ids` | `ks_program_ids` | — | 2 | -| `kb-config` | `kb_config` | `ks-config` | `ks_config` | kb-core | 3 | -| `kb-logging` | `kb_logging` | `ks-logging` | `ks_logging` | kb-core | 4 | -| `kb-wallet` | `kb_wallet` | `ks-wallet` | `ks_wallet` | kb-core | 5 | -| `kb-lib` | `kb_lib` | `ks-lib` | `ks_lib` | kb-core, kb-program-ids | 6 | -| `kb-onchain-transport` | `kb_onchain_transport` | `ks-onchain-transport` | `ks_onchain_transport` | kb-config, kb-core, kb-lib | 7 | -| `kb-store` | `kb_store` | `ks-store` | `ks_store` | kb-core, kb-lib | 8 | -| `kb-pipeline` | `kb_pipeline` | `ks-pipeline` | `ks_pipeline` | kb-core, kb-config, kb-lib, kb-onchain-transport, kb-program-ids, kb-store | 9 | -| `kb-pipeline-demo-scenarios` | `kb_pipeline_demo_scenarios` | `ks-pipeline-demo-scenarios` | `ks_pipeline_demo_scenarios` | kb-core, kb-config, kb-lib, kb-onchain-transport, kb-pipeline, kb-program-ids, kb-store, kb-wallet | 10 | +| Package actuel | Identifiant Rust actuel | Package cible | Identifiant Rust cible | Dépendances workspace directes actuelles | Ordre | +|------------------------------|------------------------------|------------------------------|------------------------------|----------------------------------------------------------------------------------------------------|------:| +| `kb-core` | `kb_core` | `ks-core` | `ks_core` | — | 1 | +| `kb-program-ids` | `kb_program_ids` | `ks-program-ids` | `ks_program_ids` | — | 2 | +| `kb-config` | `kb_config` | `ks-config` | `ks_config` | kb-core | 3 | +| `kb-logging` | `kb_logging` | `ks-logging` | `ks_logging` | kb-core | 4 | +| `kb-wallet` | `kb_wallet` | `ks-wallet` | `ks_wallet` | kb-core | 5 | +| `kb-lib` | `kb_lib` | `ks-lib` | `ks_lib` | kb-core, kb-program-ids | 6 | +| `kb-onchain-transport` | `kb_onchain_transport` | `ks-onchain-transport` | `ks_onchain_transport` | kb-config, kb-core, kb-lib | 7 | +| `kb-store` | `kb_store` | `ks-store` | `ks_store` | kb-core, kb-lib | 8 | +| `kb-pipeline` | `kb_pipeline` | `ks-pipeline` | `ks_pipeline` | kb-core, kb-config, kb-lib, kb-onchain-transport, kb-program-ids, kb-store | 9 | +| `kb-pipeline-demo-scenarios` | `kb_pipeline_demo_scenarios` | `ks-pipeline-demo-scenarios` | `ks_pipeline_demo_scenarios` | kb-core, kb-config, kb-lib, kb-onchain-transport, kb-pipeline, kb-program-ids, kb-store, kb-wallet | 10 | `kb-app-demo-desktop` est adapté en dernier, mais garde son package, son répertoire et ses identifiants applicatifs. @@ -58,18 +58,18 @@ bin : kb-pipeline-demo-scenarios-cli -> ks-pipeline-demo-scenarios-cli Les valeurs suivantes sont des métriques d'orientation sur les fichiers actifs, hors archives et artefacts générés. Elles montrent qu'un renommage massif en une seule opération serait difficile à diagnostiquer. -| Crate | Références package / fichiers | Références identifiant Rust / fichiers | -|---|---:|---:| -| `kb-core` | 51 / 32 | 3165 / 407 | -| `kb-config` | 56 / 33 | 201 / 44 | -| `kb-lib` | 3145 / 550 | 2785 / 97 | -| `kb-logging` | 72 / 27 | 22 / 3 | -| `kb-program-ids` | 42 / 28 | 1413 / 334 | -| `kb-pipeline` | 287 / 112 | 436 / 38 | -| `kb-pipeline-demo-scenarios` | 145 / 75 | 173 / 18 | -| `kb-onchain-transport` | 125 / 56 | 530 / 50 | -| `kb-store` | 157 / 81 | 344 / 30 | -| `kb-wallet` | 75 / 30 | 53 / 17 | +| Crate | Références package / fichiers | Références identifiant Rust / fichiers | +|------------------------------|------------------------------:|---------------------------------------:| +| `kb-core` | 51 / 32 | 3165 / 407 | +| `kb-config` | 56 / 33 | 201 / 44 | +| `kb-lib` | 3145 / 550 | 2785 / 97 | +| `kb-logging` | 72 / 27 | 22 / 3 | +| `kb-program-ids` | 42 / 28 | 1413 / 334 | +| `kb-pipeline` | 287 / 112 | 436 / 38 | +| `kb-pipeline-demo-scenarios` | 145 / 75 | 173 / 18 | +| `kb-onchain-transport` | 125 / 56 | 530 / 50 | +| `kb-store` | 157 / 81 | 344 / 30 | +| `kb-wallet` | 75 / 30 | 53 / 17 | Le volume `kb-lib` inclut notamment les identités techniques `kb-lib.*`; il ne doit pas être interprété comme autant d'importations Cargo. @@ -145,264 +145,264 @@ Les targets de tracing de crates génériques doivent également migrer vers `ks ### 6.1 Inventaire exhaustif des identités `kb-lib.*` -| Identité actuelle | Identité cible | -|---|---| -| `kb-lib.decoder.adapter.saber_decimal_wrapper` | `ks-lib-decoder.adapter.saber_decimal_wrapper` | -| `kb-lib.decoder.adapter.spl_token_wrap` | `ks-lib-decoder.adapter.spl_token_wrap` | -| `kb-lib.decoder.admin.jupiter_lock` | `ks-lib-decoder.admin.jupiter_lock` | -| `kb-lib.decoder.admin.pump_fees` | `ks-lib-decoder.admin.pump_fees` | -| `kb-lib.decoder.amm.aldrin_v1` | `ks-lib-decoder.amm.aldrin_v1` | -| `kb-lib.decoder.amm.aldrin_v2` | `ks-lib-decoder.amm.aldrin_v2` | -| `kb-lib.decoder.amm.alphaq` | `ks-lib-decoder.amm.alphaq` | -| `kb-lib.decoder.amm.believe` | `ks-lib-decoder.amm.believe` | -| `kb-lib.decoder.amm.bonk_swap` | `ks-lib-decoder.amm.bonk_swap` | -| `kb-lib.decoder.amm.fluxbeam` | `ks-lib-decoder.amm.fluxbeam` | -| `kb-lib.decoder.amm.goon_fi` | `ks-lib-decoder.amm.goon_fi` | -| `kb-lib.decoder.amm.goosefx_gamma` | `ks-lib-decoder.amm.goosefx_gamma` | -| `kb-lib.decoder.amm.goosefx_v2` | `ks-lib-decoder.amm.goosefx_v2` | -| `kb-lib.decoder.amm.guac_swap` | `ks-lib-decoder.amm.guac_swap` | -| `kb-lib.decoder.amm.lifinity_swap_v2` | `ks-lib-decoder.amm.lifinity_swap_v2` | -| `kb-lib.decoder.amm.metadao_futarchy_amm` | `ks-lib-decoder.amm.metadao_futarchy_amm` | -| `kb-lib.decoder.amm.metadao_v0_5` | `ks-lib-decoder.amm.metadao_v0_5` | -| `kb-lib.decoder.amm.meteora_damm_v1` | `ks-lib-decoder.amm.meteora_damm_v1` | -| `kb-lib.decoder.amm.meteora_damm_v2` | `ks-lib-decoder.amm.meteora_damm_v2` | -| `kb-lib.decoder.amm.obric_v2` | `ks-lib-decoder.amm.obric_v2` | -| `kb-lib.decoder.amm.one_dex` | `ks-lib-decoder.amm.one_dex` | -| `kb-lib.decoder.amm.pump_swap` | `ks-lib-decoder.amm.pump_swap` | -| `kb-lib.decoder.amm.raydium_lp_v4` | `ks-lib-decoder.amm.raydium_lp_v4` | -| `kb-lib.decoder.amm.solfi` | `ks-lib-decoder.amm.solfi` | -| `kb-lib.decoder.amm.solfi_v2` | `ks-lib-decoder.amm.solfi_v2` | -| `kb-lib.decoder.amm.vertigo` | `ks-lib-decoder.amm.vertigo` | -| `kb-lib.decoder.amm.virtuals` | `ks-lib-decoder.amm.virtuals` | -| `kb-lib.decoder.amm.woofi` | `ks-lib-decoder.amm.woofi` | -| `kb-lib.decoder.amm.zero_fi` | `ks-lib-decoder.amm.zero_fi` | -| `kb-lib.decoder.amm.zora` | `ks-lib-decoder.amm.zora` | -| `kb-lib.decoder.anchor` | `ks-lib-decoder.anchor` | -| `kb-lib.decoder.api` | `ks-lib-decoder.api` | -| `kb-lib.decoder.bridge.circle_cctp_token_messenger_minter` | `ks-lib-decoder.bridge.circle_cctp_token_messenger_minter` | -| `kb-lib.decoder.bridge.circle_cctp_token_messenger_minter_v2` | `ks-lib-decoder.bridge.circle_cctp_token_messenger_minter_v2` | -| `kb-lib.decoder.bridge.layer_zero_endpoint` | `ks-lib-decoder.bridge.layer_zero_endpoint` | -| `kb-lib.decoder.bridge.layer_zero_executor` | `ks-lib-decoder.bridge.layer_zero_executor` | -| `kb-lib.decoder.clmm.byreal` | `ks-lib-decoder.clmm.byreal` | -| `kb-lib.decoder.clmm.fusion` | `ks-lib-decoder.clmm.fusion` | -| `kb-lib.decoder.clmm.orca_whirlpool` | `ks-lib-decoder.clmm.orca_whirlpool` | -| `kb-lib.decoder.clmm.pancake_swap` | `ks-lib-decoder.clmm.pancake_swap` | -| `kb-lib.decoder.clmm.raydium` | `ks-lib-decoder.clmm.raydium` | -| `kb-lib.decoder.clmm.stabble` | `ks-lib-decoder.clmm.stabble` | -| `kb-lib.decoder.cpmm.raydium` | `ks-lib-decoder.cpmm.raydium` | -| `kb-lib.decoder.dlmm.meteora` | `ks-lib-decoder.dlmm.meteora` | -| `kb-lib.decoder.fees.bags_fee_share_v1` | `ks-lib-decoder.fees.bags_fee_share_v1` | -| `kb-lib.decoder.fees.bags_fee_share_v2` | `ks-lib-decoder.fees.bags_fee_share_v2` | -| `kb-lib.decoder.fees.pump_fees` | `ks-lib-decoder.fees.pump_fees` | -| `kb-lib.decoder.governance.metadao_bid_wall` | `ks-lib-decoder.governance.metadao_bid_wall` | -| `kb-lib.decoder.governance.metadao_futarchy` | `ks-lib-decoder.governance.metadao_futarchy` | -| `kb-lib.decoder.governance.squads_multisig` | `ks-lib-decoder.governance.squads_multisig` | -| `kb-lib.decoder.launchpad.boop_fun` | `ks-lib-decoder.launchpad.boop_fun` | -| `kb-lib.decoder.launchpad.metadao_ico` | `ks-lib-decoder.launchpad.metadao_ico` | -| `kb-lib.decoder.launchpad.meteora_dbc` | `ks-lib-decoder.launchpad.meteora_dbc` | -| `kb-lib.decoder.launchpad.moonit` | `ks-lib-decoder.launchpad.moonit` | -| `kb-lib.decoder.launchpad.orca_wavebreak` | `ks-lib-decoder.launchpad.orca_wavebreak` | -| `kb-lib.decoder.launchpad.printr` | `ks-lib-decoder.launchpad.printr` | -| `kb-lib.decoder.launchpad.pump_fun` | `ks-lib-decoder.launchpad.pump_fun` | -| `kb-lib.decoder.launchpad.pump_pumpup_ai` | `ks-lib-decoder.launchpad.pump_pumpup_ai` | -| `kb-lib.decoder.launchpad.raydium_launchlab` | `ks-lib-decoder.launchpad.raydium_launchlab` | -| `kb-lib.decoder.launchpad.virtuals` | `ks-lib-decoder.launchpad.virtuals` | -| `kb-lib.decoder.lending.clone` | `ks-lib-decoder.lending.clone` | -| `kb-lib.decoder.lending.jupiter_lend_borrow` | `ks-lib-decoder.lending.jupiter_lend_borrow` | -| `kb-lib.decoder.lending.jupiter_lend_earn` | `ks-lib-decoder.lending.jupiter_lend_earn` | -| `kb-lib.decoder.lending.jupiter_lend_flash_loan` | `ks-lib-decoder.lending.jupiter_lend_flash_loan` | -| `kb-lib.decoder.lending.jupiter_lend_liquidity` | `ks-lib-decoder.lending.jupiter_lend_liquidity` | -| `kb-lib.decoder.lending.kamino` | `ks-lib-decoder.lending.kamino` | -| `kb-lib.decoder.lending.marginfi_v2` | `ks-lib-decoder.lending.marginfi_v2` | -| `kb-lib.decoder.lock.raydium_lp` | `ks-lib-decoder.lock.raydium_lp` | -| `kb-lib.decoder.metadata.metaplex_token_metadata` | `ks-lib-decoder.metadata.metaplex_token_metadata` | -| `kb-lib.decoder.metadata.solana_program_metadata` | `ks-lib-decoder.metadata.solana_program_metadata` | -| `kb-lib.decoder.metadata.spl_name_service` | `ks-lib-decoder.metadata.spl_name_service` | -| `kb-lib.decoder.nft.metaplex_bubblegum` | `ks-lib-decoder.nft.metaplex_bubblegum` | -| `kb-lib.decoder.nft.tensor_cnft` | `ks-lib-decoder.nft.tensor_cnft` | -| `kb-lib.decoder.orderbook.jupiter_limit_order` | `ks-lib-decoder.orderbook.jupiter_limit_order` | -| `kb-lib.decoder.orderbook.jupiter_limit_order_v2` | `ks-lib-decoder.orderbook.jupiter_limit_order_v2` | -| `kb-lib.decoder.orderbook.openbook_v2` | `ks-lib-decoder.orderbook.openbook_v2` | -| `kb-lib.decoder.perpetuals.drift_v2` | `ks-lib-decoder.perpetuals.drift_v2` | -| `kb-lib.decoder.perpetuals.jupiter` | `ks-lib-decoder.perpetuals.jupiter` | -| `kb-lib.decoder.perpetuals.phoenix_eternal` | `ks-lib-decoder.perpetuals.phoenix_eternal` | -| `kb-lib.decoder.perpetuals.zeta` | `ks-lib-decoder.perpetuals.zeta` | -| `kb-lib.decoder.router.dflow_aggregator_v4` | `ks-lib-decoder.router.dflow_aggregator_v4` | -| `kb-lib.decoder.router.jupiter_aggregator_v4` | `ks-lib-decoder.router.jupiter_aggregator_v4` | -| `kb-lib.decoder.router.jupiter_aggregator_v6` | `ks-lib-decoder.router.jupiter_aggregator_v6` | -| `kb-lib.decoder.router.jupiter_dca` | `ks-lib-decoder.router.jupiter_dca` | -| `kb-lib.decoder.router.okx_labs_v1` | `ks-lib-decoder.router.okx_labs_v1` | -| `kb-lib.decoder.router.okx_labs_v2` | `ks-lib-decoder.router.okx_labs_v2` | -| `kb-lib.decoder.rwa.ondo_global_markets` | `ks-lib-decoder.rwa.ondo_global_markets` | -| `kb-lib.decoder.solana.core` | `ks-lib-decoder.solana.core` | -| `kb-lib.decoder.spl.account_compression` | `ks-lib-decoder.spl.account_compression` | -| `kb-lib.decoder.spl.associated_token_account` | `ks-lib-decoder.spl.associated_token_account` | -| `kb-lib.decoder.spl.elgamal_registry` | `ks-lib-decoder.spl.elgamal_registry` | -| `kb-lib.decoder.spl.memo` | `ks-lib-decoder.spl.memo` | -| `kb-lib.decoder.spl.noop` | `ks-lib-decoder.spl.noop` | -| `kb-lib.decoder.spl.single_pool` | `ks-lib-decoder.spl.single_pool` | -| `kb-lib.decoder.spl.stake_pool` | `ks-lib-decoder.spl.stake_pool` | -| `kb-lib.decoder.spl.token` | `ks-lib-decoder.spl.token` | -| `kb-lib.decoder.spl.token_2022` | `ks-lib-decoder.spl.token_2022` | -| `kb-lib.decoder.stable.swap_hylo_exchange` | `ks-lib-decoder.stable.swap_hylo_exchange` | -| `kb-lib.decoder.stable.swap_jupiter_stable` | `ks-lib-decoder.stable.swap_jupiter_stable` | -| `kb-lib.decoder.stable.swap_numeraire` | `ks-lib-decoder.stable.swap_numeraire` | -| `kb-lib.decoder.stable.swap_stabble` | `ks-lib-decoder.stable.swap_stabble` | -| `kb-lib.decoder.staking.jito_tip_distribution` | `ks-lib-decoder.staking.jito_tip_distribution` | -| `kb-lib.decoder.staking.kamino_farm` | `ks-lib-decoder.staking.kamino_farm` | -| `kb-lib.decoder.staking.marinade_finance` | `ks-lib-decoder.staking.marinade_finance` | -| `kb-lib.decoder.staking.solayer` | `ks-lib-decoder.staking.solayer` | -| `kb-lib.decoder.storage.solana_record` | `ks-lib-decoder.storage.solana_record` | -| `kb-lib.decoder.strategy.jupiter_dca` | `ks-lib-decoder.strategy.jupiter_dca` | -| `kb-lib.decoder.treasury.helium_treasury_management` | `ks-lib-decoder.treasury.helium_treasury_management` | -| `kb-lib.decoder.vault.carrot_defi` | `ks-lib-decoder.vault.carrot_defi` | -| `kb-lib.decoder.vault.hylo_stability_pool` | `ks-lib-decoder.vault.hylo_stability_pool` | -| `kb-lib.decoder.vault.kamino` | `ks-lib-decoder.vault.kamino` | -| `kb-lib.decoder.vault.kamino_v2` | `ks-lib-decoder.vault.kamino_v2` | -| `kb-lib.decoder.vault.kamino_yvaults` | `ks-lib-decoder.vault.kamino_yvaults` | -| `kb-lib.decoder.vault.meteora` | `ks-lib-decoder.vault.meteora` | -| `kb-lib.decoder.vesting.jupiter_lock` | `ks-lib-decoder.vesting.jupiter_lock` | -| `kb-lib.decoder.vesting.streamflow` | `ks-lib-decoder.vesting.streamflow` | -| `kb-lib.decoder.wallet.jupiter_apepro_smart_wallet` | `ks-lib-decoder.wallet.jupiter_apepro_smart_wallet` | -| `kb-lib.decoder.weighted.swap_stabble` | `ks-lib-decoder.weighted.swap_stabble` | -| `kb-lib.executor.adapter.saber_decimal_wrapper` | `ks-lib-executor.adapter.saber_decimal_wrapper` | -| `kb-lib.executor.adapter.spl_token_wrap` | `ks-lib-executor.adapter.spl_token_wrap` | -| `kb-lib.executor.amm.aldrin_v1` | `ks-lib-executor.amm.aldrin_v1` | -| `kb-lib.executor.amm.aldrin_v2` | `ks-lib-executor.amm.aldrin_v2` | -| `kb-lib.executor.amm.alphaq` | `ks-lib-executor.amm.alphaq` | -| `kb-lib.executor.amm.believe` | `ks-lib-executor.amm.believe` | -| `kb-lib.executor.amm.bonk_swap` | `ks-lib-executor.amm.bonk_swap` | -| `kb-lib.executor.amm.fluxbeam` | `ks-lib-executor.amm.fluxbeam` | -| `kb-lib.executor.amm.goon_fi` | `ks-lib-executor.amm.goon_fi` | -| `kb-lib.executor.amm.goosefx_gamma` | `ks-lib-executor.amm.goosefx_gamma` | -| `kb-lib.executor.amm.goosefx_v2` | `ks-lib-executor.amm.goosefx_v2` | -| `kb-lib.executor.amm.guac_swap` | `ks-lib-executor.amm.guac_swap` | -| `kb-lib.executor.amm.lifinity_swap_v2` | `ks-lib-executor.amm.lifinity_swap_v2` | -| `kb-lib.executor.amm.metadao_v0_5` | `ks-lib-executor.amm.metadao_v0_5` | -| `kb-lib.executor.amm.meteora_damm_v1` | `ks-lib-executor.amm.meteora_damm_v1` | -| `kb-lib.executor.amm.meteora_damm_v2` | `ks-lib-executor.amm.meteora_damm_v2` | -| `kb-lib.executor.amm.obric_v2` | `ks-lib-executor.amm.obric_v2` | -| `kb-lib.executor.amm.one_dex` | `ks-lib-executor.amm.one_dex` | -| `kb-lib.executor.amm.pump_swap` | `ks-lib-executor.amm.pump_swap` | -| `kb-lib.executor.amm.raydium_lp_v4` | `ks-lib-executor.amm.raydium_lp_v4` | -| `kb-lib.executor.amm.solfi` | `ks-lib-executor.amm.solfi` | -| `kb-lib.executor.amm.solfi_v2` | `ks-lib-executor.amm.solfi_v2` | -| `kb-lib.executor.amm.vertigo` | `ks-lib-executor.amm.vertigo` | -| `kb-lib.executor.amm.woofi` | `ks-lib-executor.amm.woofi` | -| `kb-lib.executor.amm.zero_fi` | `ks-lib-executor.amm.zero_fi` | -| `kb-lib.executor.amm.zora` | `ks-lib-executor.amm.zora` | -| `kb-lib.executor.bridge.circle_cctp_token_messenger_minter` | `ks-lib-executor.bridge.circle_cctp_token_messenger_minter` | +| Identité actuelle | Identité cible | +|----------------------------------------------------------------|----------------------------------------------------------------| +| `kb-lib.decoder.adapter.saber_decimal_wrapper` | `ks-lib-decoder.adapter.saber_decimal_wrapper` | +| `kb-lib.decoder.adapter.spl_token_wrap` | `ks-lib-decoder.adapter.spl_token_wrap` | +| `kb-lib.decoder.admin.jupiter_lock` | `ks-lib-decoder.admin.jupiter_lock` | +| `kb-lib.decoder.admin.pump_fees` | `ks-lib-decoder.admin.pump_fees` | +| `kb-lib.decoder.amm.aldrin_v1` | `ks-lib-decoder.amm.aldrin_v1` | +| `kb-lib.decoder.amm.aldrin_v2` | `ks-lib-decoder.amm.aldrin_v2` | +| `kb-lib.decoder.amm.alphaq` | `ks-lib-decoder.amm.alphaq` | +| `kb-lib.decoder.amm.believe` | `ks-lib-decoder.amm.believe` | +| `kb-lib.decoder.amm.bonk_swap` | `ks-lib-decoder.amm.bonk_swap` | +| `kb-lib.decoder.amm.fluxbeam` | `ks-lib-decoder.amm.fluxbeam` | +| `kb-lib.decoder.amm.goon_fi` | `ks-lib-decoder.amm.goon_fi` | +| `kb-lib.decoder.amm.goosefx_gamma` | `ks-lib-decoder.amm.goosefx_gamma` | +| `kb-lib.decoder.amm.goosefx_v2` | `ks-lib-decoder.amm.goosefx_v2` | +| `kb-lib.decoder.amm.guac_swap` | `ks-lib-decoder.amm.guac_swap` | +| `kb-lib.decoder.amm.lifinity_swap_v2` | `ks-lib-decoder.amm.lifinity_swap_v2` | +| `kb-lib.decoder.amm.metadao_futarchy_amm` | `ks-lib-decoder.amm.metadao_futarchy_amm` | +| `kb-lib.decoder.amm.metadao_v0_5` | `ks-lib-decoder.amm.metadao_v0_5` | +| `kb-lib.decoder.amm.meteora_damm_v1` | `ks-lib-decoder.amm.meteora_damm_v1` | +| `kb-lib.decoder.amm.meteora_damm_v2` | `ks-lib-decoder.amm.meteora_damm_v2` | +| `kb-lib.decoder.amm.obric_v2` | `ks-lib-decoder.amm.obric_v2` | +| `kb-lib.decoder.amm.one_dex` | `ks-lib-decoder.amm.one_dex` | +| `kb-lib.decoder.amm.pump_swap` | `ks-lib-decoder.amm.pump_swap` | +| `kb-lib.decoder.amm.raydium_lp_v4` | `ks-lib-decoder.amm.raydium_lp_v4` | +| `kb-lib.decoder.amm.solfi` | `ks-lib-decoder.amm.solfi` | +| `kb-lib.decoder.amm.solfi_v2` | `ks-lib-decoder.amm.solfi_v2` | +| `kb-lib.decoder.amm.vertigo` | `ks-lib-decoder.amm.vertigo` | +| `kb-lib.decoder.amm.virtuals` | `ks-lib-decoder.amm.virtuals` | +| `kb-lib.decoder.amm.woofi` | `ks-lib-decoder.amm.woofi` | +| `kb-lib.decoder.amm.zero_fi` | `ks-lib-decoder.amm.zero_fi` | +| `kb-lib.decoder.amm.zora` | `ks-lib-decoder.amm.zora` | +| `kb-lib.decoder.anchor` | `ks-lib-decoder.anchor` | +| `kb-lib.decoder.api` | `ks-lib-decoder.api` | +| `kb-lib.decoder.bridge.circle_cctp_token_messenger_minter` | `ks-lib-decoder.bridge.circle_cctp_token_messenger_minter` | +| `kb-lib.decoder.bridge.circle_cctp_token_messenger_minter_v2` | `ks-lib-decoder.bridge.circle_cctp_token_messenger_minter_v2` | +| `kb-lib.decoder.bridge.layer_zero_endpoint` | `ks-lib-decoder.bridge.layer_zero_endpoint` | +| `kb-lib.decoder.bridge.layer_zero_executor` | `ks-lib-decoder.bridge.layer_zero_executor` | +| `kb-lib.decoder.clmm.byreal` | `ks-lib-decoder.clmm.byreal` | +| `kb-lib.decoder.clmm.fusion` | `ks-lib-decoder.clmm.fusion` | +| `kb-lib.decoder.clmm.orca_whirlpool` | `ks-lib-decoder.clmm.orca_whirlpool` | +| `kb-lib.decoder.clmm.pancake_swap` | `ks-lib-decoder.clmm.pancake_swap` | +| `kb-lib.decoder.clmm.raydium` | `ks-lib-decoder.clmm.raydium` | +| `kb-lib.decoder.clmm.stabble` | `ks-lib-decoder.clmm.stabble` | +| `kb-lib.decoder.cpmm.raydium` | `ks-lib-decoder.cpmm.raydium` | +| `kb-lib.decoder.dlmm.meteora` | `ks-lib-decoder.dlmm.meteora` | +| `kb-lib.decoder.fees.bags_fee_share_v1` | `ks-lib-decoder.fees.bags_fee_share_v1` | +| `kb-lib.decoder.fees.bags_fee_share_v2` | `ks-lib-decoder.fees.bags_fee_share_v2` | +| `kb-lib.decoder.fees.pump_fees` | `ks-lib-decoder.fees.pump_fees` | +| `kb-lib.decoder.governance.metadao_bid_wall` | `ks-lib-decoder.governance.metadao_bid_wall` | +| `kb-lib.decoder.governance.metadao_futarchy` | `ks-lib-decoder.governance.metadao_futarchy` | +| `kb-lib.decoder.governance.squads_multisig` | `ks-lib-decoder.governance.squads_multisig` | +| `kb-lib.decoder.launchpad.boop_fun` | `ks-lib-decoder.launchpad.boop_fun` | +| `kb-lib.decoder.launchpad.metadao_ico` | `ks-lib-decoder.launchpad.metadao_ico` | +| `kb-lib.decoder.launchpad.meteora_dbc` | `ks-lib-decoder.launchpad.meteora_dbc` | +| `kb-lib.decoder.launchpad.moonit` | `ks-lib-decoder.launchpad.moonit` | +| `kb-lib.decoder.launchpad.orca_wavebreak` | `ks-lib-decoder.launchpad.orca_wavebreak` | +| `kb-lib.decoder.launchpad.printr` | `ks-lib-decoder.launchpad.printr` | +| `kb-lib.decoder.launchpad.pump_fun` | `ks-lib-decoder.launchpad.pump_fun` | +| `kb-lib.decoder.launchpad.pump_pumpup_ai` | `ks-lib-decoder.launchpad.pump_pumpup_ai` | +| `kb-lib.decoder.launchpad.raydium_launchlab` | `ks-lib-decoder.launchpad.raydium_launchlab` | +| `kb-lib.decoder.launchpad.virtuals` | `ks-lib-decoder.launchpad.virtuals` | +| `kb-lib.decoder.lending.clone` | `ks-lib-decoder.lending.clone` | +| `kb-lib.decoder.lending.jupiter_lend_borrow` | `ks-lib-decoder.lending.jupiter_lend_borrow` | +| `kb-lib.decoder.lending.jupiter_lend_earn` | `ks-lib-decoder.lending.jupiter_lend_earn` | +| `kb-lib.decoder.lending.jupiter_lend_flash_loan` | `ks-lib-decoder.lending.jupiter_lend_flash_loan` | +| `kb-lib.decoder.lending.jupiter_lend_liquidity` | `ks-lib-decoder.lending.jupiter_lend_liquidity` | +| `kb-lib.decoder.lending.kamino` | `ks-lib-decoder.lending.kamino` | +| `kb-lib.decoder.lending.marginfi_v2` | `ks-lib-decoder.lending.marginfi_v2` | +| `kb-lib.decoder.lock.raydium_lp` | `ks-lib-decoder.lock.raydium_lp` | +| `kb-lib.decoder.metadata.metaplex_token_metadata` | `ks-lib-decoder.metadata.metaplex_token_metadata` | +| `kb-lib.decoder.metadata.solana_program_metadata` | `ks-lib-decoder.metadata.solana_program_metadata` | +| `kb-lib.decoder.metadata.spl_name_service` | `ks-lib-decoder.metadata.spl_name_service` | +| `kb-lib.decoder.nft.metaplex_bubblegum` | `ks-lib-decoder.nft.metaplex_bubblegum` | +| `kb-lib.decoder.nft.tensor_cnft` | `ks-lib-decoder.nft.tensor_cnft` | +| `kb-lib.decoder.orderbook.jupiter_limit_order` | `ks-lib-decoder.orderbook.jupiter_limit_order` | +| `kb-lib.decoder.orderbook.jupiter_limit_order_v2` | `ks-lib-decoder.orderbook.jupiter_limit_order_v2` | +| `kb-lib.decoder.orderbook.openbook_v2` | `ks-lib-decoder.orderbook.openbook_v2` | +| `kb-lib.decoder.perpetuals.drift_v2` | `ks-lib-decoder.perpetuals.drift_v2` | +| `kb-lib.decoder.perpetuals.jupiter` | `ks-lib-decoder.perpetuals.jupiter` | +| `kb-lib.decoder.perpetuals.phoenix_eternal` | `ks-lib-decoder.perpetuals.phoenix_eternal` | +| `kb-lib.decoder.perpetuals.zeta` | `ks-lib-decoder.perpetuals.zeta` | +| `kb-lib.decoder.router.dflow_aggregator_v4` | `ks-lib-decoder.router.dflow_aggregator_v4` | +| `kb-lib.decoder.router.jupiter_aggregator_v4` | `ks-lib-decoder.router.jupiter_aggregator_v4` | +| `kb-lib.decoder.router.jupiter_aggregator_v6` | `ks-lib-decoder.router.jupiter_aggregator_v6` | +| `kb-lib.decoder.router.jupiter_dca` | `ks-lib-decoder.router.jupiter_dca` | +| `kb-lib.decoder.router.okx_labs_v1` | `ks-lib-decoder.router.okx_labs_v1` | +| `kb-lib.decoder.router.okx_labs_v2` | `ks-lib-decoder.router.okx_labs_v2` | +| `kb-lib.decoder.rwa.ondo_global_markets` | `ks-lib-decoder.rwa.ondo_global_markets` | +| `kb-lib.decoder.solana.core` | `ks-lib-decoder.solana.core` | +| `kb-lib.decoder.spl.account_compression` | `ks-lib-decoder.spl.account_compression` | +| `kb-lib.decoder.spl.associated_token_account` | `ks-lib-decoder.spl.associated_token_account` | +| `kb-lib.decoder.spl.elgamal_registry` | `ks-lib-decoder.spl.elgamal_registry` | +| `kb-lib.decoder.spl.memo` | `ks-lib-decoder.spl.memo` | +| `kb-lib.decoder.spl.noop` | `ks-lib-decoder.spl.noop` | +| `kb-lib.decoder.spl.single_pool` | `ks-lib-decoder.spl.single_pool` | +| `kb-lib.decoder.spl.stake_pool` | `ks-lib-decoder.spl.stake_pool` | +| `kb-lib.decoder.spl.token` | `ks-lib-decoder.spl.token` | +| `kb-lib.decoder.spl.token_2022` | `ks-lib-decoder.spl.token_2022` | +| `kb-lib.decoder.stable.swap_hylo_exchange` | `ks-lib-decoder.stable.swap_hylo_exchange` | +| `kb-lib.decoder.stable.swap_jupiter_stable` | `ks-lib-decoder.stable.swap_jupiter_stable` | +| `kb-lib.decoder.stable.swap_numeraire` | `ks-lib-decoder.stable.swap_numeraire` | +| `kb-lib.decoder.stable.swap_stabble` | `ks-lib-decoder.stable.swap_stabble` | +| `kb-lib.decoder.staking.jito_tip_distribution` | `ks-lib-decoder.staking.jito_tip_distribution` | +| `kb-lib.decoder.staking.kamino_farm` | `ks-lib-decoder.staking.kamino_farm` | +| `kb-lib.decoder.staking.marinade_finance` | `ks-lib-decoder.staking.marinade_finance` | +| `kb-lib.decoder.staking.solayer` | `ks-lib-decoder.staking.solayer` | +| `kb-lib.decoder.storage.solana_record` | `ks-lib-decoder.storage.solana_record` | +| `kb-lib.decoder.strategy.jupiter_dca` | `ks-lib-decoder.strategy.jupiter_dca` | +| `kb-lib.decoder.treasury.helium_treasury_management` | `ks-lib-decoder.treasury.helium_treasury_management` | +| `kb-lib.decoder.vault.carrot_defi` | `ks-lib-decoder.vault.carrot_defi` | +| `kb-lib.decoder.vault.hylo_stability_pool` | `ks-lib-decoder.vault.hylo_stability_pool` | +| `kb-lib.decoder.vault.kamino` | `ks-lib-decoder.vault.kamino` | +| `kb-lib.decoder.vault.kamino_v2` | `ks-lib-decoder.vault.kamino_v2` | +| `kb-lib.decoder.vault.kamino_yvaults` | `ks-lib-decoder.vault.kamino_yvaults` | +| `kb-lib.decoder.vault.meteora` | `ks-lib-decoder.vault.meteora` | +| `kb-lib.decoder.vesting.jupiter_lock` | `ks-lib-decoder.vesting.jupiter_lock` | +| `kb-lib.decoder.vesting.streamflow` | `ks-lib-decoder.vesting.streamflow` | +| `kb-lib.decoder.wallet.jupiter_apepro_smart_wallet` | `ks-lib-decoder.wallet.jupiter_apepro_smart_wallet` | +| `kb-lib.decoder.weighted.swap_stabble` | `ks-lib-decoder.weighted.swap_stabble` | +| `kb-lib.executor.adapter.saber_decimal_wrapper` | `ks-lib-executor.adapter.saber_decimal_wrapper` | +| `kb-lib.executor.adapter.spl_token_wrap` | `ks-lib-executor.adapter.spl_token_wrap` | +| `kb-lib.executor.amm.aldrin_v1` | `ks-lib-executor.amm.aldrin_v1` | +| `kb-lib.executor.amm.aldrin_v2` | `ks-lib-executor.amm.aldrin_v2` | +| `kb-lib.executor.amm.alphaq` | `ks-lib-executor.amm.alphaq` | +| `kb-lib.executor.amm.believe` | `ks-lib-executor.amm.believe` | +| `kb-lib.executor.amm.bonk_swap` | `ks-lib-executor.amm.bonk_swap` | +| `kb-lib.executor.amm.fluxbeam` | `ks-lib-executor.amm.fluxbeam` | +| `kb-lib.executor.amm.goon_fi` | `ks-lib-executor.amm.goon_fi` | +| `kb-lib.executor.amm.goosefx_gamma` | `ks-lib-executor.amm.goosefx_gamma` | +| `kb-lib.executor.amm.goosefx_v2` | `ks-lib-executor.amm.goosefx_v2` | +| `kb-lib.executor.amm.guac_swap` | `ks-lib-executor.amm.guac_swap` | +| `kb-lib.executor.amm.lifinity_swap_v2` | `ks-lib-executor.amm.lifinity_swap_v2` | +| `kb-lib.executor.amm.metadao_v0_5` | `ks-lib-executor.amm.metadao_v0_5` | +| `kb-lib.executor.amm.meteora_damm_v1` | `ks-lib-executor.amm.meteora_damm_v1` | +| `kb-lib.executor.amm.meteora_damm_v2` | `ks-lib-executor.amm.meteora_damm_v2` | +| `kb-lib.executor.amm.obric_v2` | `ks-lib-executor.amm.obric_v2` | +| `kb-lib.executor.amm.one_dex` | `ks-lib-executor.amm.one_dex` | +| `kb-lib.executor.amm.pump_swap` | `ks-lib-executor.amm.pump_swap` | +| `kb-lib.executor.amm.raydium_lp_v4` | `ks-lib-executor.amm.raydium_lp_v4` | +| `kb-lib.executor.amm.solfi` | `ks-lib-executor.amm.solfi` | +| `kb-lib.executor.amm.solfi_v2` | `ks-lib-executor.amm.solfi_v2` | +| `kb-lib.executor.amm.vertigo` | `ks-lib-executor.amm.vertigo` | +| `kb-lib.executor.amm.woofi` | `ks-lib-executor.amm.woofi` | +| `kb-lib.executor.amm.zero_fi` | `ks-lib-executor.amm.zero_fi` | +| `kb-lib.executor.amm.zora` | `ks-lib-executor.amm.zora` | +| `kb-lib.executor.bridge.circle_cctp_token_messenger_minter` | `ks-lib-executor.bridge.circle_cctp_token_messenger_minter` | | `kb-lib.executor.bridge.circle_cctp_token_messenger_minter_v2` | `ks-lib-executor.bridge.circle_cctp_token_messenger_minter_v2` | -| `kb-lib.executor.bridge.layer_zero_endpoint` | `ks-lib-executor.bridge.layer_zero_endpoint` | -| `kb-lib.executor.bridge.layer_zero_executor` | `ks-lib-executor.bridge.layer_zero_executor` | -| `kb-lib.executor.clmm.byreal` | `ks-lib-executor.clmm.byreal` | -| `kb-lib.executor.clmm.fusion` | `ks-lib-executor.clmm.fusion` | -| `kb-lib.executor.clmm.orca_whirlpool` | `ks-lib-executor.clmm.orca_whirlpool` | -| `kb-lib.executor.clmm.pancake_swap` | `ks-lib-executor.clmm.pancake_swap` | -| `kb-lib.executor.clmm.raydium` | `ks-lib-executor.clmm.raydium` | -| `kb-lib.executor.clmm.stabble` | `ks-lib-executor.clmm.stabble` | -| `kb-lib.executor.cpmm.raydium` | `ks-lib-executor.cpmm.raydium` | -| `kb-lib.executor.dlmm.meteora` | `ks-lib-executor.dlmm.meteora` | -| `kb-lib.executor.fees.bags_fee_share_v1` | `ks-lib-executor.fees.bags_fee_share_v1` | -| `kb-lib.executor.fees.bags_fee_share_v2` | `ks-lib-executor.fees.bags_fee_share_v2` | -| `kb-lib.executor.fees.pump_fees` | `ks-lib-executor.fees.pump_fees` | -| `kb-lib.executor.governance.metadao_bid_wall` | `ks-lib-executor.governance.metadao_bid_wall` | -| `kb-lib.executor.governance.metadao_futarchy` | `ks-lib-executor.governance.metadao_futarchy` | -| `kb-lib.executor.governance.squads_multisig` | `ks-lib-executor.governance.squads_multisig` | -| `kb-lib.executor.launchpad.boop_fun` | `ks-lib-executor.launchpad.boop_fun` | -| `kb-lib.executor.launchpad.metadao_ico` | `ks-lib-executor.launchpad.metadao_ico` | -| `kb-lib.executor.launchpad.meteora_dbc` | `ks-lib-executor.launchpad.meteora_dbc` | -| `kb-lib.executor.launchpad.moonit` | `ks-lib-executor.launchpad.moonit` | -| `kb-lib.executor.launchpad.orca_wavebreak` | `ks-lib-executor.launchpad.orca_wavebreak` | -| `kb-lib.executor.launchpad.printr` | `ks-lib-executor.launchpad.printr` | -| `kb-lib.executor.launchpad.pump_fun` | `ks-lib-executor.launchpad.pump_fun` | -| `kb-lib.executor.launchpad.pump_pumpup_ai` | `ks-lib-executor.launchpad.pump_pumpup_ai` | -| `kb-lib.executor.launchpad.raydium_launchlab` | `ks-lib-executor.launchpad.raydium_launchlab` | -| `kb-lib.executor.launchpad.virtuals` | `ks-lib-executor.launchpad.virtuals` | -| `kb-lib.executor.lending.clone` | `ks-lib-executor.lending.clone` | -| `kb-lib.executor.lending.jupiter_lend_borrow` | `ks-lib-executor.lending.jupiter_lend_borrow` | -| `kb-lib.executor.lending.jupiter_lend_earn` | `ks-lib-executor.lending.jupiter_lend_earn` | -| `kb-lib.executor.lending.jupiter_lend_flash_loan` | `ks-lib-executor.lending.jupiter_lend_flash_loan` | -| `kb-lib.executor.lending.jupiter_lend_liquidity` | `ks-lib-executor.lending.jupiter_lend_liquidity` | -| `kb-lib.executor.lending.kamino` | `ks-lib-executor.lending.kamino` | -| `kb-lib.executor.lending.marginfi_v2` | `ks-lib-executor.lending.marginfi_v2` | -| `kb-lib.executor.lock.raydium_lp` | `ks-lib-executor.lock.raydium_lp` | -| `kb-lib.executor.metadata.metaplex_token_metadata` | `ks-lib-executor.metadata.metaplex_token_metadata` | -| `kb-lib.executor.metadata.solana_program_metadata` | `ks-lib-executor.metadata.solana_program_metadata` | -| `kb-lib.executor.metadata.spl_name_service` | `ks-lib-executor.metadata.spl_name_service` | -| `kb-lib.executor.nft.metaplex_bubblegum` | `ks-lib-executor.nft.metaplex_bubblegum` | -| `kb-lib.executor.nft.tensor_cnft` | `ks-lib-executor.nft.tensor_cnft` | -| `kb-lib.executor.orderbook.jupiter_limit_order` | `ks-lib-executor.orderbook.jupiter_limit_order` | -| `kb-lib.executor.orderbook.jupiter_limit_order_v2` | `ks-lib-executor.orderbook.jupiter_limit_order_v2` | -| `kb-lib.executor.orderbook.openbook_v2` | `ks-lib-executor.orderbook.openbook_v2` | -| `kb-lib.executor.perpetuals.drift_v2` | `ks-lib-executor.perpetuals.drift_v2` | -| `kb-lib.executor.perpetuals.jupiter` | `ks-lib-executor.perpetuals.jupiter` | -| `kb-lib.executor.perpetuals.phoenix_eternal` | `ks-lib-executor.perpetuals.phoenix_eternal` | -| `kb-lib.executor.perpetuals.zeta` | `ks-lib-executor.perpetuals.zeta` | -| `kb-lib.executor.router.dflow_aggregator_v4` | `ks-lib-executor.router.dflow_aggregator_v4` | -| `kb-lib.executor.router.jupiter_aggregator_v4` | `ks-lib-executor.router.jupiter_aggregator_v4` | -| `kb-lib.executor.router.jupiter_aggregator_v6` | `ks-lib-executor.router.jupiter_aggregator_v6` | -| `kb-lib.executor.router.okx_labs_v1` | `ks-lib-executor.router.okx_labs_v1` | -| `kb-lib.executor.router.okx_labs_v2` | `ks-lib-executor.router.okx_labs_v2` | -| `kb-lib.executor.rwa.ondo_global_markets` | `ks-lib-executor.rwa.ondo_global_markets` | -| `kb-lib.executor.solana.core` | `ks-lib-executor.solana.core` | -| `kb-lib.executor.solana.transaction` | `ks-lib-executor.solana.transaction` | -| `kb-lib.executor.spl.account_compression` | `ks-lib-executor.spl.account_compression` | -| `kb-lib.executor.spl.associated_token_account` | `ks-lib-executor.spl.associated_token_account` | -| `kb-lib.executor.spl.elgamal_registry` | `ks-lib-executor.spl.elgamal_registry` | -| `kb-lib.executor.spl.memo` | `ks-lib-executor.spl.memo` | -| `kb-lib.executor.spl.noop` | `ks-lib-executor.spl.noop` | -| `kb-lib.executor.spl.single_pool` | `ks-lib-executor.spl.single_pool` | -| `kb-lib.executor.spl.stake_pool` | `ks-lib-executor.spl.stake_pool` | -| `kb-lib.executor.spl.token` | `ks-lib-executor.spl.token` | -| `kb-lib.executor.spl.token-2022` | `ks-lib-executor.spl.token-2022` | -| `kb-lib.executor.spl.token_2022` | `ks-lib-executor.spl.token_2022` | -| `kb-lib.executor.stable.swap_hylo_exchange` | `ks-lib-executor.stable.swap_hylo_exchange` | -| `kb-lib.executor.stable.swap_jupiter_stable` | `ks-lib-executor.stable.swap_jupiter_stable` | -| `kb-lib.executor.stable.swap_numeraire` | `ks-lib-executor.stable.swap_numeraire` | -| `kb-lib.executor.stable.swap_stabble` | `ks-lib-executor.stable.swap_stabble` | -| `kb-lib.executor.staking.jito_tip_distribution` | `ks-lib-executor.staking.jito_tip_distribution` | -| `kb-lib.executor.staking.kamino_farm` | `ks-lib-executor.staking.kamino_farm` | -| `kb-lib.executor.staking.marinade_finance` | `ks-lib-executor.staking.marinade_finance` | -| `kb-lib.executor.staking.solayer` | `ks-lib-executor.staking.solayer` | -| `kb-lib.executor.storage.solana_record` | `ks-lib-executor.storage.solana_record` | -| `kb-lib.executor.strategy.jupiter_dca` | `ks-lib-executor.strategy.jupiter_dca` | -| `kb-lib.executor.treasury.helium_treasury_management` | `ks-lib-executor.treasury.helium_treasury_management` | -| `kb-lib.executor.vault.carrot_defi` | `ks-lib-executor.vault.carrot_defi` | -| `kb-lib.executor.vault.hylo_stability_pool` | `ks-lib-executor.vault.hylo_stability_pool` | -| `kb-lib.executor.vault.kamino` | `ks-lib-executor.vault.kamino` | -| `kb-lib.executor.vault.kamino_v2` | `ks-lib-executor.vault.kamino_v2` | -| `kb-lib.executor.vault.kamino_yvaults` | `ks-lib-executor.vault.kamino_yvaults` | -| `kb-lib.executor.vault.meteora` | `ks-lib-executor.vault.meteora` | -| `kb-lib.executor.vesting.jupiter_lock` | `ks-lib-executor.vesting.jupiter_lock` | -| `kb-lib.executor.vesting.streamflow` | `ks-lib-executor.vesting.streamflow` | -| `kb-lib.executor.wallet.jupiter_apepro_smart_wallet` | `ks-lib-executor.wallet.jupiter_apepro_smart_wallet` | -| `kb-lib.executor.weighted.swap_stabble` | `ks-lib-executor.weighted.swap_stabble` | -| `kb-lib.materializer.admin` | `ks-lib-materializer.admin` | -| `kb-lib.materializer.bridge` | `ks-lib-materializer.bridge` | -| `kb-lib.materializer.compliance.audit` | `ks-lib-materializer.compliance.audit` | -| `kb-lib.materializer.fees` | `ks-lib-materializer.fees` | -| `kb-lib.materializer.governance` | `ks-lib-materializer.governance` | -| `kb-lib.materializer.lending` | `ks-lib-materializer.lending` | -| `kb-lib.materializer.lifecycle` | `ks-lib-materializer.lifecycle` | -| `kb-lib.materializer.liquidity` | `ks-lib-materializer.liquidity` | -| `kb-lib.materializer.metadata.metaplex_token_metadata` | `ks-lib-materializer.metadata.metaplex_token_metadata` | -| `kb-lib.materializer.metadata.solana_program_metadata` | `ks-lib-materializer.metadata.solana_program_metadata` | -| `kb-lib.materializer.metadata.token_2022` | `ks-lib-materializer.metadata.token_2022` | -| `kb-lib.materializer.nft` | `ks-lib-materializer.nft` | -| `kb-lib.materializer.oracle` | `ks-lib-materializer.oracle` | -| `kb-lib.materializer.orderbook` | `ks-lib-materializer.orderbook` | -| `kb-lib.materializer.perpetuals` | `ks-lib-materializer.perpetuals` | -| `kb-lib.materializer.pool.state` | `ks-lib-materializer.pool.state` | -| `kb-lib.materializer.rewards` | `ks-lib-materializer.rewards` | -| `kb-lib.materializer.risk` | `ks-lib-materializer.risk` | -| `kb-lib.materializer.routing` | `ks-lib-materializer.routing` | -| `kb-lib.materializer.staking` | `ks-lib-materializer.staking` | -| `kb-lib.materializer.token.accounts` | `ks-lib-materializer.token.accounts` | -| `kb-lib.materializer.token.metadata_risk` | `ks-lib-materializer.token.metadata_risk` | -| `kb-lib.materializer.trades` | `ks-lib-materializer.trades` | -| `kb-lib.materializer.transaction.annotations` | `ks-lib-materializer.transaction.annotations` | -| `kb-lib.materializer.vault` | `ks-lib-materializer.vault` | +| `kb-lib.executor.bridge.layer_zero_endpoint` | `ks-lib-executor.bridge.layer_zero_endpoint` | +| `kb-lib.executor.bridge.layer_zero_executor` | `ks-lib-executor.bridge.layer_zero_executor` | +| `kb-lib.executor.clmm.byreal` | `ks-lib-executor.clmm.byreal` | +| `kb-lib.executor.clmm.fusion` | `ks-lib-executor.clmm.fusion` | +| `kb-lib.executor.clmm.orca_whirlpool` | `ks-lib-executor.clmm.orca_whirlpool` | +| `kb-lib.executor.clmm.pancake_swap` | `ks-lib-executor.clmm.pancake_swap` | +| `kb-lib.executor.clmm.raydium` | `ks-lib-executor.clmm.raydium` | +| `kb-lib.executor.clmm.stabble` | `ks-lib-executor.clmm.stabble` | +| `kb-lib.executor.cpmm.raydium` | `ks-lib-executor.cpmm.raydium` | +| `kb-lib.executor.dlmm.meteora` | `ks-lib-executor.dlmm.meteora` | +| `kb-lib.executor.fees.bags_fee_share_v1` | `ks-lib-executor.fees.bags_fee_share_v1` | +| `kb-lib.executor.fees.bags_fee_share_v2` | `ks-lib-executor.fees.bags_fee_share_v2` | +| `kb-lib.executor.fees.pump_fees` | `ks-lib-executor.fees.pump_fees` | +| `kb-lib.executor.governance.metadao_bid_wall` | `ks-lib-executor.governance.metadao_bid_wall` | +| `kb-lib.executor.governance.metadao_futarchy` | `ks-lib-executor.governance.metadao_futarchy` | +| `kb-lib.executor.governance.squads_multisig` | `ks-lib-executor.governance.squads_multisig` | +| `kb-lib.executor.launchpad.boop_fun` | `ks-lib-executor.launchpad.boop_fun` | +| `kb-lib.executor.launchpad.metadao_ico` | `ks-lib-executor.launchpad.metadao_ico` | +| `kb-lib.executor.launchpad.meteora_dbc` | `ks-lib-executor.launchpad.meteora_dbc` | +| `kb-lib.executor.launchpad.moonit` | `ks-lib-executor.launchpad.moonit` | +| `kb-lib.executor.launchpad.orca_wavebreak` | `ks-lib-executor.launchpad.orca_wavebreak` | +| `kb-lib.executor.launchpad.printr` | `ks-lib-executor.launchpad.printr` | +| `kb-lib.executor.launchpad.pump_fun` | `ks-lib-executor.launchpad.pump_fun` | +| `kb-lib.executor.launchpad.pump_pumpup_ai` | `ks-lib-executor.launchpad.pump_pumpup_ai` | +| `kb-lib.executor.launchpad.raydium_launchlab` | `ks-lib-executor.launchpad.raydium_launchlab` | +| `kb-lib.executor.launchpad.virtuals` | `ks-lib-executor.launchpad.virtuals` | +| `kb-lib.executor.lending.clone` | `ks-lib-executor.lending.clone` | +| `kb-lib.executor.lending.jupiter_lend_borrow` | `ks-lib-executor.lending.jupiter_lend_borrow` | +| `kb-lib.executor.lending.jupiter_lend_earn` | `ks-lib-executor.lending.jupiter_lend_earn` | +| `kb-lib.executor.lending.jupiter_lend_flash_loan` | `ks-lib-executor.lending.jupiter_lend_flash_loan` | +| `kb-lib.executor.lending.jupiter_lend_liquidity` | `ks-lib-executor.lending.jupiter_lend_liquidity` | +| `kb-lib.executor.lending.kamino` | `ks-lib-executor.lending.kamino` | +| `kb-lib.executor.lending.marginfi_v2` | `ks-lib-executor.lending.marginfi_v2` | +| `kb-lib.executor.lock.raydium_lp` | `ks-lib-executor.lock.raydium_lp` | +| `kb-lib.executor.metadata.metaplex_token_metadata` | `ks-lib-executor.metadata.metaplex_token_metadata` | +| `kb-lib.executor.metadata.solana_program_metadata` | `ks-lib-executor.metadata.solana_program_metadata` | +| `kb-lib.executor.metadata.spl_name_service` | `ks-lib-executor.metadata.spl_name_service` | +| `kb-lib.executor.nft.metaplex_bubblegum` | `ks-lib-executor.nft.metaplex_bubblegum` | +| `kb-lib.executor.nft.tensor_cnft` | `ks-lib-executor.nft.tensor_cnft` | +| `kb-lib.executor.orderbook.jupiter_limit_order` | `ks-lib-executor.orderbook.jupiter_limit_order` | +| `kb-lib.executor.orderbook.jupiter_limit_order_v2` | `ks-lib-executor.orderbook.jupiter_limit_order_v2` | +| `kb-lib.executor.orderbook.openbook_v2` | `ks-lib-executor.orderbook.openbook_v2` | +| `kb-lib.executor.perpetuals.drift_v2` | `ks-lib-executor.perpetuals.drift_v2` | +| `kb-lib.executor.perpetuals.jupiter` | `ks-lib-executor.perpetuals.jupiter` | +| `kb-lib.executor.perpetuals.phoenix_eternal` | `ks-lib-executor.perpetuals.phoenix_eternal` | +| `kb-lib.executor.perpetuals.zeta` | `ks-lib-executor.perpetuals.zeta` | +| `kb-lib.executor.router.dflow_aggregator_v4` | `ks-lib-executor.router.dflow_aggregator_v4` | +| `kb-lib.executor.router.jupiter_aggregator_v4` | `ks-lib-executor.router.jupiter_aggregator_v4` | +| `kb-lib.executor.router.jupiter_aggregator_v6` | `ks-lib-executor.router.jupiter_aggregator_v6` | +| `kb-lib.executor.router.okx_labs_v1` | `ks-lib-executor.router.okx_labs_v1` | +| `kb-lib.executor.router.okx_labs_v2` | `ks-lib-executor.router.okx_labs_v2` | +| `kb-lib.executor.rwa.ondo_global_markets` | `ks-lib-executor.rwa.ondo_global_markets` | +| `kb-lib.executor.solana.core` | `ks-lib-executor.solana.core` | +| `kb-lib.executor.solana.transaction` | `ks-lib-executor.solana.transaction` | +| `kb-lib.executor.spl.account_compression` | `ks-lib-executor.spl.account_compression` | +| `kb-lib.executor.spl.associated_token_account` | `ks-lib-executor.spl.associated_token_account` | +| `kb-lib.executor.spl.elgamal_registry` | `ks-lib-executor.spl.elgamal_registry` | +| `kb-lib.executor.spl.memo` | `ks-lib-executor.spl.memo` | +| `kb-lib.executor.spl.noop` | `ks-lib-executor.spl.noop` | +| `kb-lib.executor.spl.single_pool` | `ks-lib-executor.spl.single_pool` | +| `kb-lib.executor.spl.stake_pool` | `ks-lib-executor.spl.stake_pool` | +| `kb-lib.executor.spl.token` | `ks-lib-executor.spl.token` | +| `kb-lib.executor.spl.token-2022` | `ks-lib-executor.spl.token-2022` | +| `kb-lib.executor.spl.token_2022` | `ks-lib-executor.spl.token_2022` | +| `kb-lib.executor.stable.swap_hylo_exchange` | `ks-lib-executor.stable.swap_hylo_exchange` | +| `kb-lib.executor.stable.swap_jupiter_stable` | `ks-lib-executor.stable.swap_jupiter_stable` | +| `kb-lib.executor.stable.swap_numeraire` | `ks-lib-executor.stable.swap_numeraire` | +| `kb-lib.executor.stable.swap_stabble` | `ks-lib-executor.stable.swap_stabble` | +| `kb-lib.executor.staking.jito_tip_distribution` | `ks-lib-executor.staking.jito_tip_distribution` | +| `kb-lib.executor.staking.kamino_farm` | `ks-lib-executor.staking.kamino_farm` | +| `kb-lib.executor.staking.marinade_finance` | `ks-lib-executor.staking.marinade_finance` | +| `kb-lib.executor.staking.solayer` | `ks-lib-executor.staking.solayer` | +| `kb-lib.executor.storage.solana_record` | `ks-lib-executor.storage.solana_record` | +| `kb-lib.executor.strategy.jupiter_dca` | `ks-lib-executor.strategy.jupiter_dca` | +| `kb-lib.executor.treasury.helium_treasury_management` | `ks-lib-executor.treasury.helium_treasury_management` | +| `kb-lib.executor.vault.carrot_defi` | `ks-lib-executor.vault.carrot_defi` | +| `kb-lib.executor.vault.hylo_stability_pool` | `ks-lib-executor.vault.hylo_stability_pool` | +| `kb-lib.executor.vault.kamino` | `ks-lib-executor.vault.kamino` | +| `kb-lib.executor.vault.kamino_v2` | `ks-lib-executor.vault.kamino_v2` | +| `kb-lib.executor.vault.kamino_yvaults` | `ks-lib-executor.vault.kamino_yvaults` | +| `kb-lib.executor.vault.meteora` | `ks-lib-executor.vault.meteora` | +| `kb-lib.executor.vesting.jupiter_lock` | `ks-lib-executor.vesting.jupiter_lock` | +| `kb-lib.executor.vesting.streamflow` | `ks-lib-executor.vesting.streamflow` | +| `kb-lib.executor.wallet.jupiter_apepro_smart_wallet` | `ks-lib-executor.wallet.jupiter_apepro_smart_wallet` | +| `kb-lib.executor.weighted.swap_stabble` | `ks-lib-executor.weighted.swap_stabble` | +| `kb-lib.materializer.admin` | `ks-lib-materializer.admin` | +| `kb-lib.materializer.bridge` | `ks-lib-materializer.bridge` | +| `kb-lib.materializer.compliance.audit` | `ks-lib-materializer.compliance.audit` | +| `kb-lib.materializer.fees` | `ks-lib-materializer.fees` | +| `kb-lib.materializer.governance` | `ks-lib-materializer.governance` | +| `kb-lib.materializer.lending` | `ks-lib-materializer.lending` | +| `kb-lib.materializer.lifecycle` | `ks-lib-materializer.lifecycle` | +| `kb-lib.materializer.liquidity` | `ks-lib-materializer.liquidity` | +| `kb-lib.materializer.metadata.metaplex_token_metadata` | `ks-lib-materializer.metadata.metaplex_token_metadata` | +| `kb-lib.materializer.metadata.solana_program_metadata` | `ks-lib-materializer.metadata.solana_program_metadata` | +| `kb-lib.materializer.metadata.token_2022` | `ks-lib-materializer.metadata.token_2022` | +| `kb-lib.materializer.nft` | `ks-lib-materializer.nft` | +| `kb-lib.materializer.oracle` | `ks-lib-materializer.oracle` | +| `kb-lib.materializer.orderbook` | `ks-lib-materializer.orderbook` | +| `kb-lib.materializer.perpetuals` | `ks-lib-materializer.perpetuals` | +| `kb-lib.materializer.pool.state` | `ks-lib-materializer.pool.state` | +| `kb-lib.materializer.rewards` | `ks-lib-materializer.rewards` | +| `kb-lib.materializer.risk` | `ks-lib-materializer.risk` | +| `kb-lib.materializer.routing` | `ks-lib-materializer.routing` | +| `kb-lib.materializer.staking` | `ks-lib-materializer.staking` | +| `kb-lib.materializer.token.accounts` | `ks-lib-materializer.token.accounts` | +| `kb-lib.materializer.token.metadata_risk` | `ks-lib-materializer.token.metadata_risk` | +| `kb-lib.materializer.trades` | `ks-lib-materializer.trades` | +| `kb-lib.materializer.transaction.annotations` | `ks-lib-materializer.transaction.annotations` | +| `kb-lib.materializer.vault` | `ks-lib-materializer.vault` | ## 7. Inventaire des variables d'environnement @@ -446,13 +446,13 @@ Les anciennes fixtures `TOKEN_2022_*` et les alias opérateur déjà préfixés ### 7.2 Mapping code/config/tests — 85 noms -| Nom actuel | Nom cible | Classe | -|---|---|---| -| `HELIUS_API_KEY` | `KS_SECRET_HELIUS_API_KEY` | `secret` | -| `KB_CONFIG_PATH` | `KB_APP_DEMO_DESKTOP_CONFIG_PATH` | `internal` Bot | -| `KB_CONFIG_TEST_MISSING` | `KS_CONFIG_TEST_MISSING` | `internal` | -| `KB_DEVNET_AIRDROP_LAMPORTS` | `KS_DEVNET_AIRDROP_LAMPORTS` | `internal` | -| `KB_DEVNET_CONFIG_PATH` | `KS_DEVNET_CONFIG_PATH` | `internal` | +| Nom actuel | Nom cible | Classe | +|------------------------------|-----------------------------------|----------------| +| `HELIUS_API_KEY` | `KS_SECRET_HELIUS_API_KEY` | `secret` | +| `KB_CONFIG_PATH` | `KB_APP_DEMO_DESKTOP_CONFIG_PATH` | `internal` Bot | +| `KB_CONFIG_TEST_MISSING` | `KS_CONFIG_TEST_MISSING` | `internal` | +| `KB_DEVNET_AIRDROP_LAMPORTS` | `KS_DEVNET_AIRDROP_LAMPORTS` | `internal` | +| `KB_DEVNET_CONFIG_PATH` | `KS_DEVNET_CONFIG_PATH` | `internal` | La cible `KB_APP_DEMO_DESKTOP_CONFIG_PATH` remplace la transition `KS_CONFIG_PATH` de `pre.004` : une composition appartient au binaire qui la charge, alors que les documents qu’elle référence restent possédés par les crates `ks-*`. | `KB_DEVNET_EXECUTION_TEST` | `KS_DEVNET_EXECUTION_TEST` | `internal` | @@ -538,25 +538,25 @@ La cible `KB_APP_DEMO_DESKTOP_CONFIG_PATH` remplace la transition `KS_CONFIG_PAT ### 7.3 Mapping guide opérateur Devnet — 17 noms -| Nom actuel | Nom cible | Classe | -|---|---|---| -| `KB_CONFIRM_DEVNET_DATABASE_RESET` | `KS_CONFIRM_DEVNET_DATABASE_RESET` | `internal` | -| `KB_DEVNET_CLASSIC_DESTINATION_ATA` | `KS_DEVNET_CLASSIC_DESTINATION_ATA` | `internal` | -| `KB_DEVNET_CLASSIC_MINT` | `KS_DEVNET_CLASSIC_MINT` | `internal` | -| `KB_DEVNET_CLASSIC_MINT_KEYPAIR` | `KS_DEVNET_CLASSIC_MINT_KEYPAIR` | `internal` | -| `KB_DEVNET_CLASSIC_RECIPIENT` | `KS_DEVNET_CLASSIC_RECIPIENT` | `internal` | +| Nom actuel | Nom cible | Classe | +|---------------------------------------|---------------------------------------|------------| +| `KB_CONFIRM_DEVNET_DATABASE_RESET` | `KS_CONFIRM_DEVNET_DATABASE_RESET` | `internal` | +| `KB_DEVNET_CLASSIC_DESTINATION_ATA` | `KS_DEVNET_CLASSIC_DESTINATION_ATA` | `internal` | +| `KB_DEVNET_CLASSIC_MINT` | `KS_DEVNET_CLASSIC_MINT` | `internal` | +| `KB_DEVNET_CLASSIC_MINT_KEYPAIR` | `KS_DEVNET_CLASSIC_MINT_KEYPAIR` | `internal` | +| `KB_DEVNET_CLASSIC_RECIPIENT` | `KS_DEVNET_CLASSIC_RECIPIENT` | `internal` | | `KB_DEVNET_CLASSIC_RECIPIENT_KEYPAIR` | `KS_DEVNET_CLASSIC_RECIPIENT_KEYPAIR` | `internal` | -| `KB_DEVNET_CLASSIC_SOURCE_ATA` | `KS_DEVNET_CLASSIC_SOURCE_ATA` | `internal` | -| `KB_DEVNET_RECIPIENT` | `KS_DEVNET_RECIPIENT` | `internal` | -| `KB_DEVNET_SCENARIO` | `KS_DEVNET_SCENARIO` | `internal` | -| `KB_DEVNET_SIGNATURE` | `KS_DEVNET_SIGNATURE` | `internal` | -| `KB_DEVNET_TOKEN_2022_FIXTURE` | `KS_DEVNET_TOKEN_2022_FIXTURE` | `internal` | -| `KB_DEVNET_TOKEN_2022_MINT` | `KS_PUBLIC_DEVNET_TOKEN_2022_MINT` | `public` | -| `KB_DEVNET_TOKEN_2022_MINT_KEYPAIR` | `KS_DEVNET_TOKEN_2022_MINT_KEYPAIR` | `internal` | -| `KB_DEVNET_VALIDATION_DIR` | `KS_DEVNET_VALIDATION_DIR` | `internal` | -| `KB_DEVNET_WALLET_PUBKEY` | `KS_PUBLIC_DEVNET_WALLET_ADDRESS` | `public` | -| `KB_SPL_TOKEN_2022_PROGRAM_ID` | `KS_PUBLIC_SPL_TOKEN_2022_PROGRAM_ID` | `public` | -| `KB_SPL_TOKEN_PROGRAM_ID` | `KS_PUBLIC_SPL_TOKEN_PROGRAM_ID` | `public` | +| `KB_DEVNET_CLASSIC_SOURCE_ATA` | `KS_DEVNET_CLASSIC_SOURCE_ATA` | `internal` | +| `KB_DEVNET_RECIPIENT` | `KS_DEVNET_RECIPIENT` | `internal` | +| `KB_DEVNET_SCENARIO` | `KS_DEVNET_SCENARIO` | `internal` | +| `KB_DEVNET_SIGNATURE` | `KS_DEVNET_SIGNATURE` | `internal` | +| `KB_DEVNET_TOKEN_2022_FIXTURE` | `KS_DEVNET_TOKEN_2022_FIXTURE` | `internal` | +| `KB_DEVNET_TOKEN_2022_MINT` | `KS_PUBLIC_DEVNET_TOKEN_2022_MINT` | `public` | +| `KB_DEVNET_TOKEN_2022_MINT_KEYPAIR` | `KS_DEVNET_TOKEN_2022_MINT_KEYPAIR` | `internal` | +| `KB_DEVNET_VALIDATION_DIR` | `KS_DEVNET_VALIDATION_DIR` | `internal` | +| `KB_DEVNET_WALLET_PUBKEY` | `KS_PUBLIC_DEVNET_WALLET_ADDRESS` | `public` | +| `KB_SPL_TOKEN_2022_PROGRAM_ID` | `KS_PUBLIC_SPL_TOKEN_2022_PROGRAM_ID` | `public` | +| `KB_SPL_TOKEN_PROGRAM_ID` | `KS_PUBLIC_SPL_TOKEN_PROGRAM_ID` | `public` | ## 8. Décomposition des documents de configuration @@ -595,7 +595,7 @@ Le transport possède des classes WebSocket de defaults nommées. Chaque endpoin Lorsqu'une composition référence un profil, `ks-config` doit prouver son existence avant de construire le runtime. Lorsqu'aucune composition n'est fournie, les noms des `default_profile` n'ont pas besoin d'être identiques : chaque document est résolu indépendamment. La composition ne devient pas une seconde surface de paramètres : les overrides de champ restent dans le contrat spécialisé qui les possède, et les globals explicitement configurables utilisent leurs variables d'environnement dédiées. -`AppConfig/ProfileConfig` demeure provisoirement un **contrat runtime résolu** pour préserver les consommateurs pendant la migration. Les champs applicatifs `app`/`demo` restent dans la composition exclusive du desktop pendant cette transition ; leur ownership Rust/public est traité avec les DTO applicatifs de `pre.008`, sans réintroduire un document généraliste monolithique. +`AppConfig/ProfileConfig` demeure provisoirement un **contrat runtime résolu backend-only** pour préserver les consommateurs pendant la migration. Depuis `pre.008`, il ne contient plus les champs propres au desktop, ne dérive plus `serde::Serialize`/`Debug` et ne traverse plus Tauri. La section `application` de la composition est opaque à `ks-config`; le desktop la valide avec son propre schéma sans réintroduire un document généraliste monolithique. ## 9. Source, runtime, public et diagnostic @@ -606,38 +606,34 @@ La configuration doit distinguer explicitement : 3. **public** : DTO explicitement construit et strictement borné pour Tauri/TS-RS/UI ; 4. **diagnostic** : surface explicitement demandée, pouvant montrer des valeurs `KS_*` internes non sensibles mais jamais une valeur `KS_SECRET_*`. -### 9.1 Fuite actuelle à éliminer +### 9.1 Frontière appliquée en `pre.008` -`kb-app-demo-desktop/src/demo_config.rs` transporte actuellement dans `DemoConfigPayload` : +`kb-app-demo-desktop/src/demo_config.rs` ne transporte plus `AppConfig` ni `ProfileConfig`. Il construit explicitement : ```text -AppConfig complet -ProfileConfig actif complet -schema_json +runtime backend-only + -> DemoConfigPublicPayload + -> DemoConfigDiagnosticPayload ``` -La configuration est déjà résolue avant la construction du payload, puis le frontend affiche les objets via JsonViewer. Cette frontière permet donc à un secret résolu d'atteindre Tauri/UI. +La projection publique exclut URLs, DSN, chemins de stockage/wallet et politiques internes non destinées à l'UI. Le diagnostic borné ne transmet pour les valeurs sensibles que des états tels que `configured`/`missing`, plus les métadonnées internes explicitement autorisées. -La correction ne doit jamais suivre le modèle : +La correction interdit le modèle : ```text serialize(runtime_config) -> supprimer quelques champs -> exposer ``` -La seule direction admise est : +Les contrats source/runtime de `ks-config` susceptibles de contenir des secrets résolus ne dérivent ni `serde::Serialize` ni `Debug`, et les sérialiseurs publics historiques de `AppConfig` sont supprimés. La sensibilité des placeholders est classée selon `Secret > Internal > Public`; une chaîne composée contenant un placeholder `KS_SECRET_*`/`KB_SECRET_*` hérite de `Secret`. -```text -runtime_config -> construction explicite d'un PublicConfig / ConfigDiagnostics -``` - -Les types contenant des secrets ne doivent pas être exportés en TS-RS simplement parce qu'ils sont sérialisables côté backend. +TS-RS est désormais une frontière applicative : `ks-config` et `ks-lib` ne dépendent plus de `ts-rs` et ne génèrent plus de bindings. `kb-app-demo-desktop` possède les DTO TS-RS nécessaires à ses commandes Tauri. ## 10. Tests de caractérisation avant et pendant migration Avant chaque changement structurel correspondant, conserver ou ajouter des tests qui vérifient : - la topologie des dix crates et leurs dépendances attendues ; -- les 13 tests d'API externe et leurs imports par crate root ; +- les 14 tests d'API externe et leurs imports par crate root ; - l'inventaire exact des identités runtime ; - l'absence de `kb-*` / `kb_*` résiduel dans les dix crates après leur prerelease de renommage, hors exceptions explicitement listées ; - le maintien de `khadhroony-bot3` comme nom racine ; @@ -730,13 +726,14 @@ Le split partagé est considéré terminé après cette prerelease. Les champs a ### `0.5.1-pre.008` — surfaces publiques sûres, TS-RS et camouflage -- séparer types source/runtime/public/diagnostic ; -- déplacer les DTO réellement destinés à Tauri/TypeScript vers `kb-app-demo-desktop` ou des wrappers applicatifs dédiés ; -- supprimer les dérivations/export TS-RS de `ks-config` qui n'ont aucun consommateur frontend générique et auditer les 100 exports de `ks-lib` au cas par cas ; -- propager la classification secret/public/internal lors de la résolution des placeholders et valeurs composées ; -- retirer `AppConfig`/`ProfileConfig` résolus du payload Tauri public ; -- borner les DTO TS-RS et diagnostics ; -- valider qu'aucun secret ne traverse logs/UI/erreurs/sérialisation. +- **implémenté** : `AppConfig/ProfileConfig` et les documents source/runtime sensibles restent backend-only, sans `serde::Serialize` ni `Debug` ; +- **implémenté** : le fragment `application` devient opaque pour `ks-config` et est validé par le schéma possédé par `kb-app-demo-desktop` ; +- **implémenté** : `DemoConfigPayload` remplace la configuration résolue complète par des DTO public/diagnostic construits champ par champ ; +- **implémenté** : URLs RPC/WS, DSN PostgreSQL et chemins sensibles ne traversent pas le payload configuration ; les diagnostics n'exposent que des états bornés ; +- **implémenté** : classification `Secret > Internal > Public` des variables et chaînes composées, avec canaris de non-divulgation et erreurs de validation qui n'échoent plus les valeurs rejetées ; +- **implémenté** : suppression de `ts-rs` de `ks-config` et `ks-lib`, suppression de leurs dérivations/export et de leurs bindings générés historiques ; +- **implémenté** : audit workspace empêchant la réintroduction de TS-RS dans `ks-*` et de `Serialize`/`Debug` sur les contrats de configuration sensibles sans décision explicite ; +- **implémenté** : test d'API externe de la classification/camouflage et canaris desktop empêchant la fuite de secrets dans les projections Tauri. ### `0.5.1-pre.009` — clôture diff --git a/docs/rules/RULES_SPECIFIC_KHADHROONY.md b/docs/rules/RULES_SPECIFIC_KHADHROONY.md index 2d19fb0..0e466c5 100644 --- a/docs/rules/RULES_SPECIFIC_KHADHROONY.md +++ b/docs/rules/RULES_SPECIFIC_KHADHROONY.md @@ -1,5 +1,5 @@ - + # Règles spécifiques à `khadhroony-bot3` @@ -12,7 +12,7 @@ Toute divergence avec une règle générale doit être explicitement documentée - Tous les noms de fichiers et de répertoires doivent être écrits en anglais. - Les noms de fichiers et de répertoires ne doivent contenir aucun accent, espace ou caractère spécial inutile. - Les noms internes doivent utiliser le format `snake_case` lorsque c'est applicable. -- Les packages Rust utilisent le préfixe `kb-` ; leur identifiant Rust correspondant utilise automatiquement `kb_`. +- Les bibliothèques généralistes Khadhroony Solana utilisent le préfixe Cargo `ks-` et l’identifiant Rust `ks_`; les applications et futures crates réellement propres au domaine Bot utilisent `kb-` / `kb_`. - Les décodeurs, matérialisateurs et exécuteurs sont des modules de `ks-lib`, pas des crates séparées. - Le crate de journalisation s'appelle `ks-logging`. - Les réexports sont regroupés par visibilité : le bloc `pub use` précède le bloc séparé `pub(crate) use`, sans ligne vide interne à un bloc ; leur ordre naturel doit rester compatible avec `cargo fmt`. @@ -26,6 +26,8 @@ Toute divergence avec une règle générale doit être explicitement documentée - Les types Rust exportés vers TypeScript doivent utiliser des noms stables et explicites. - Les bindings générés doivent être produits dans un dossier dédié, généralement `../frontend/ts/bindings` ou `#[ts(export, export_to = "../frontend/ts/bindings/MyStruct.ts")]`. - Les types purement internes au backend ne doivent pas être exportés vers TypeScript par défaut. +- Dans l’état courant, les crates généralistes `ks-*` ne possèdent ni dépendance `ts-rs`, ni dérivation/export TS-RS, ni dossier de bindings générés. Une exception future exige un contrat TypeScript générique indépendant de Tauri, explicitement documenté et ajouté à l’audit workspace. +- Les types source/runtime de `ks-config` susceptibles de contenir des secrets ou valeurs internes ne dérivent ni `serde::Serialize` ni `Debug`. Une surface publique ou diagnostic est construite champ par champ dans l’application propriétaire ; il est interdit de sérialiser un runtime complet puis de le redacter a posteriori. - Corriger le type Rust/TS-rs source puis régénérer les bindings ; ne pas considérer une modification manuelle isolée d’un fichier généré comme un correctif durable. - `kb-app-demo-desktop/src/tauri.rs` contient les attributs `#[tauri::command]`, l’enregistrement des commandes et des wrappers privés minces ; la logique complète doit vivre dans le module fonctionnel correspondant sous une fonction `pub(crate)` testable. - Un wrapper Tauri ne doit effectuer que l’adaptation des handles/states/arguments, l’appel de la fonction de module et le retour du résultat ; toute validation métier, orchestration ou construction de payload doit rester hors de `tauri.rs`. @@ -247,8 +249,8 @@ Aucun renommage massif de modules n'est autorisé sans étape de contrôle dédi - Les endpoints WebSocket doivent sélectionner une classe de defaults nommée ; leurs timeouts, capacités et politique `auto_reconnect` peuvent être remplacés explicitement au niveau de l'endpoint. - Les autorisations `*_send_enabled` appartiennent à la politique d'exécution, pas au contrat d'identité/stockage wallet. - Les schémas JSON actifs sont conservés exclusivement sous `config/schemas/`. Les documents source spécialisés possèdent chacun leur schéma ; `resolved.app.config.schema.json` décrit uniquement le contrat runtime transitoire reconstruit par `ks-config`. -- Les exemples conformes restent sous `config/` avec un nom distinct des fichiers runtime. Les fixtures d'un contrat runtime résolu appartiennent à `test-fixtures/` et ne doivent pas être chargées en production. -- Les fichiers historiques `config/app.config.json`, `config/example.app.config.json`, `config/schemas/app.config.schema.json`, `config/example.config.json`, `config/schema.config.json` et `config/ks-pipeline-demo-scenarios.default.config.json` sont interdits dans l'état courant. +- Les exemples conformes résident exclusivement sous `config/exemples/` avec un nom distinct des fichiers runtime. Les fixtures d'un contrat runtime résolu appartiennent à `test-fixtures/` et ne doivent pas être chargées en production. +- Les fichiers historiques `config/app.config.json`, `config/example.app.config.json`, `config/schemas/app.config.schema.json`, `config/example.config.json`, les fichiers `config/example.*.config.json` hors `config/exemples/`, `config/schema.config.json` et `config/ks-pipeline-demo-scenarios.default.config.json` sont interdits dans l'état courant. - Le chemin de composition desktop peut être remplacé par `KB_APP_DEMO_DESKTOP_CONFIG_PATH`. `KS_DEVNET_CONFIG_PATH` est uniquement un override facultatif vers une composition explicite pour les scénarios Devnet ; sans cet override, les scénarios utilisent les defaults partagés. `KS_LOGGING_CONFIG_PATH` reste un override explicite du document logging. - Les fichiers JSON de configuration ne doivent pas contenir de commentaires. - Les secrets ne doivent pas être écrits en clair dans le dépôt. @@ -256,7 +258,13 @@ Aucun renommage massif de modules n'est autorisé sans étape de contrôle dédi - Les variables possédées par les composants généralistes `ks-*` utilisent obligatoirement `KS_SECRET_*`, `KS_PUBLIC_*` ou `KS_*` ; les variables réellement spécifiques à `kb-app-demo-desktop` ou à de futures crates `kb-*` utilisent `KB_SECRET_*`, `KB_PUBLIC_*` ou `KB_*`. - Le namespace est déterminé par le propriétaire fonctionnel du contrat et non par son consommateur. - Les sous-préfixes `SECRET` sont toujours non exposables ; les sous-préfixes `PUBLIC` ne sont exposables que par une surface explicitement autorisée ; les autres variables du domaine sont internes. -- Les bindings TS-RS sont prioritairement une frontière d'application Tauri. Une crate `ks-*` ne conserve une dérivation/export TypeScript que si le type constitue un contrat externe générique explicitement justifié ; sinon l'application définit un DTO/wrapper dédié. +- La sensibilité d’une valeur composée suit `Secret > Internal > Public` : une URL, un DSN ou toute autre chaîne incorporant un placeholder `KS_SECRET_*`/`KB_SECRET_*` hérite de la sensibilité `Secret`, quel que soit le nom du champ final. +- Une valeur `Secret` ne doit jamais apparaître en clair dans une sérialisation, un `Debug`, une erreur, un log, un diagnostic, un payload Tauri ou l’UI. Une valeur interne n’apparaît que dans un diagnostic explicitement borné et sémantiquement autorisé. +- La section `application` d’une composition est opaque pour `ks-config`; le binaire propriétaire définit et valide son propre schéma sous `config/schemas/` avant usage. +- Les bindings TS-RS sont une frontière d'application Tauri. Une crate `ks-*` ne conserve une dérivation/export TypeScript que si le type constitue un contrat externe générique explicitement justifié et audité ; sinon l'application définit un DTO/wrapper dédié. +- Une commande Tauri ne retourne jamais directement un contrat `ks-*` susceptible de contenir des valeurs runtime ; elle retourne un DTO appartenant à l’application et construit explicitement la projection autorisée. +- Les snapshots/runtime transport contenant une URL résolue restent backend-only : ils ne sont pas sérialisables directement, leur `Debug` est sanitisé, les DTO Tauri ne contiennent pas `endpoint_url`, et les erreurs/logs de transport ne recopient ni corps HTTP non-success ni message JSON-RPC distant susceptible de réinjecter un credential. +- Les chemins dérivés du stockage wallet sont internes ; un payload fonctionnel Tauri ne transporte pas un chemin de fixture. Seule une surface de diagnostic explicitement bornée peut exposer un chemin interne autorisé. ## Ordre de développement cible diff --git a/kb-app-demo-desktop/CHANGELOG.md b/kb-app-demo-desktop/CHANGELOG.md index 74ed5f2..9b9cbba 100644 --- a/kb-app-demo-desktop/CHANGELOG.md +++ b/kb-app-demo-desktop/CHANGELOG.md @@ -1,8 +1,20 @@ - + # CHANGELOG — kb-app-demo-desktop +## `0.5.1-pre.008` + +- Fix: remplace les snapshots `ks-onchain-transport` retournés directement par les commandes Tauri HTTP/WS par des DTO desktop sanitisés, fait supprimer l’URL des snapshots backend et supprime `endpointUrl` des payloads d’exécution/statut et ajoute des canaris empêchant toute URL résolue de traverser le frontend. Supprime également le chemin local du fixture Token-2022 des payloads fonctionnels et des erreurs affichées. + +- Fix: use the crate-root DTO façades inside the configuration projection itself so required `pub(crate)` re-exports are not dead imports. +- Fix: remove the unused desktop application-schema getter; validation continues to use the embedded schema directly. + +- remplace l'exposition Tauri de `AppConfig/ProfileConfig` résolus par une projection publique typée et un diagnostic borné construits champ par champ ; +- n'expose plus les URLs RPC/WebSocket, DSN PostgreSQL, chemins SQLite/wallet ni aucune valeur secrète ; les diagnostics correspondants transmettent seulement des états `configured`/`missing` et des paramètres non sensibles explicitement retenus ; +- devient propriétaire du fragment `application` de sa composition et le valide avec `config/schemas/kb-app-demo-desktop.application.config.schema.json` ; +- conserve TS-RS uniquement à la frontière applicative et ajoute des canaris garantissant que les valeurs sensibles injectées dans le runtime ne traversent pas les payloads configuration. + ## `0.5.1-pre.007` - consomme les nouveaux documents store/wallet/execution via la composition desktop ; diff --git a/kb-app-demo-desktop/README.md b/kb-app-demo-desktop/README.md index 7e27e41..95609db 100644 --- a/kb-app-demo-desktop/README.md +++ b/kb-app-demo-desktop/README.md @@ -1,5 +1,5 @@ - + # kb-app-demo-desktop @@ -21,6 +21,10 @@ Les commandes Tauri sont des adaptateurs minces. La logique réutilisable appart Les campagnes Devnet/Testnet réutilisables appartiennent à `ks-pipeline-demo-scenarios`. Le desktop conserve leurs adaptateurs UI/Tauri, la sélection opérateur, la progression, les payloads TS-RS et la présentation ; `0.5.4` doit retirer les orchestrations réutilisables qui subsistent encore localement. +Depuis `0.5.1-pre.008`, TS-RS appartient à cette frontière applicative : `ks-config` et `ks-lib` ne génèrent plus de bindings TypeScript. Lorsqu'un type généraliste doit être présenté au frontend, le desktop construit un DTO/wrapper dédié. + +La fenêtre Configuration ne reçoit plus `ks_config::AppConfig` ou `ProfileConfig`. Elle reçoit une projection publique typée et un diagnostic borné construits champ par champ. Les URLs, DSN et chemins sensibles ne traversent pas Tauri ; le diagnostic n'expose que leur état `configured`/`missing`. + ## Exécution Metaplex Token Metadata Le panneau conserve les scénarios synthétiques pour inspecter les contrats, mais son parcours Devnet principal expose désormais 11 campagnes qualifiées de `pre.013` et appelle directement leurs runners spécialisés de `ks-pipeline-demo-scenarios`. Les cinq variantes `Create -> Mint`, collection, Print/Burn, lifecycle pNFT et escrow soumettent uniquement les opérations autorisées ; Maintenance et Use conservent les opérations `unavailable` dans des probes simulation-only. Le desktop ne reconstruit pas les builders ; il conserve encore une partie du dispatch, des critères de complétion et de la projection de preuves, à centraliser en `0.5.4` lorsque cette logique est réutilisable. diff --git a/kb-app-demo-desktop/frontend/demo_config.html b/kb-app-demo-desktop/frontend/demo_config.html index e4aaa87..26240db 100644 --- a/kb-app-demo-desktop/frontend/demo_config.html +++ b/kb-app-demo-desktop/frontend/demo_config.html @@ -1,5 +1,5 @@ - + @@ -46,7 +46,7 @@
-

Configuration active

+

Configuration publique

Chargement...
@@ -55,7 +55,7 @@
-

Configuration complète

+

Diagnostic borné

Chargement...
@@ -64,7 +64,7 @@
-

Schéma JSON embarqué

+

Schéma JSON de composition

Chargement...
@@ -84,4 +84,4 @@ - \ No newline at end of file + diff --git a/kb-app-demo-desktop/frontend/ts/demo_config.ts b/kb-app-demo-desktop/frontend/ts/demo_config.ts index 2b71f2c..7e9e58f 100644 --- a/kb-app-demo-desktop/frontend/ts/demo_config.ts +++ b/kb-app-demo-desktop/frontend/ts/demo_config.ts @@ -1,5 +1,5 @@ // file: kb-app-demo-desktop/frontend/ts/demo_config.ts -// version: 6 +// version: 7 import * as bootstrap from "bootstrap"; import "simplebar"; @@ -55,12 +55,12 @@ function parseSchemaJson(schemaJson: string): unknown { async function loadDemoConfig(): Promise { try { const payload = await invoke("load_demo_config"); - setText("#configPath", payload.config_path); + setText("#configPath", payload.diagnostic_config.sources.find(source => source.category === "composition")?.path ?? "non disponible"); setText("#activeProfileName", payload.active_profile_name); setText("#activeEnvironment", payload.environment); - renderJsonViewer("#activeProfileJson", payload.active_profile); - renderJsonViewer("#fullConfigJson", payload.app_config); - renderJsonViewer("#schemaJson", parseSchemaJson(payload.schema_json)); + renderJsonViewer("#activeProfileJson", payload.public_config); + renderJsonViewer("#fullConfigJson", payload.diagnostic_config); + renderJsonViewer("#schemaJson", parseSchemaJson(payload.composition_schema_json)); } catch (caughtError) { const message = caughtError instanceof Error ? caughtError.message : String(caughtError); setText("#activeProfileJson", `Erreur pendant le chargement de la configuration : ${message}`); diff --git a/kb-app-demo-desktop/frontend/ts/demo_execution_spl.ts b/kb-app-demo-desktop/frontend/ts/demo_execution_spl.ts index d71f6d8..ad4851c 100644 --- a/kb-app-demo-desktop/frontend/ts/demo_execution_spl.ts +++ b/kb-app-demo-desktop/frontend/ts/demo_execution_spl.ts @@ -1,5 +1,5 @@ // file: kb-app-demo-desktop/frontend/ts/demo_execution_spl.ts -// version: 9 +// version: 10 import * as bootstrap from "bootstrap"; import "simplebar"; @@ -698,7 +698,7 @@ async function loadToken2022Fixture(): Promise { profileName: element("#executionProfileSelect").value, }); applyToken2022Fixture(); - element("#token_2022FixtureHelp").textContent = `Fixture: ${token_2022Fixture.fixturePath} — program=${token_2022Fixture.programId}`; + element("#token_2022FixtureHelp").textContent = `Fixture Token-2022 chargée — program=${token_2022Fixture.programId}`; appendLog({ timestamp: new Date().toISOString(), level: "info", diff --git a/kb-app-demo-desktop/frontend/ts/demo_http.ts b/kb-app-demo-desktop/frontend/ts/demo_http.ts index c71f5c1..e04c2e1 100644 --- a/kb-app-demo-desktop/frontend/ts/demo_http.ts +++ b/kb-app-demo-desktop/frontend/ts/demo_http.ts @@ -1,5 +1,5 @@ // file: kb-app-demo-desktop/frontend/ts/demo_http.ts -// version: 7 +// version: 9 import * as bootstrap from "bootstrap"; import "simplebar"; @@ -7,6 +7,7 @@ import ResizeObserver from "resize-observer-polyfill"; import { invoke } from "@tauri-apps/api/core"; import { frontendDebug, frontendError, installFrontendConsoleBridge } from "./frontend_log.ts"; import { renderJsonViewer } from "./json_viewer.ts"; +import type { DemoHttpEndpointPayload } from "./bindings/kb_app_demo_desktop/demo_http/DemoHttpEndpointPayload.ts"; import type { DemoHttpExecutionPayload } from "./bindings/kb_app_demo_desktop/demo_http/DemoHttpExecutionPayload.ts"; import type { DemoHttpMethodOption } from "./bindings/kb_app_demo_desktop/demo_http/DemoHttpMethodOption.ts"; import type { DemoHttpOptionsPayload } from "./bindings/kb_app_demo_desktop/demo_http/DemoHttpOptionsPayload.ts"; @@ -96,7 +97,6 @@ function formatHttpExecutionPayload(payload: DemoHttpExecutionPayload): unknown return { endpointName: payload.endpointName, provider: payload.provider, - endpointUrl: payload.endpointUrl, role: payload.role, method: payload.method, requestKind: payload.requestKind, @@ -124,7 +124,7 @@ async function refreshHttpOptions(): Promise { async function refreshHttpPool(): Promise { try { - const snapshots = await invoke("demo_http_list_pool_clients"); + const snapshots = await invoke("demo_http_list_pool_clients"); renderJsonViewer("#httpPoolOutput", snapshots); } catch (caughtError) { const message = caughtError instanceof Error ? caughtError.message : String(caughtError); diff --git a/kb-app-demo-desktop/frontend/ts/demo_ws.ts b/kb-app-demo-desktop/frontend/ts/demo_ws.ts index 7163ad3..7a96920 100644 --- a/kb-app-demo-desktop/frontend/ts/demo_ws.ts +++ b/kb-app-demo-desktop/frontend/ts/demo_ws.ts @@ -1,5 +1,5 @@ // file: kb-app-demo-desktop/frontend/ts/demo_ws.ts -// version: 8 +// version: 10 import * as bootstrap from "bootstrap"; import "simplebar"; @@ -8,6 +8,7 @@ import {invoke} from "@tauri-apps/api/core"; import {listen} from "@tauri-apps/api/event"; import {frontendDebug, frontendError, installFrontendConsoleBridge} from "./frontend_log.ts"; import {renderJsonViewer} from "./json_viewer.ts"; +import type {DemoWsEndpointPayload} from "./bindings/kb_app_demo_desktop/demo_ws/DemoWsEndpointPayload.ts"; import type {DemoWsExecutionPayload} from "./bindings/kb_app_demo_desktop/demo_ws/DemoWsExecutionPayload.ts"; import type {DemoWsMessagePayload} from "./bindings/kb_app_demo_desktop/demo_ws/DemoWsMessagePayload.ts"; import type {DemoWsMethodOption} from "./bindings/kb_app_demo_desktop/demo_ws/DemoWsMethodOption.ts"; @@ -177,7 +178,6 @@ function formatWsExecutionPayload(payload: DemoWsExecutionPayload): string { const header = { endpointName: payload.endpointName, provider: payload.provider, - endpointUrl: payload.endpointUrl, role: payload.role, method: payload.method, requestKind: payload.requestKind, @@ -214,7 +214,7 @@ async function refreshWsOptions(): Promise { async function refreshWsPool(): Promise { try { - const snapshots = await invoke("demo_ws_list_pool_clients"); + const snapshots = await invoke("demo_ws_list_pool_clients"); renderJsonViewer("#wsPoolOutput", snapshots); } catch (caughtError) { const message = caughtError instanceof Error ? caughtError.message : String(caughtError); diff --git a/kb-app-demo-desktop/src/app_state.rs b/kb-app-demo-desktop/src/app_state.rs index 84294a6..01cfd55 100644 --- a/kb-app-demo-desktop/src/app_state.rs +++ b/kb-app-demo-desktop/src/app_state.rs @@ -1,5 +1,5 @@ // file: kb-app-demo-desktop/src/app_state.rs -// version: 15 +// version: 16 //! Shared Tauri application state and startup initialization. @@ -7,6 +7,12 @@ pub(crate) struct AppState { config_path: std::string::String, logging_config_path: std::string::String, + transport_config_path: std::string::String, + listeners_config_path: std::string::String, + store_config_path: std::string::String, + wallet_config_path: std::string::String, + execution_config_path: std::string::String, + desktop_config: crate::DesktopApplicationConfig, app_config: ks_config::AppConfig, active_profile: ks_config::ProfileConfig, logging_guard: std::sync::Mutex, @@ -44,8 +50,17 @@ impl crate::AppState { std::result::Result::Ok(profile) => profile.clone(), std::result::Result::Err(error) => return std::result::Result::Err(error), }; + let desktop_config = match crate::desktop_application_config(&composition_profile) { + std::result::Result::Ok(config) => config, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; let logging_config_path = resolve_logging_config_path(&workspace_root, &composed.logging_path); + let transport_config_path = composed.transport_path.display().to_string(); + let listeners_config_path = composed.listeners_path.display().to_string(); + let store_config_path = composed.store_path.display().to_string(); + let wallet_config_path = composed.wallet_path.display().to_string(); + let execution_config_path = composed.execution_path.display().to_string(); let app_config = composed.app_config; let active_profile = match ks_config::active_profile(&app_config) { std::result::Result::Ok(profile) => profile.clone(), @@ -79,6 +94,12 @@ impl crate::AppState { return std::result::Result::Ok(crate::AppState { config_path: config_path.display().to_string(), logging_config_path: logging_config_path.display().to_string(), + transport_config_path, + listeners_config_path, + store_config_path, + wallet_config_path, + execution_config_path, + desktop_config, app_config, active_profile, logging_guard: std::sync::Mutex::new(logging_guard), @@ -108,6 +129,36 @@ impl crate::AppState { return self.logging_config_path.as_str(); } + /// Returns the path used to load the transport configuration file. + pub(crate) fn transport_config_path(&self) -> &str { + return self.transport_config_path.as_str(); + } + + /// Returns the path used to load the listeners configuration file. + pub(crate) fn listeners_config_path(&self) -> &str { + return self.listeners_config_path.as_str(); + } + + /// Returns the path used to load the store configuration file. + pub(crate) fn store_config_path(&self) -> &str { + return self.store_config_path.as_str(); + } + + /// Returns the path used to load the wallet configuration file. + pub(crate) fn wallet_config_path(&self) -> &str { + return self.wallet_config_path.as_str(); + } + + /// Returns the path used to load the execution configuration file. + pub(crate) fn execution_config_path(&self) -> &str { + return self.execution_config_path.as_str(); + } + + /// Returns desktop-owned settings selected from the active composition profile. + pub(crate) fn desktop_config(&self) -> &crate::DesktopApplicationConfig { + return &self.desktop_config; + } + /// Returns the complete parsed application configuration. pub(crate) fn app_config(&self) -> &ks_config::AppConfig { return &self.app_config; diff --git a/kb-app-demo-desktop/src/demo_config.rs b/kb-app-demo-desktop/src/demo_config.rs index e28b6ee..f596512 100644 --- a/kb-app-demo-desktop/src/demo_config.rs +++ b/kb-app-demo-desktop/src/demo_config.rs @@ -1,7 +1,7 @@ // file: kb-app-demo-desktop/src/demo_config.rs -// version: 3 +// version: 5 -//! Configuration demo payload and state projection. +//! Configuration demo payloads with explicit public and bounded diagnostic projections. use ts_rs::TS; // rust-rules: derive-import @@ -12,29 +12,432 @@ use ts_rs::TS; // rust-rules: derive-import export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_config/DemoConfigPayload.ts" )] pub(crate) struct DemoConfigPayload { - /// Path used to load the configuration file. - pub(crate) config_path: std::string::String, - /// Active profile name. + /// Active composition profile name. pub(crate) active_profile_name: std::string::String, - /// Active profile environment. + /// Desktop-owned environment label. pub(crate) environment: std::string::String, - /// Entire parsed configuration. - pub(crate) app_config: ks_config::AppConfig, - /// Active profile configuration. - pub(crate) active_profile: ks_config::ProfileConfig, - /// Embedded JSON Schema text used during loading. - pub(crate) schema_json: std::string::String, + /// Explicit public configuration projection. + pub(crate) public_config: crate::DemoConfigPublicPayload, + /// Explicit bounded diagnostic projection. + pub(crate) diagnostic_config: crate::DemoConfigDiagnosticPayload, + /// Generic binary-composition JSON Schema text. + pub(crate) composition_schema_json: std::string::String, +} + +/// Public desktop configuration projection containing no secret or internal paths. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_config/DemoConfigPublicPayload.ts" +)] +pub(crate) struct DemoConfigPublicPayload { + /// Desktop application identity. + pub(crate) application_name: std::string::String, + /// Desktop environment label. + pub(crate) environment: std::string::String, + /// Active composed profile. + pub(crate) profile_name: std::string::String, + /// Public HTTP endpoint identities without URLs or credentials. + pub(crate) http_endpoints: std::vec::Vec, + /// Public WebSocket endpoint identities without URLs or credentials. + pub(crate) ws_endpoints: std::vec::Vec, + /// Listener summary without account-specific filters. + pub(crate) listeners: crate::DemoConfigPublicListenersPayload, + /// Wallet cluster only; wallet paths and aliases remain diagnostic/internal. + pub(crate) wallet_cluster: std::string::String, + /// Desktop-owned demo feature flags. + pub(crate) demo: crate::DemoConfigPublicDemoPayload, +} + +/// Public endpoint identity with no URL or authentication material. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_config/DemoConfigPublicEndpointPayload.ts" +)] +pub(crate) struct DemoConfigPublicEndpointPayload { + /// Endpoint code. + pub(crate) name: std::string::String, + /// Whether the endpoint is enabled. + pub(crate) enabled: bool, + /// Provider code. + pub(crate) provider: std::string::String, + /// Cluster code. + pub(crate) cluster: std::string::String, +} + +/// Public listener summary. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_config/DemoConfigPublicListenersPayload.ts" +)] +pub(crate) struct DemoConfigPublicListenersPayload { + /// Whether listener creation is enabled. + pub(crate) enabled: bool, + /// Default commitment used by subscriptions. + pub(crate) default_commitment: std::string::String, + /// Number of log listener declarations. + pub(crate) log_listener_count: u32, + /// Number of program listener declarations. + pub(crate) program_listener_count: u32, + /// Number of account listener declarations. + pub(crate) account_listener_count: u32, +} + +/// Public desktop demo feature flags. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_config/DemoConfigPublicDemoPayload.ts" +)] +pub(crate) struct DemoConfigPublicDemoPayload { + /// Enables live demo pages. + pub(crate) live_demo_enabled: bool, + /// Enables trading demo pages. + pub(crate) trading_demo_enabled: bool, +} + +/// Bounded diagnostic projection containing internal configuration metadata but no secret values. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_config/DemoConfigDiagnosticPayload.ts" +)] +pub(crate) struct DemoConfigDiagnosticPayload { + /// Filesystem sources selected by the active binary composition. + pub(crate) sources: std::vec::Vec, + /// Endpoint runtime settings without endpoint URLs. + pub(crate) endpoints: std::vec::Vec, + /// Store diagnostics without database URLs or filesystem paths. + pub(crate) store: crate::DemoConfigStoreDiagnosticPayload, + /// Wallet diagnostics without wallet filesystem paths or key material. + pub(crate) wallet: crate::DemoConfigWalletDiagnosticPayload, + /// Execution-policy diagnostics. + pub(crate) execution: crate::DemoConfigExecutionDiagnosticPayload, +} + +/// One selected configuration source exposed only through the explicit diagnostic surface. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_config/DemoConfigSourceDiagnosticPayload.ts" +)] +pub(crate) struct DemoConfigSourceDiagnosticPayload { + /// Source category. + pub(crate) category: std::string::String, + /// Resolved filesystem path. + pub(crate) path: std::string::String, +} + +/// Endpoint diagnostic without URL content. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_config/DemoConfigEndpointDiagnosticPayload.ts" +)] +pub(crate) struct DemoConfigEndpointDiagnosticPayload { + /// Transport kind, either `http` or `ws`. + pub(crate) kind: std::string::String, + /// Endpoint code. + pub(crate) name: std::string::String, + /// Whether the endpoint is enabled. + pub(crate) enabled: bool, + /// Provider code. + pub(crate) provider: std::string::String, + /// Cluster code. + pub(crate) cluster: std::string::String, + /// URL state marker; the URL itself is never serialized. + pub(crate) url_state: std::string::String, + /// Connection timeout in milliseconds. + #[ts(type = "number")] + pub(crate) connect_timeout_ms: u64, + /// Request timeout in milliseconds. + #[ts(type = "number")] + pub(crate) request_timeout_ms: u64, + /// Optional unsubscribe timeout in milliseconds for WebSocket endpoints. + #[ts(type = "number | null")] + pub(crate) unsubscribe_timeout_ms: std::option::Option, + /// Optional reconnect policy for WebSocket endpoints. + pub(crate) auto_reconnect: std::option::Option, + /// Number of configured endpoint roles. + pub(crate) role_count: u32, +} + +/// Store diagnostic projection without DSN or SQLite path content. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_config/DemoConfigStoreDiagnosticPayload.ts" +)] +pub(crate) struct DemoConfigStoreDiagnosticPayload { + /// Whether persistence is enabled. + pub(crate) enabled: bool, + /// Selected store backend. + pub(crate) backend: std::string::String, + /// Whether a PostgreSQL URL is configured. + pub(crate) postgres_url_state: std::string::String, + /// PostgreSQL connection pool ceiling. + pub(crate) postgres_max_connections: u32, + /// PostgreSQL connection timeout in milliseconds. + #[ts(type = "number")] + pub(crate) postgres_connect_timeout_ms: u64, + /// Whether PostgreSQL schema initialization is enabled. + pub(crate) postgres_auto_initialize_schema: bool, + /// Whether a SQLite path is configured. + pub(crate) sqlite_path_state: std::string::String, + /// SQLite connection pool ceiling. + pub(crate) sqlite_max_connections: u32, + /// Whether SQLite schema initialization is enabled. + pub(crate) sqlite_auto_initialize_schema: bool, +} + +/// Wallet diagnostic projection without paths or secret key material. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_config/DemoConfigWalletDiagnosticPayload.ts" +)] +pub(crate) struct DemoConfigWalletDiagnosticPayload { + /// Whether a wallet directory is configured. + pub(crate) wallet_directory_state: std::string::String, + /// Wallet cluster. + pub(crate) cluster: std::string::String, + /// Whether the managed temporary wallet is enabled. + pub(crate) temporary_wallet_enabled: bool, + /// Temporary wallet alias, which is internal but not secret. + pub(crate) temporary_wallet_alias: std::string::String, + /// Whether the managed temporary wallet persists between restarts. + pub(crate) temporary_wallet_persist: bool, +} + +/// Execution-policy diagnostic projection. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_config/DemoConfigExecutionDiagnosticPayload.ts" +)] +pub(crate) struct DemoConfigExecutionDiagnosticPayload { + /// Enables local-validator sends. + pub(crate) localnet_send_enabled: bool, + /// Enables Devnet sends. + pub(crate) devnet_send_enabled: bool, + /// Enables Testnet sends. + pub(crate) testnet_send_enabled: bool, + /// Enables Mainnet sends. + pub(crate) mainnet_send_enabled: bool, + /// Default dry-run policy. + pub(crate) dry_run_default: bool, + /// Requires simulation before send. + pub(crate) require_simulation: bool, + /// Requires explicit operator confirmation. + pub(crate) require_operator_confirmation: bool, + /// Maximum local-validator spend in lamports. + #[ts(type = "number")] + pub(crate) localnet_max_spend_lamports: u64, + /// Maximum Devnet spend in lamports. + #[ts(type = "number")] + pub(crate) devnet_max_spend_lamports: u64, + /// Maximum Testnet spend in lamports. + #[ts(type = "number")] + pub(crate) testnet_max_spend_lamports: u64, + /// Maximum Mainnet spend in lamports. + #[ts(type = "number")] + pub(crate) mainnet_max_spend_lamports: u64, + /// Maximum fee in lamports. + #[ts(type = "number")] + pub(crate) max_fee_lamports: u64, + /// Maximum compute-unit price in micro-lamports. + #[ts(type = "number")] + pub(crate) max_compute_unit_price_micro_lamports: u64, + /// Maximum recent-blockhash age in slots. + #[ts(type = "number")] + pub(crate) recent_blockhash_max_age_slots: u64, + /// Maximum send retries. + pub(crate) send_max_retries: u32, + /// Confirmation poll interval in milliseconds. + #[ts(type = "number")] + pub(crate) confirmation_poll_interval_ms: u64, + /// Maximum confirmation poll attempts. + pub(crate) confirmation_max_attempts: u32, + /// Maximum Devnet faucet airdrop in lamports. + #[ts(type = "number")] + pub(crate) devnet_airdrop_max_lamports: u64, } /// Builds the configuration payload from shared application state. pub(crate) fn demo_config_payload(state: &crate::AppState) -> crate::DemoConfigPayload { + let active_profile = state.active_profile(); + let desktop = state.desktop_config(); return crate::DemoConfigPayload { - config_path: state.config_path().to_owned(), - active_profile_name: state.active_profile().name.clone(), - environment: state.active_profile().app.environment.clone(), - app_config: state.app_config().clone(), - active_profile: state.active_profile().clone(), - schema_json: ks_config::config_json_schema_text().to_owned(), + active_profile_name: active_profile.name.clone(), + environment: desktop.environment.clone(), + public_config: public_config_payload(active_profile, desktop), + diagnostic_config: diagnostic_config_payload(state), + composition_schema_json: ks_config::composition_json_schema_text().to_owned(), + }; +} + +fn public_config_payload( + profile: &ks_config::ProfileConfig, + desktop: &crate::DesktopApplicationConfig, +) -> DemoConfigPublicPayload { + let http_endpoints = profile + .solana + .http_endpoints + .iter() + .map(|endpoint| { + return DemoConfigPublicEndpointPayload { + name: endpoint.name.clone(), + enabled: endpoint.enabled, + provider: endpoint.provider.clone(), + cluster: endpoint.cluster.clone(), + }; + }) + .collect::>(); + let ws_endpoints = profile + .solana + .ws_endpoints + .iter() + .map(|endpoint| { + return DemoConfigPublicEndpointPayload { + name: endpoint.name.clone(), + enabled: endpoint.enabled, + provider: endpoint.provider.clone(), + cluster: endpoint.cluster.clone(), + }; + }) + .collect::>(); + return DemoConfigPublicPayload { + application_name: desktop.name.clone(), + environment: desktop.environment.clone(), + profile_name: profile.name.clone(), + http_endpoints, + ws_endpoints, + listeners: DemoConfigPublicListenersPayload { + enabled: profile.solana.listeners.enabled, + default_commitment: profile.solana.listeners.default_commitment.clone(), + log_listener_count: bounded_len(profile.solana.listeners.log_listeners.len()), + program_listener_count: bounded_len(profile.solana.listeners.program_listeners.len()), + account_listener_count: bounded_len(profile.solana.listeners.account_listeners.len()), + }, + wallet_cluster: profile.wallet.cluster.clone(), + demo: DemoConfigPublicDemoPayload { + live_demo_enabled: desktop.demo.live_demo_enabled, + trading_demo_enabled: desktop.demo.trading_demo_enabled, + }, + }; +} + +fn diagnostic_config_payload(state: &crate::AppState) -> DemoConfigDiagnosticPayload { + let profile = state.active_profile(); + let mut endpoints = std::vec::Vec::::new(); + for endpoint in &profile.solana.http_endpoints { + endpoints.push(DemoConfigEndpointDiagnosticPayload { + kind: "http".to_string(), + name: endpoint.name.clone(), + enabled: endpoint.enabled, + provider: endpoint.provider.clone(), + cluster: endpoint.cluster.clone(), + url_state: configured_state(endpoint.url.as_str()), + connect_timeout_ms: endpoint.connect_timeout_ms, + request_timeout_ms: endpoint.request_timeout_ms, + unsubscribe_timeout_ms: std::option::Option::None, + auto_reconnect: std::option::Option::None, + role_count: bounded_len(endpoint.roles.len()), + }); + } + for endpoint in &profile.solana.ws_endpoints { + endpoints.push(DemoConfigEndpointDiagnosticPayload { + kind: "ws".to_string(), + name: endpoint.name.clone(), + enabled: endpoint.enabled, + provider: endpoint.provider.clone(), + cluster: endpoint.cluster.clone(), + url_state: configured_state(endpoint.url.as_str()), + connect_timeout_ms: endpoint.connect_timeout_ms, + request_timeout_ms: endpoint.request_timeout_ms, + unsubscribe_timeout_ms: std::option::Option::Some(endpoint.unsubscribe_timeout_ms), + auto_reconnect: std::option::Option::Some(endpoint.auto_reconnect), + role_count: bounded_len(endpoint.roles.len()), + }); + } + return DemoConfigDiagnosticPayload { + sources: std::vec![ + source_diagnostic("composition", state.config_path()), + source_diagnostic("logging", state.logging_config_path()), + source_diagnostic("transport", state.transport_config_path()), + source_diagnostic("listeners", state.listeners_config_path()), + source_diagnostic("store", state.store_config_path()), + source_diagnostic("wallet", state.wallet_config_path()), + source_diagnostic("execution", state.execution_config_path()), + ], + endpoints, + store: DemoConfigStoreDiagnosticPayload { + enabled: profile.database.enabled, + backend: profile.database.backend.clone(), + postgres_url_state: configured_state(profile.database.postgres.url.as_str()), + postgres_max_connections: profile.database.postgres.max_connections, + postgres_connect_timeout_ms: profile.database.postgres.connect_timeout_ms, + postgres_auto_initialize_schema: profile.database.postgres.auto_initialize_schema, + sqlite_path_state: configured_state(profile.database.sqlite.path.as_str()), + sqlite_max_connections: profile.database.sqlite.max_connections, + sqlite_auto_initialize_schema: profile.database.sqlite.auto_initialize_schema, + }, + wallet: DemoConfigWalletDiagnosticPayload { + wallet_directory_state: configured_state(profile.wallet.wallet_dir.as_str()), + cluster: profile.wallet.cluster.clone(), + temporary_wallet_enabled: profile.wallet.temporary_wallet_enabled, + temporary_wallet_alias: profile.wallet.temporary_wallet_alias.clone(), + temporary_wallet_persist: profile.wallet.temporary_wallet_persist, + }, + execution: execution_diagnostic(&profile.execution), + }; +} + +fn source_diagnostic(category: &str, path: &str) -> DemoConfigSourceDiagnosticPayload { + return DemoConfigSourceDiagnosticPayload { + category: category.to_string(), + path: path.to_string(), + }; +} + +fn configured_state(value: &str) -> std::string::String { + if value.trim().is_empty() { + return "missing".to_string(); + } + return "configured".to_string(); +} + +fn bounded_len(value: usize) -> u32 { + return match u32::try_from(value) { + std::result::Result::Ok(count) => count, + std::result::Result::Err(_) => u32::MAX, + }; +} + +fn execution_diagnostic( + execution: &ks_config::ExecutionConfig, +) -> DemoConfigExecutionDiagnosticPayload { + return DemoConfigExecutionDiagnosticPayload { + localnet_send_enabled: execution.localnet_send_enabled, + devnet_send_enabled: execution.devnet_send_enabled, + testnet_send_enabled: execution.testnet_send_enabled, + mainnet_send_enabled: execution.mainnet_send_enabled, + dry_run_default: execution.dry_run_default, + require_simulation: execution.require_simulation, + require_operator_confirmation: execution.require_operator_confirmation, + localnet_max_spend_lamports: execution.localnet_max_spend_lamports, + devnet_max_spend_lamports: execution.devnet_max_spend_lamports, + testnet_max_spend_lamports: execution.testnet_max_spend_lamports, + mainnet_max_spend_lamports: execution.mainnet_max_spend_lamports, + max_fee_lamports: execution.max_fee_lamports, + max_compute_unit_price_micro_lamports: execution.max_compute_unit_price_micro_lamports, + recent_blockhash_max_age_slots: execution.recent_blockhash_max_age_slots, + send_max_retries: execution.send_max_retries, + confirmation_poll_interval_ms: execution.confirmation_poll_interval_ms, + confirmation_max_attempts: execution.confirmation_max_attempts, + devnet_airdrop_max_lamports: execution.devnet_airdrop_max_lamports, }; } @@ -42,10 +445,126 @@ pub(crate) fn demo_config_payload(state: &crate::AppState) -> crate::DemoConfigP mod tests { use ts_rs::TS; // rust-rules: trait-import + fn active_fixture_profile() -> ks_config::ProfileConfig { + let parsed = ks_config::parse_config_json(include_str!( + "../../test-fixtures/config/resolved.app.config.json" + )); + let config = match parsed { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("resolved fixture must parse: {error}"), + }; + return match ks_config::active_profile(&config) { + std::result::Result::Ok(value) => value.clone(), + std::result::Result::Err(error) => { + panic!("resolved fixture active profile must exist: {error}") + }, + }; + } + + fn desktop_fixture() -> crate::DesktopApplicationConfig { + return crate::DesktopApplicationConfig { + name: "kb-app-demo-desktop".to_string(), + environment: "test".to_string(), + demo: crate::DesktopDemoConfig { + live_demo_enabled: true, + trading_demo_enabled: false, + }, + }; + } + #[test] fn payload_binding_uses_desktop_path() { let config = ts_rs::Config::default(); let declaration = ::decl(&config); assert!(declaration.contains("DemoConfigPayload")); + assert!(declaration.contains("DemoConfigPublicPayload")); + assert!(declaration.contains("DemoConfigDiagnosticPayload")); + assert!(!declaration.contains("AppConfig")); + assert!(!declaration.contains("ProfileConfig")); + } + + #[test] + fn public_projection_excludes_internal_execution_and_storage_fields() { + let profile = active_fixture_profile(); + let payload = super::public_config_payload(&profile, &desktop_fixture()); + let serialized = match serde_json::to_string(&payload) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("public payload must serialize: {error}"), + }; + assert!(!serialized.contains("mainnet_send_enabled")); + assert!(!serialized.contains("postgres")); + assert!(!serialized.contains("wallet_dir")); + assert!(!serialized.contains("url")); + } + + #[test] + fn public_and_diagnostic_projections_never_serialize_secret_canaries() { + let mut profile = active_fixture_profile(); + profile.database.postgres.url = + "postgres://operator:POSTGRES-SECRET-CANARY@localhost/solana".to_string(); + profile.database.sqlite.path = "/private/SQLITE-PATH-CANARY.sqlite".to_string(); + profile.wallet.wallet_dir = "/private/WALLET-PATH-CANARY".to_string(); + if let std::option::Option::Some(endpoint) = profile.solana.http_endpoints.first_mut() { + endpoint.url = "https://provider.invalid/?api-key=HELIUS-SECRET-CANARY".to_string(); + } + if let std::option::Option::Some(endpoint) = profile.solana.ws_endpoints.first_mut() { + endpoint.url = "wss://provider.invalid/WS-SECRET-CANARY".to_string(); + } + let public = super::public_config_payload(&profile, &desktop_fixture()); + let diagnostics = super::DemoConfigDiagnosticPayload { + sources: std::vec![], + endpoints: profile + .solana + .http_endpoints + .iter() + .map(|endpoint| { + return super::DemoConfigEndpointDiagnosticPayload { + kind: "http".to_string(), + name: endpoint.name.clone(), + enabled: endpoint.enabled, + provider: endpoint.provider.clone(), + cluster: endpoint.cluster.clone(), + url_state: super::configured_state(endpoint.url.as_str()), + connect_timeout_ms: endpoint.connect_timeout_ms, + request_timeout_ms: endpoint.request_timeout_ms, + unsubscribe_timeout_ms: std::option::Option::None, + auto_reconnect: std::option::Option::None, + role_count: super::bounded_len(endpoint.roles.len()), + }; + }) + .collect::>(), + store: super::DemoConfigStoreDiagnosticPayload { + enabled: profile.database.enabled, + backend: profile.database.backend.clone(), + postgres_url_state: super::configured_state(profile.database.postgres.url.as_str()), + postgres_max_connections: profile.database.postgres.max_connections, + postgres_connect_timeout_ms: profile.database.postgres.connect_timeout_ms, + postgres_auto_initialize_schema: profile.database.postgres.auto_initialize_schema, + sqlite_path_state: super::configured_state(profile.database.sqlite.path.as_str()), + sqlite_max_connections: profile.database.sqlite.max_connections, + sqlite_auto_initialize_schema: profile.database.sqlite.auto_initialize_schema, + }, + wallet: super::DemoConfigWalletDiagnosticPayload { + wallet_directory_state: super::configured_state(profile.wallet.wallet_dir.as_str()), + cluster: profile.wallet.cluster.clone(), + temporary_wallet_enabled: profile.wallet.temporary_wallet_enabled, + temporary_wallet_alias: profile.wallet.temporary_wallet_alias.clone(), + temporary_wallet_persist: profile.wallet.temporary_wallet_persist, + }, + execution: super::execution_diagnostic(&profile.execution), + }; + let serialized = match serde_json::to_string(&(public, diagnostics)) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("safe projections must serialize: {error}"), + }; + for secret in [ + "POSTGRES-SECRET-CANARY", + "SQLITE-PATH-CANARY", + "WALLET-PATH-CANARY", + "HELIUS-SECRET-CANARY", + "WS-SECRET-CANARY", + ] { + assert!(!serialized.contains(secret)); + } } } diff --git a/kb-app-demo-desktop/src/demo_http.rs b/kb-app-demo-desktop/src/demo_http.rs index 38620aa..b486420 100644 --- a/kb-app-demo-desktop/src/demo_http.rs +++ b/kb-app-demo-desktop/src/demo_http.rs @@ -1,5 +1,5 @@ // file: kb-app-demo-desktop/src/demo_http.rs -// version: 14 +// version: 16 //! HTTP JSON-RPC demo commands. @@ -53,6 +53,24 @@ pub(crate) struct DemoHttpOptionsPayload { pub(crate) methods: std::vec::Vec, } +/// UI-safe snapshot of one configured HTTP endpoint. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_http/DemoHttpEndpointPayload.ts" +)] +pub(crate) struct DemoHttpEndpointPayload { + /// Logical endpoint name. + pub(crate) endpoint_name: std::string::String, + /// Provider name. + pub(crate) provider: std::string::String, + /// UI-safe role and local-limit snapshots. + pub(crate) roles: std::vec::Vec, + /// Local endpoint status. + pub(crate) status: std::string::String, +} + /// Request payload for one HTTP JSON-RPC demo execution. #[derive(Clone, Debug, serde::Deserialize, TS)] #[serde(rename_all = "camelCase")] @@ -85,8 +103,6 @@ pub(crate) struct DemoHttpExecutionPayload { pub(crate) endpoint_name: std::string::String, /// Selected provider name. pub(crate) provider: std::string::String, - /// Selected endpoint URL. - pub(crate) endpoint_url: std::string::String, /// Required role used by the selection. pub(crate) role: std::string::String, /// JSON-RPC method name. @@ -146,7 +162,6 @@ pub(crate) async fn demo_http_execute_request( return std::result::Result::Ok(crate::DemoHttpExecutionPayload { endpoint_name: selected_client.endpoint_name().to_string(), provider: selected_client.provider().to_string(), - endpoint_url: selected_client.endpoint_url().to_string(), role, request_kind: ks_onchain_transport::request_kind_from_method(&method), method, @@ -157,8 +172,13 @@ pub(crate) async fn demo_http_execute_request( pub(crate) fn demo_http_list_pool_clients( state: tauri::State<'_, crate::AppState>, -) -> std::vec::Vec { - return state.http_pool().snapshot(); +) -> std::vec::Vec { + let snapshots = state.http_pool().snapshot(); + let mut endpoints = std::vec::Vec::new(); + for snapshot in snapshots { + endpoints.push(http_endpoint_payload(snapshot)); + } + return endpoints; } pub(crate) fn demo_http_options( @@ -170,6 +190,21 @@ pub(crate) fn demo_http_options( }; } +fn http_endpoint_payload( + snapshot: ks_onchain_transport::HttpPoolClientSnapshot, +) -> crate::DemoHttpEndpointPayload { + let mut roles = std::vec::Vec::new(); + for role in snapshot.roles { + roles.push(crate::DemoEndpointRolePayload::from_snapshot(role)); + } + return crate::DemoHttpEndpointPayload { + endpoint_name: snapshot.endpoint_name, + provider: snapshot.provider, + roles, + status: snapshot.status, + }; +} + fn build_http_role_options( snapshots: std::vec::Vec, ) -> std::vec::Vec { @@ -412,4 +447,23 @@ mod tests { } assert!(!options.is_empty()); } + + #[test] + fn endpoint_payload_never_serializes_resolved_url() { + let snapshot = ks_onchain_transport::HttpPoolClientSnapshot { + endpoint_name: "helius".to_string(), + provider: "helius".to_string(), + roles: std::vec::Vec::new(), + status: "idle".to_string(), + }; + let payload = super::http_endpoint_payload(snapshot); + let serialized = match serde_json::to_string(&payload) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + panic!("cannot serialize HTTP endpoint payload: {error}") + }, + }; + assert!(!serialized.contains("HTTP-SECRET-CANARY")); + assert!(!serialized.contains("endpointUrl")); + } } diff --git a/kb-app-demo-desktop/src/demo_spl_token_2022.rs b/kb-app-demo-desktop/src/demo_spl_token_2022.rs index 1f864a9..f1a0655 100644 --- a/kb-app-demo-desktop/src/demo_spl_token_2022.rs +++ b/kb-app-demo-desktop/src/demo_spl_token_2022.rs @@ -1,5 +1,5 @@ // file: kb-app-demo-desktop/src/demo_spl_token_2022.rs -// version: 14 +// version: 15 //! Thin Tauri adapter for independent public Token-2022 Devnet validation scenarios. @@ -13,8 +13,6 @@ use ts_rs::TS; // rust-rules: derive-import export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_spl_token_2022/DemoSplToken2022FixturePayload.ts" )] pub(crate) struct DemoSplToken2022FixturePayload { - /// Fixture file used by the application. - pub(crate) fixture_path: std::string::String, /// Token-2022 program ID. pub(crate) program_id: std::string::String, /// Mint account. @@ -231,11 +229,10 @@ pub(crate) fn demo_spl_token_2022_fixture( let fixture_path = wallet_dir.join("spl_token_2022_validation").join("fixture.env"); let contents = match std::fs::read_to_string(&fixture_path) { std::result::Result::Ok(value) => value, - std::result::Result::Err(error) => { - return std::result::Result::Err(format!( - "unable to read Token-2022 fixture {}: {error}", - fixture_path.display() - )); + std::result::Result::Err(_) => { + return std::result::Result::Err( + "unable to read configured Token-2022 fixture".to_string(), + ); }, }; let values = parse_fixture(contents.as_str()); @@ -292,7 +289,6 @@ pub(crate) fn demo_spl_token_2022_fixture( }, }; return std::result::Result::Ok(crate::DemoSplToken2022FixturePayload { - fixture_path: fixture_path.display().to_string(), program_id, mint, source, @@ -581,6 +577,31 @@ mod tests { }; } + #[test] + fn fixture_payload_never_serializes_wallet_path() { + let payload = crate::DemoSplToken2022FixturePayload { + program_id: "program".to_string(), + mint: "mint".to_string(), + source: "source".to_string(), + destination: "destination".to_string(), + close_account: "close".to_string(), + delegate: "delegate".to_string(), + authority: "authority".to_string(), + freeze_authority: "freeze".to_string(), + decimals: 9, + mint_amount_raw: "1".to_string(), + transfer_amount_raw: "1".to_string(), + approve_amount_raw: "1".to_string(), + burn_amount_raw: "1".to_string(), + }; + let serialized = serde_json::to_string(&payload); + assert!(serialized.is_ok()); + if let std::result::Result::Ok(json) = serialized { + assert!(!json.contains("fixturePath")); + assert!(!json.contains("WALLET-PATH-CANARY")); + } + } + #[test] fn every_public_scenario_builds_one_typed_operation() { for scenario in [ diff --git a/kb-app-demo-desktop/src/demo_transport.rs b/kb-app-demo-desktop/src/demo_transport.rs new file mode 100644 index 0000000..892f94d --- /dev/null +++ b/kb-app-demo-desktop/src/demo_transport.rs @@ -0,0 +1,52 @@ +// file: kb-app-demo-desktop/src/demo_transport.rs +// version: 1 + +//! UI-safe transport DTOs shared by HTTP and WebSocket demo surfaces. + +use ts_rs::TS; // rust-rules: derive-import + +/// UI-safe endpoint role and local routing limits. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_transport/DemoEndpointRolePayload.ts" +)] +pub(crate) struct DemoEndpointRolePayload { + /// Role code used by endpoint pools. + pub(crate) role: std::string::String, + /// Whether the role is enabled. + pub(crate) enabled: bool, + /// Request or subscription kinds handled by this role. + pub(crate) request_kinds: std::vec::Vec, + /// Role priority where lower values are preferred. + pub(crate) priority: u32, + /// Requests per second allowed for this role. + pub(crate) requests_per_second: u32, + /// Burst capacity allowed for this role. + pub(crate) burst_capacity: u32, + /// Maximum concurrent requests allowed for this role. + pub(crate) max_concurrent_requests: u32, + /// Maximum subscriptions allowed for this role. + pub(crate) max_subscriptions: u32, + /// Pause after a rate limit response in milliseconds. + #[ts(type = "number")] + pub(crate) pause_after_rate_limit_ms: u64, +} + +impl crate::DemoEndpointRolePayload { + /// Converts one backend transport role snapshot into the UI-safe application contract. + pub(crate) fn from_snapshot(snapshot: ks_onchain_transport::EndpointRoleSnapshot) -> Self { + return Self { + role: snapshot.role, + enabled: snapshot.enabled, + request_kinds: snapshot.request_kinds, + priority: snapshot.priority, + requests_per_second: snapshot.requests_per_second, + burst_capacity: snapshot.burst_capacity, + max_concurrent_requests: snapshot.max_concurrent_requests, + max_subscriptions: snapshot.max_subscriptions, + pause_after_rate_limit_ms: snapshot.pause_after_rate_limit_ms, + }; + } +} diff --git a/kb-app-demo-desktop/src/demo_ws.rs b/kb-app-demo-desktop/src/demo_ws.rs index dfa95b4..3119a6b 100644 --- a/kb-app-demo-desktop/src/demo_ws.rs +++ b/kb-app-demo-desktop/src/demo_ws.rs @@ -1,5 +1,5 @@ // file: kb-app-demo-desktop/src/demo_ws.rs -// version: 20 +// version: 23 //! Standard Solana WebSocket demo commands backed by `ks_onchain_transport::WsSession`. @@ -59,6 +59,24 @@ pub(crate) struct DemoWsOptionsPayload { pub(crate) methods: std::vec::Vec, } +/// UI-safe snapshot of one configured WebSocket endpoint. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_ws/DemoWsEndpointPayload.ts" +)] +pub(crate) struct DemoWsEndpointPayload { + /// Logical endpoint name. + pub(crate) endpoint_name: std::string::String, + /// Provider name. + pub(crate) provider: std::string::String, + /// UI-safe role and local-limit snapshots. + pub(crate) roles: std::vec::Vec, + /// Local endpoint status. + pub(crate) status: std::string::String, +} + /// Request payload for one WebSocket subscription demo command. #[derive(Clone, Debug, serde::Deserialize, TS)] #[serde(rename_all = "camelCase")] @@ -91,8 +109,6 @@ pub(crate) struct DemoWsExecutionPayload { pub(crate) endpoint_name: std::string::String, /// Selected provider name. pub(crate) provider: std::string::String, - /// Selected endpoint URL. - pub(crate) endpoint_url: std::string::String, /// Required role used by the selection. pub(crate) role: std::string::String, /// JSON-RPC method name. @@ -120,8 +136,6 @@ pub(crate) struct DemoWsStatusPayload { pub(crate) connected: bool, /// Selected endpoint name. pub(crate) endpoint_name: std::option::Option, - /// Selected endpoint URL. - pub(crate) endpoint_url: std::option::Option, /// Last subscription method. pub(crate) method: std::option::Option, /// Last unsubscribe method. @@ -230,7 +244,6 @@ pub(crate) async fn demo_ws_connect( return std::result::Result::Ok(crate::DemoWsExecutionPayload { endpoint_name: selected_client.endpoint_name().to_string(), provider: selected_client.provider().to_string(), - endpoint_url: selected_client.endpoint_url().to_string(), role, method: method.clone(), request_kind: ks_onchain_transport::request_kind_from_method(&method), @@ -259,15 +272,17 @@ pub(crate) async fn demo_ws_status( /// Lists WebSocket endpoints available through the configured pool. pub(crate) fn demo_ws_list_pool_clients( state: tauri::State<'_, crate::AppState>, -) -> std::result::Result< - std::vec::Vec, - std::string::String, -> { +) -> std::result::Result, std::string::String> { let pool = match state.demo_ws_pool() { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()), }; - return std::result::Result::Ok(pool.snapshot()); + let snapshots = pool.snapshot(); + let mut endpoints = std::vec::Vec::new(); + for snapshot in snapshots { + endpoints.push(ws_endpoint_payload(snapshot)); + } + return std::result::Result::Ok(endpoints); } /// Lists selectable WebSocket roles and methods for the demo UI. @@ -393,7 +408,7 @@ async fn ensure_session( if let std::option::Option::Some(session) = existing { let snapshot = session.snapshot().await; if snapshot.state != ks_onchain_transport::WsSessionState::Disconnected { - if snapshot.endpoint_url != selected_client.endpoint_url() { + if snapshot.endpoint_name.as_str() != selected_client.endpoint_name() { return std::result::Result::Err(format!( "demo WebSocket session already uses endpoint '{}'; disconnect before selecting '{}'", snapshot.endpoint_name, @@ -569,7 +584,6 @@ fn status_from_snapshot( return crate::DemoWsStatusPayload { connected: snapshot.state != ks_onchain_transport::WsSessionState::Disconnected, endpoint_name: std::option::Option::Some(snapshot.endpoint_name), - endpoint_url: std::option::Option::Some(snapshot.endpoint_url), method, unsubscribe_method, subscription_id, @@ -585,7 +599,6 @@ fn disconnected_status() -> crate::DemoWsStatusPayload { return crate::DemoWsStatusPayload { connected: false, endpoint_name: std::option::Option::None, - endpoint_url: std::option::Option::None, method: std::option::Option::None, unsubscribe_method: std::option::Option::None, subscription_id: std::option::Option::None, @@ -594,6 +607,21 @@ fn disconnected_status() -> crate::DemoWsStatusPayload { }; } +fn ws_endpoint_payload( + snapshot: ks_onchain_transport::WsPoolClientSnapshot, +) -> crate::DemoWsEndpointPayload { + let mut roles = std::vec::Vec::new(); + for role in snapshot.roles { + roles.push(crate::DemoEndpointRolePayload::from_snapshot(role)); + } + return crate::DemoWsEndpointPayload { + endpoint_name: snapshot.endpoint_name, + provider: snapshot.provider, + roles, + status: snapshot.status, + }; +} + fn build_ws_role_options( snapshots: std::vec::Vec, ) -> std::vec::Vec { @@ -957,10 +985,46 @@ mod tests { ); } + #[test] + fn endpoint_and_status_payloads_never_serialize_resolved_url() { + let endpoint_snapshot = ks_onchain_transport::WsPoolClientSnapshot { + endpoint_name: "helius".to_string(), + provider: "helius".to_string(), + roles: std::vec::Vec::new(), + status: "idle".to_string(), + }; + let endpoint_payload = super::ws_endpoint_payload(endpoint_snapshot); + let endpoint_json = match serde_json::to_string(&endpoint_payload) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + panic!("cannot serialize WebSocket endpoint payload: {error}") + }, + }; + assert!(!endpoint_json.contains("WS-SECRET-CANARY")); + assert!(!endpoint_json.contains("endpointUrl")); + let status = super::status_from_snapshot(ks_onchain_transport::WsSessionSnapshot { + endpoint_name: "helius".to_string(), + provider: "helius".to_string(), + state: ks_onchain_transport::WsSessionState::Connected, + reconnect_count: 0, + capabilities: ks_onchain_transport::StandardWsCapabilities::default(), + subscriptions: std::vec::Vec::new(), + }); + let status_json = match serde_json::to_string(&status) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + panic!("cannot serialize WebSocket status payload: {error}") + }, + }; + assert!(!status_json.contains("WS-SECRET-CANARY")); + assert!(!status_json.contains("endpointUrl")); + } + #[test] fn tauri_numeric_bindings_use_json_compatible_numbers() { let config = ts_rs::Config::default(); let declarations = [ + ::decl(&config), ::decl(&config), ::decl(&config), ::decl(&config), diff --git a/kb-app-demo-desktop/src/desktop_config.rs b/kb-app-demo-desktop/src/desktop_config.rs new file mode 100644 index 0000000..185f84c --- /dev/null +++ b/kb-app-demo-desktop/src/desktop_config.rs @@ -0,0 +1,96 @@ +// file: kb-app-demo-desktop/src/desktop_config.rs +// version: 2 + +//! Desktop-owned application settings embedded in one generic binary composition profile. + +const DESKTOP_APPLICATION_SCHEMA: &str = + include_str!("../../config/schemas/kb-app-demo-desktop.application.config.schema.json"); + +/// Desktop-owned settings for one selected composition profile. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub(crate) struct DesktopApplicationConfig { + /// Application display/name identity. + pub(crate) name: std::string::String, + /// Human-readable environment classification. + pub(crate) environment: std::string::String, + /// Desktop demo feature flags. + pub(crate) demo: crate::DesktopDemoConfig, +} + +/// Desktop-owned demo feature flags. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub(crate) struct DesktopDemoConfig { + /// Enables live demo pages. + pub(crate) live_demo_enabled: bool, + /// Enables trading demo pages. + pub(crate) trading_demo_enabled: bool, +} + +/// Parses and validates desktop-owned settings from one generic composition profile. +pub(crate) fn desktop_application_config( + profile: &ks_config::CompositionProfileConfig, +) -> ks_core::Result { + let application = match profile.application.as_ref() { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(ks_core::Error::new( + "desktop_application_config_missing", + profile.name.clone(), + )); + }, + }; + match ks_config::validate_json_value_against_schema( + DESKTOP_APPLICATION_SCHEMA, + application, + "desktop_application_config_schema_validation_failed", + ) { + std::result::Result::Ok(()) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + return match serde_json::from_value::(application.clone()) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(_) => std::result::Result::Err(ks_core::Error::new( + "desktop_application_config_decode_failed", + "desktop application configuration could not be decoded", + )), + }; +} + +#[cfg(test)] +mod tests { + #[test] + fn default_composition_contains_valid_desktop_application_fragments() { + let raw = include_str!("../../config/kb-app-demo-desktop.default.config.json"); + let composition = match ks_config::parse_composition_json(raw) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("default composition must parse: {error}"), + }; + for profile in &composition.profiles { + let result = super::desktop_application_config(profile); + assert!(result.is_ok()); + } + } + + #[test] + fn application_fragment_rejects_unknown_or_missing_fields() { + let profile = ks_config::CompositionProfileConfig { + name: "test".to_string(), + application: std::option::Option::Some(serde_json::json!({ + "name": "kb-app-demo-desktop", + "environment": "test", + "demo": { + "live_demo_enabled": true, + "trading_demo_enabled": false + }, + "secret": "must-not-be-accepted" + })), + logging_profile: std::option::Option::None, + transport_profile: std::option::Option::None, + listeners_profile: std::option::Option::None, + store_profile: std::option::Option::None, + wallet_profile: std::option::Option::None, + execution_profile: std::option::Option::None, + }; + assert!(super::desktop_application_config(&profile).is_err()); + } +} diff --git a/kb-app-demo-desktop/src/lib.rs b/kb-app-demo-desktop/src/lib.rs index 904284a..4466063 100644 --- a/kb-app-demo-desktop/src/lib.rs +++ b/kb-app-demo-desktop/src/lib.rs @@ -1,5 +1,5 @@ // file: kb-app-demo-desktop/src/lib.rs -// version: 34 +// version: 37 //! Tauri desktop demo application for `khadhroony-bot3`. @@ -29,7 +29,9 @@ mod demo_sql_diag; mod demo_sql_pg_core; mod demo_sql_pg_raw; mod demo_sql_replay_candidates; +mod demo_transport; mod demo_ws; +mod desktop_config; mod frontend_log; mod main_window; mod splash; @@ -64,8 +66,28 @@ pub(crate) use self::demo_backfill::demo_backfill_cancel; pub(crate) use self::demo_backfill::demo_backfill_execute; /// Returns endpoint roles, known programs and conservative backfill defaults. pub(crate) use self::demo_backfill::demo_backfill_options; +/// Bounded internal configuration diagnostics shown by the configuration window. +pub(crate) use self::demo_config::DemoConfigDiagnosticPayload; +/// Bounded endpoint diagnostics shown by the configuration window. +pub(crate) use self::demo_config::DemoConfigEndpointDiagnosticPayload; +/// Bounded execution diagnostics shown by the configuration window. +pub(crate) use self::demo_config::DemoConfigExecutionDiagnosticPayload; /// Configuration payload shown by the configuration window. pub(crate) use self::demo_config::DemoConfigPayload; +/// Public desktop demo feature flags. +pub(crate) use self::demo_config::DemoConfigPublicDemoPayload; +/// Public endpoint identity shown by the configuration window. +pub(crate) use self::demo_config::DemoConfigPublicEndpointPayload; +/// Public listener summary shown by the configuration window. +pub(crate) use self::demo_config::DemoConfigPublicListenersPayload; +/// Explicit public configuration projection shown by the configuration window. +pub(crate) use self::demo_config::DemoConfigPublicPayload; +/// Bounded source-path diagnostics shown by the configuration window. +pub(crate) use self::demo_config::DemoConfigSourceDiagnosticPayload; +/// Bounded store diagnostics shown by the configuration window. +pub(crate) use self::demo_config::DemoConfigStoreDiagnosticPayload; +/// Bounded wallet diagnostics shown by the configuration window. +pub(crate) use self::demo_config::DemoConfigWalletDiagnosticPayload; /// Builds the configuration payload from shared application state. pub(crate) use self::demo_config::demo_config_payload; /// Internal demo core extraction item. @@ -228,6 +250,8 @@ pub(crate) use self::demo_execution_solana_core::select_devnet_profile; pub(crate) use self::demo_execution_spl::DevnetSplValidationScenarioPayload; /// Returns the complete ordered Devnet SPL validation inventory for milestone 0.4.6. pub(crate) use self::demo_execution_spl::demo_execution_spl_validation_scenarios; +/// UI-safe HTTP endpoint snapshot. +pub(crate) use self::demo_http::DemoHttpEndpointPayload; /// HTTP demo response payload. pub(crate) use self::demo_http::DemoHttpExecutionPayload; /// One selectable HTTP JSON-RPC method. @@ -336,6 +360,10 @@ pub(crate) use self::demo_sql_replay_candidates::load_demo_sql_replay_entities; pub(crate) use self::demo_sql_replay_candidates::load_demo_sql_replay_programs; /// Loads bounded transaction replay candidates from PostgreSQL. pub(crate) use self::demo_sql_replay_candidates::load_demo_sql_replay_transactions; +/// UI-safe endpoint role shared by HTTP and WebSocket diagnostics. +pub(crate) use self::demo_transport::DemoEndpointRolePayload; +/// UI-safe WebSocket endpoint snapshot. +pub(crate) use self::demo_ws::DemoWsEndpointPayload; /// WebSocket execution response payload. pub(crate) use self::demo_ws::DemoWsExecutionPayload; /// WebSocket message emitted to the frontend. @@ -368,6 +396,12 @@ pub(crate) use self::demo_ws::demo_ws_status; pub(crate) use self::demo_ws::demo_ws_unsubscribe; /// Disconnects the persistent WebSocket session during application shutdown. pub(crate) use self::demo_ws::disconnect_demo_ws_app_state; +/// Desktop-owned application settings selected from the active composition profile. +pub(crate) use self::desktop_config::DesktopApplicationConfig; +/// Desktop-owned demo feature flags. +pub(crate) use self::desktop_config::DesktopDemoConfig; +/// Parses desktop-owned settings from one composition profile. +pub(crate) use self::desktop_config::desktop_application_config; /// Frontend logging payload. pub(crate) use self::frontend_log::FrontendLogPayload; /// Emits one normalized frontend log event. diff --git a/kb-app-demo-desktop/src/tauri.rs b/kb-app-demo-desktop/src/tauri.rs index 557707c..16bec0a 100644 --- a/kb-app-demo-desktop/src/tauri.rs +++ b/kb-app-demo-desktop/src/tauri.rs @@ -1,5 +1,5 @@ // file: kb-app-demo-desktop/src/tauri.rs -// version: 37 +// version: 38 //! Tauri runtime assembly and private command wrappers. @@ -257,7 +257,7 @@ fn open_demo_http_window( #[tauri::command] fn demo_http_list_pool_clients( state: tauri::State<'_, crate::AppState>, -) -> std::vec::Vec { +) -> std::vec::Vec { return crate::demo_http_list_pool_clients(state); } @@ -284,10 +284,7 @@ fn open_demo_ws_window( #[tauri::command] fn demo_ws_list_pool_clients( state: tauri::State<'_, crate::AppState>, -) -> std::result::Result< - std::vec::Vec, - std::string::String, -> { +) -> std::result::Result, std::string::String> { return crate::demo_ws_list_pool_clients(state); } diff --git a/ks-config/CHANGELOG.md b/ks-config/CHANGELOG.md index c7b930a..1c231a7 100644 --- a/ks-config/CHANGELOG.md +++ b/ks-config/CHANGELOG.md @@ -1,8 +1,19 @@ - + # CHANGELOG — ks-config +## `0.5.1-pre.008` + +- Fix: use `Result::is_err()` for execution and wallet schema validation checks, removing the Clippy redundant-pattern warnings without changing fail-closed behavior. + +- retire `ts-rs` de `ks-config`, supprime ses bindings TypeScript générés et réserve les projections frontend aux DTO des applications propriétaires ; +- rend les contrats source/runtime susceptibles de contenir des valeurs résolues non sérialisables et non `Debug` par défaut, et retire les sérialiseurs publics de `AppConfig` ; +- ajoute la classification `Secret > Internal > Public`, y compris pour les chaînes composées contenant plusieurs placeholders d'environnement, ainsi qu'une représentation diagnostic générique qui ne révèle jamais les valeurs secret/internal ; +- rend la section `application` des compositions opaque pour `ks-config` et fournit un helper générique de validation JSON Schema pour les fragments possédés par les binaires ; +- durcit les erreurs de validation/décodage afin de ne plus recopier les valeurs rejetées, notamment pour les URLs et fragments contenant potentiellement des secrets ; +- ajoute un test d'API externe et des canaris de non-divulgation pour les helpers de sensibilité. + ## `0.5.1-pre.007` - extrait store, wallet et execution vers `config/store.config.json`, `config/wallet.config.json` et `config/execution.config.json`, chacun avec schéma, exemple et `default_profile` ; diff --git a/ks-config/Cargo.toml b/ks-config/Cargo.toml index 59b8e28..9381d4c 100644 --- a/ks-config/Cargo.toml +++ b/ks-config/Cargo.toml @@ -1,5 +1,5 @@ # file: ks-config/Cargo.toml -# version: 4 +# version: 5 [package] name = "ks-config" @@ -14,7 +14,6 @@ ks-core = { path = "../ks-core" } jsonschema.workspace = true serde.workspace = true serde_json.workspace = true -ts-rs.workspace = true [lints] workspace = true diff --git a/ks-config/README.md b/ks-config/README.md index 9a1f59f..212c8fd 100644 --- a/ks-config/README.md +++ b/ks-config/README.md @@ -1,5 +1,5 @@ - + # ks-config @@ -46,7 +46,9 @@ Les valeurs globales telles que `logs_directory` et `wallets_directory` ne sont - `read_wallet_json_file_with_environment`, `wallet_profile`, `default_wallet_profile` ; - `read_execution_json_file_with_environment`, `execution_profile`, `default_execution_profile` ; - `load_workspace_environment`, `resolve_environment_placeholders` ; -- `AppConfig`, `ProfileConfig` pendant la phase de compatibilité. +- `classify_environment_variable`, `classify_environment_template`, `diagnostic_environment_value` ; +- `validate_json_value_against_schema` pour les fragments possédés par les binaires ; +- `AppConfig`, `ProfileConfig` pendant la phase de compatibilité backend-only. ## Frontières @@ -54,7 +56,9 @@ Les valeurs globales telles que `logs_directory` et `wallets_directory` ne sont `ks-onchain-transport` peut consommer directement `TransportProfileConfig`. -Les dérivations TS-RS des types de `ks-config` sont encore présentes pendant `pre.007`, mais elles ne sont pas considérées comme une frontière durable. L'audit montre que le frontend desktop n'importe pas directement ces bindings ; leur suppression ou réduction est prévue avec les DTO publics sûrs de la prochaine prerelease. +Depuis `0.5.1-pre.008`, `ks-config` ne dépend plus de TS-RS et ne produit plus de bindings TypeScript. Ses contrats source/runtime peuvent contenir des secrets résolus : ils ne dérivent donc ni `serde::Serialize` ni `Debug`. Les applications propriétaires construisent leurs DTO publics et diagnostics bornés champ par champ. + +La section `application` d'une composition est volontairement opaque pour `ks-config`. Chaque binaire propriétaire définit son schéma et le valide avec `validate_json_value_against_schema`; le desktop utilise `config/schemas/kb-app-demo-desktop.application.config.schema.json`. ## Documents diff --git a/ks-config/TODO.md b/ks-config/TODO.md index b188399..221a6d9 100644 --- a/ks-config/TODO.md +++ b/ks-config/TODO.md @@ -1,16 +1,9 @@ - + # TODO — ks-config ## Série `0.5.x` -- [ ] `0.5.1` - appliquer à l’exécution les classes `KS_SECRET_*` / `KB_SECRET_*`, `KS_PUBLIC_*` / `KB_PUBLIC_*` et internes, avec propagation de sensibilité aux valeurs composées. -- [ ] `0.5.1` - séparer les représentations source, runtime résolue, publique et diagnostic. -- [ ] `0.5.1` - interdire qu’un secret résolu soit sérialisé, loggé, inclus dans une erreur ou transmis via Tauri. -- [ ] `0.5.1` - supprimer l’exposition frontend de `AppConfig` et `ProfileConfig` résolus complets. -- [ ] `0.5.1` - retirer de `ks-config` et réduire dans `ks-lib` les bindings TS-RS qui ne constituent pas un contrat générique indépendant de Tauri ; définir les wrappers publics nécessaires dans `kb-app-demo-desktop`. +- [ ] `0.5.1-pre.009` - réconcilier une dernière fois schémas, exemples, guide de configuration et contrat d'API externe avant archivage du plan. - [ ] Contrat - maintenir l’identité entre chaque schéma embarqué et son fichier sous `config/schemas/`. -- [ ] Tests - ajouter les tests d’API externe et les canaris de non-divulgation. -- [ ] Documentation - maintenir guides, exemples et `.env.example` avec l’implémentation correspondante. -- [ ] Intégration - coordonner les surfaces publiques sûres avec logging, transports, pipeline, scénarios et applications. diff --git a/ks-config/USAGE.md b/ks-config/USAGE.md index 99d8f26..a91d993 100644 --- a/ks-config/USAGE.md +++ b/ks-config/USAGE.md @@ -1,5 +1,5 @@ - + # Utilisation de ks-config @@ -108,6 +108,14 @@ KS_WALLETS_DIRECTORY Le schéma de ce contrat est `config/schemas/resolved.app.config.schema.json`. Les fixtures correspondantes sont sous `test-fixtures/config/`. +## Frontière source/runtime/public/diagnostic + +`AppConfig`, `ProfileConfig` et les documents spécialisés chargés avec résolution d'environnement sont des contrats backend-only. Ils peuvent contenir des URLs, DSN, chemins ou autres valeurs sensibles et ne dérivent ni `serde::Serialize` ni `Debug`. `ks-config` n'offre plus de sérialiseur du runtime résolu. + +La sensibilité des variables suit `Secret > Internal > Public`. `classify_environment_template` applique cette règle aux chaînes composées : une URL contenant `${KS_SECRET_HELIUS_API_KEY}` est secrète même si le champ s'appelle simplement `url`. `diagnostic_environment_value` ne retourne jamais une valeur secrète ou interne en clair. + +Les fragments propres à un binaire restent opaques dans `CompositionProfileConfig.application`. Le propriétaire les valide avec `validate_json_value_against_schema` et son schéma dédié. + ## Frontière TypeScript -Les bindings TS-RS générés depuis `ks-config` ne sont pas importés directement par le frontend desktop actuel. Ils sont donc considérés comme transitoires jusqu'à la prochaine prerelease, qui introduira les DTO publics/diagnostiques et supprimera les exports TypeScript non justifiés des crates généralistes. +`ks-config` ne dépend plus de TS-RS et ne génère aucun binding TypeScript. Les types traversant Tauri sont définis comme DTO/wrappers dans l'application propriétaire ; `kb-app-demo-desktop` construit notamment ses projections de configuration publique et diagnostic sans sérialiser `AppConfig/ProfileConfig`. diff --git a/ks-config/src/composition.rs b/ks-config/src/composition.rs index 4a6480e..ca6e2c7 100644 --- a/ks-config/src/composition.rs +++ b/ks-config/src/composition.rs @@ -1,5 +1,5 @@ // file: ks-config/src/composition.rs -// version: 2 +// version: 3 //! Binary composition configuration and resolution into the shared runtime profile contract. @@ -13,7 +13,7 @@ const DEFAULT_WALLET_CONFIG_PATH: &str = "config/wallet.config.json"; const DEFAULT_EXECUTION_CONFIG_PATH: &str = "config/execution.config.json"; /// Root composition document used by a binary to select shared configuration profiles. -#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize)] +#[derive(Clone, serde::Deserialize, Eq, PartialEq)] pub struct CompositionConfigDocument { /// Active binary composition profile name. pub active_profile: std::string::String, @@ -24,7 +24,7 @@ pub struct CompositionConfigDocument { } /// Paths of shared configuration documents consumed by one binary composition. -#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize)] +#[derive(Clone, serde::Deserialize, Eq, PartialEq)] pub struct CompositionConfigSources { /// Logging configuration document path relative to the workspace root unless absolute. pub logging: std::string::String, @@ -41,12 +41,12 @@ pub struct CompositionConfigSources { } /// Named composition profile with optional overrides of each shared document default. -#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize)] +#[derive(Clone, serde::Deserialize, Eq, PartialEq)] pub struct CompositionProfileConfig { /// Composition profile code. pub name: std::string::String, - /// Application metadata retained until the application-specific split is completed. - pub app: crate::AppSectionConfig, + /// Optional binary-owned application settings opaque to the shared composition layer. + pub application: std::option::Option, /// Optional logging profile override. pub logging_profile: std::option::Option, /// Optional transport profile override. @@ -59,12 +59,10 @@ pub struct CompositionProfileConfig { pub wallet_profile: std::option::Option, /// Optional execution profile override. pub execution_profile: std::option::Option, - /// Desktop demo flags retained until the application-specific split is completed. - pub demo: crate::DemoConfig, } /// Resolved application configuration and the source documents needed by a binary. -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Eq, PartialEq)] pub struct ComposedAppConfig { /// Parsed binary composition document. pub composition: CompositionConfigDocument, @@ -127,9 +125,9 @@ pub fn validate_composition_json_schema(raw_json: &str) -> ks_core::Result<()> { }; return match validator.validate(&instance) { std::result::Result::Ok(()) => std::result::Result::Ok(()), - std::result::Result::Err(error) => std::result::Result::Err(ks_core::Error::new( + std::result::Result::Err(_) => std::result::Result::Err(ks_core::Error::new( "composition_config_schema_validation_failed", - error.to_string(), + "composition configuration does not satisfy its schema", )), }; } @@ -142,10 +140,10 @@ pub fn parse_composition_json(raw_json: &str) -> ks_core::Result(raw_json) { std::result::Result::Ok(value) => value, - std::result::Result::Err(error) => { + std::result::Result::Err(_) => { return std::result::Result::Err(ks_core::Error::new( "composition_config_json_decode_failed", - error.to_string(), + "composition configuration could not be decoded", )); }, }; @@ -291,7 +289,6 @@ pub fn compose_app_config( }; profiles.push(crate::ProfileConfig { name: profile.name.clone(), - app: profile.app.clone(), database: store_profile.database.clone(), solana: crate::SolanaConfig { http_endpoints: transport_profile.http_endpoints, @@ -300,7 +297,6 @@ pub fn compose_app_config( }, wallet: wallet_profile, execution: execution_profile.config.clone(), - demo: profile.demo.clone(), }); } let config = crate::AppConfig { @@ -437,20 +433,13 @@ pub fn read_default_shared_app_config_with_environment( }, profiles: vec![CompositionProfileConfig { name: profile_name.clone(), - app: crate::AppSectionConfig { - name: "khadhroony-solana".to_string(), - environment: profile_name, - }, + application: std::option::Option::None, logging_profile: std::option::Option::None, transport_profile: std::option::Option::None, listeners_profile: std::option::Option::None, store_profile: std::option::Option::None, wallet_profile: std::option::Option::None, execution_profile: std::option::Option::None, - demo: crate::DemoConfig { - live_demo_enabled: false, - trading_demo_enabled: false, - }, }], }; return compose_app_config(&composition, &transport, &listeners, &store, &wallet, &execution); @@ -593,7 +582,7 @@ mod tests { const DEFAULT_COMPOSITION: &str = include_str!("../../config/kb-app-demo-desktop.default.config.json"); const EXAMPLE_COMPOSITION: &str = - include_str!("../../config/example.kb-app-demo-desktop.default.config.json"); + include_str!("../../config/exemples/example.kb-app-demo-desktop.default.config.json"); #[test] fn default_and_example_compositions_validate() { @@ -613,6 +602,7 @@ mod tests { panic!("local Devnet composition must resolve: {error}") }, }; + assert!(profile.application.is_some()); assert!(profile.logging_profile.is_none()); assert!(profile.transport_profile.is_none()); assert!(profile.store_profile.is_none()); diff --git a/ks-config/src/environment.rs b/ks-config/src/environment.rs index 9ca0bdc..a88e315 100644 --- a/ks-config/src/environment.rs +++ b/ks-config/src/environment.rs @@ -1,5 +1,5 @@ // file: ks-config/src/environment.rs -// version: 5 +// version: 6 //! Environment-file loading and configuration placeholder resolution. @@ -32,9 +32,9 @@ pub fn load_workspace_environment( std::result::Result::Ok(()) => std::result::Result::Ok(EnvironmentLoadReport { loaded_path: std::option::Option::Some(selected_path), }), - std::result::Result::Err(error) => std::result::Result::Err(ks_core::Error::new( + std::result::Result::Err(_) => std::result::Result::Err(ks_core::Error::new( "config_env_file_load_failed", - format!("{}: {error}", selected_path.display()), + format!("failed to load environment file {}", selected_path.display()), )), }; } diff --git a/ks-config/src/execution.rs b/ks-config/src/execution.rs index d958462..28e3c77 100644 --- a/ks-config/src/execution.rs +++ b/ks-config/src/execution.rs @@ -1,5 +1,5 @@ // file: ks-config/src/execution.rs -// version: 1 +// version: 3 //! Independent execution policy configuration document loading and validation. @@ -7,7 +7,7 @@ const EXECUTION_JSON_SCHEMA: &str = include_str!("../../config/schemas/execution.config.schema.json"); /// Root execution policy document with one default profile and named alternatives. -#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize)] +#[derive(Clone, serde::Deserialize, Eq, PartialEq)] pub struct ExecutionConfigDocument { /// Default execution profile used when a composition does not override it. pub default_profile: std::string::String, @@ -16,7 +16,7 @@ pub struct ExecutionConfigDocument { } /// Named execution policy profile. -#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize)] +#[derive(Clone, serde::Deserialize, Eq, PartialEq)] pub struct ExecutionProfileConfig { /// Profile code. pub name: std::string::String, @@ -59,18 +59,18 @@ pub fn parse_execution_json(raw_json: &str) -> ks_core::Result(raw_json) { std::result::Result::Ok(value) => value, - std::result::Result::Err(error) => { + std::result::Result::Err(_) => { return std::result::Result::Err(ks_core::Error::new( "execution_config_json_decode_failed", - error.to_string(), + "execution configuration could not be decoded", )); }, }; @@ -162,7 +162,8 @@ pub fn validate_execution_document(document: &ExecutionConfigDocument) -> ks_cor #[cfg(test)] mod tests { const DEFAULT_EXECUTION: &str = include_str!("../../config/execution.config.json"); - const EXAMPLE_EXECUTION: &str = include_str!("../../config/example.execution.config.json"); + const EXAMPLE_EXECUTION: &str = + include_str!("../../config/exemples/example.execution.config.json"); #[test] fn default_and_example_execution_configs_validate() { diff --git a/ks-config/src/lib.rs b/ks-config/src/lib.rs index e1558cc..7d8b403 100644 --- a/ks-config/src/lib.rs +++ b/ks-config/src/lib.rs @@ -1,5 +1,5 @@ // file: ks-config/src/lib.rs -// version: 8 +// version: 9 //! Khadhroony Solana shared configuration contracts and composition helpers. #![warn(missing_docs)] @@ -10,6 +10,8 @@ mod composition; mod environment; mod execution; mod listeners; +mod schema; +mod sensitivity; mod settings; mod store; mod transport; @@ -97,16 +99,22 @@ pub use self::listeners::resolved_listener_config; pub use self::listeners::validate_listeners_document; /// Exposes listener JSON Schema validation. pub use self::listeners::validate_listeners_json_schema; +/// Exposes generic caller-owned JSON Schema validation. +pub use self::schema::validate_json_value_against_schema; +/// Exposes Khadhroony environment-value sensitivity classification. +pub use self::sensitivity::EnvironmentValueSensitivity; +/// Exposes composed-value environment-placeholder sensitivity classification. +pub use self::sensitivity::classify_environment_template; +/// Exposes Khadhroony environment-variable namespace classification. +pub use self::sensitivity::classify_environment_variable; +/// Exposes the bounded generic diagnostic representation for an environment value. +pub use self::sensitivity::diagnostic_environment_value; /// Exposes the account listener configuration type. pub use self::settings::AccountListenerConfig; /// Exposes the root resolved runtime configuration type. pub use self::settings::AppConfig; -/// Exposes the application metadata configuration type. -pub use self::settings::AppSectionConfig; /// Exposes the database configuration type. pub use self::settings::DatabaseConfig; -/// Exposes the temporary demo application configuration type. -pub use self::settings::DemoConfig; /// Exposes the endpoint role configuration type. pub use self::settings::EndpointRoleConfig; /// Exposes the execution configuration type. @@ -143,10 +151,6 @@ pub use self::settings::parse_config_json; pub use self::settings::read_config_json_file; /// Exposes resolved runtime snapshot loading with workspace environment resolution. pub use self::settings::read_config_json_file_with_environment; -/// Exposes the compact JSON serializer for resolved runtime configuration values. -pub use self::settings::serialize_config_json; -/// Exposes the pretty JSON serializer for resolved runtime configuration values. -pub use self::settings::serialize_config_json_pretty; /// Exposes the typed resolved runtime configuration validator. pub use self::settings::validate_config; /// Exposes the JSON Schema validator for raw resolved runtime configuration JSON. diff --git a/ks-config/src/listeners.rs b/ks-config/src/listeners.rs index e56a977..78445a9 100644 --- a/ks-config/src/listeners.rs +++ b/ks-config/src/listeners.rs @@ -1,5 +1,5 @@ // file: ks-config/src/listeners.rs -// version: 2 +// version: 3 //! Independent Solana listener configuration document loading and validation. @@ -7,7 +7,7 @@ const LISTENERS_JSON_SCHEMA: &str = include_str!("../../config/schemas/listeners.config.schema.json"); /// Root listener document containing independently selectable named profiles. -#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize)] +#[derive(Clone, serde::Deserialize, Eq, PartialEq)] pub struct ListenersConfigDocument { /// Default listener profile used by consumers that do not provide an explicit selection. pub default_profile: std::string::String, @@ -16,7 +16,7 @@ pub struct ListenersConfigDocument { } /// Named listener profile. -#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize)] +#[derive(Clone, serde::Deserialize, Eq, PartialEq)] pub struct ListenersProfileConfig { /// Profile code. pub name: std::string::String, @@ -75,9 +75,9 @@ pub fn validate_listeners_json_schema(raw_json: &str) -> ks_core::Result<()> { }; return match validator.validate(&instance) { std::result::Result::Ok(()) => std::result::Result::Ok(()), - std::result::Result::Err(error) => std::result::Result::Err(ks_core::Error::new( + std::result::Result::Err(_) => std::result::Result::Err(ks_core::Error::new( "listeners_config_schema_validation_failed", - error.to_string(), + "listeners configuration does not satisfy its schema", )), }; } @@ -90,10 +90,10 @@ pub fn parse_listeners_json(raw_json: &str) -> ks_core::Result(raw_json) { std::result::Result::Ok(document) => document, - std::result::Result::Err(error) => { + std::result::Result::Err(_) => { return std::result::Result::Err(ks_core::Error::new( "listeners_config_json_decode_failed", - error.to_string(), + "listeners configuration could not be decoded", )); }, }; @@ -214,7 +214,8 @@ pub fn validate_listeners_document(document: &ListenersConfigDocument) -> ks_cor #[cfg(test)] mod tests { const DEFAULT_LISTENERS: &str = include_str!("../../config/listeners.config.json"); - const EXAMPLE_LISTENERS: &str = include_str!("../../config/example.listeners.config.json"); + const EXAMPLE_LISTENERS: &str = + include_str!("../../config/exemples/example.listeners.config.json"); #[test] fn default_and_example_listener_configs_validate() { diff --git a/ks-config/src/schema.rs b/ks-config/src/schema.rs new file mode 100644 index 0000000..2e6f970 --- /dev/null +++ b/ks-config/src/schema.rs @@ -0,0 +1,56 @@ +// file: ks-config/src/schema.rs +// version: 1 + +//! Generic JSON Schema validation helpers for binary-owned configuration fragments. + +/// Validates one JSON value against caller-owned JSON Schema text without echoing instance values. +pub fn validate_json_value_against_schema( + schema_json: &str, + instance: &serde_json::Value, + validation_error_code: &str, +) -> ks_core::Result<()> { + let schema = match serde_json::from_str::(schema_json) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(ks_core::Error::new( + "config_fragment_schema_parse_failed", + error.to_string(), + )); + }, + }; + let validator = match jsonschema::validator_for(&schema) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(ks_core::Error::new( + "config_fragment_schema_compile_failed", + error.to_string(), + )); + }, + }; + return match validator.validate(instance) { + std::result::Result::Ok(()) => std::result::Result::Ok(()), + std::result::Result::Err(_) => std::result::Result::Err(ks_core::Error::new( + validation_error_code, + "configuration fragment does not satisfy its schema", + )), + }; +} + +#[cfg(test)] +mod tests { + #[test] + fn validation_error_never_echoes_instance_secret() { + let schema = r#"{"type":"object","additionalProperties":false,"required":["value"],"properties":{"value":{"type":"integer"}}}"#; + let instance = serde_json::json!({"value": "SECRET-CANARY"}); + let result = super::validate_json_value_against_schema( + schema, + &instance, + "test_fragment_validation_failed", + ); + let error = match result { + std::result::Result::Ok(()) => panic!("invalid fragment must fail"), + std::result::Result::Err(error) => error, + }; + assert!(!error.to_string().contains("SECRET-CANARY")); + } +} diff --git a/ks-config/src/sensitivity.rs b/ks-config/src/sensitivity.rs new file mode 100644 index 0000000..c35a9cd --- /dev/null +++ b/ks-config/src/sensitivity.rs @@ -0,0 +1,160 @@ +// file: ks-config/src/sensitivity.rs +// version: 1 + +//! Application environment-variable sensitivity classification. + +/// Sensitivity assigned to one Khadhroony-owned environment value. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum EnvironmentValueSensitivity { + /// Secret value that must never cross logs, diagnostics, serialization or UI boundaries. + Secret, + /// Internal value that is hidden by default and may appear only through an explicit bounded diagnostic. + Internal, + /// Value explicitly classified as eligible for a public DTO. + Public, +} + +/// Classifies every Khadhroony environment placeholder contained in arbitrary text. +/// +/// The returned sensitivity follows `Secret > Internal > Public`, so an endpoint URL that embeds +/// one `KS_SECRET_*` placeholder is classified as secret even when the surrounding URL is public. +pub fn classify_environment_template( + raw: &str, +) -> std::option::Option { + let bytes = raw.as_bytes(); + let mut sensitivity = std::option::Option::None; + let mut index = 0_usize; + while index < bytes.len() { + if bytes[index] == b'$' && index + 1 < bytes.len() && bytes[index + 1] == b'{' { + let mut end = index + 2; + while end < bytes.len() && bytes[end] != b'}' { + end += 1; + } + if end < bytes.len() { + let expression = &raw[index + 2..end]; + let name = match expression.split_once(":-") { + std::option::Option::Some((name, _)) => name, + std::option::Option::None => expression, + }; + sensitivity = merge_sensitivity(sensitivity, classify_environment_variable(name)); + index = end + 1; + continue; + } + } + index += 1; + } + return sensitivity; +} + +/// Classifies a Khadhroony-owned environment variable by its namespace. +pub fn classify_environment_variable( + name: &str, +) -> std::option::Option { + if name.starts_with("KS_SECRET_") || name.starts_with("KB_SECRET_") { + return std::option::Option::Some(EnvironmentValueSensitivity::Secret); + } + if name.starts_with("KS_PUBLIC_") || name.starts_with("KB_PUBLIC_") { + return std::option::Option::Some(EnvironmentValueSensitivity::Public); + } + if name.starts_with("KS_") || name.starts_with("KB_") { + return std::option::Option::Some(EnvironmentValueSensitivity::Internal); + } + return std::option::Option::None; +} + +/// Produces the generic diagnostic representation allowed for one environment value. +pub fn diagnostic_environment_value( + name: &str, + value: &str, +) -> std::option::Option { + return match classify_environment_variable(name) { + std::option::Option::Some(EnvironmentValueSensitivity::Secret) => { + std::option::Option::Some("".to_string()) + }, + std::option::Option::Some(EnvironmentValueSensitivity::Internal) => { + std::option::Option::Some("".to_string()) + }, + std::option::Option::Some(EnvironmentValueSensitivity::Public) => { + std::option::Option::Some(value.to_string()) + }, + std::option::Option::None => std::option::Option::None, + }; +} + +fn merge_sensitivity( + left: std::option::Option, + right: std::option::Option, +) -> std::option::Option { + if left == std::option::Option::Some(EnvironmentValueSensitivity::Secret) + || right == std::option::Option::Some(EnvironmentValueSensitivity::Secret) + { + return std::option::Option::Some(EnvironmentValueSensitivity::Secret); + } + if left == std::option::Option::Some(EnvironmentValueSensitivity::Internal) + || right == std::option::Option::Some(EnvironmentValueSensitivity::Internal) + { + return std::option::Option::Some(EnvironmentValueSensitivity::Internal); + } + if left == std::option::Option::Some(EnvironmentValueSensitivity::Public) + || right == std::option::Option::Some(EnvironmentValueSensitivity::Public) + { + return std::option::Option::Some(EnvironmentValueSensitivity::Public); + } + return std::option::Option::None; +} + +#[cfg(test)] +mod tests { + #[test] + fn composed_values_inherit_the_strongest_placeholder_sensitivity() { + assert_eq!( + super::classify_environment_template( + "https://rpc.invalid/?api-key=${KS_SECRET_HELIUS_API_KEY}&mint=${KS_PUBLIC_DEVNET_TOKEN_2022_MINT}" + ), + std::option::Option::Some(super::EnvironmentValueSensitivity::Secret) + ); + assert_eq!( + super::classify_environment_template( + "${KS_LOGS_DIRECTORY:-logs}/${KS_PUBLIC_SPL_TOKEN_PROGRAM_ID}" + ), + std::option::Option::Some(super::EnvironmentValueSensitivity::Internal) + ); + assert_eq!( + super::classify_environment_template("${KS_PUBLIC_SPL_TOKEN_PROGRAM_ID}"), + std::option::Option::Some(super::EnvironmentValueSensitivity::Public) + ); + } + + #[test] + fn generic_diagnostic_never_reveals_secret_or_internal_values() { + assert_eq!( + super::diagnostic_environment_value("KS_SECRET_HELIUS_API_KEY", "SECRET-CANARY"), + std::option::Option::Some("".to_string()) + ); + assert_eq!( + super::diagnostic_environment_value("KS_LOGS_DIRECTORY", "/private/logs"), + std::option::Option::Some("".to_string()) + ); + assert_eq!( + super::diagnostic_environment_value("KS_PUBLIC_SPL_TOKEN_PROGRAM_ID", "Token111"), + std::option::Option::Some("Token111".to_string()) + ); + } + + #[test] + fn namespaces_map_to_secret_internal_and_public() { + assert_eq!( + super::classify_environment_variable("KS_SECRET_HELIUS_API_KEY"), + std::option::Option::Some(super::EnvironmentValueSensitivity::Secret) + ); + assert_eq!( + super::classify_environment_variable("KS_LOGS_DIRECTORY"), + std::option::Option::Some(super::EnvironmentValueSensitivity::Internal) + ); + assert_eq!( + super::classify_environment_variable("KB_PUBLIC_DEMO_VALUE"), + std::option::Option::Some(super::EnvironmentValueSensitivity::Public) + ); + assert_eq!(super::classify_environment_variable("HOME"), std::option::Option::None); + } +} diff --git a/ks-config/src/settings.rs b/ks-config/src/settings.rs index e9eefae..de087b9 100644 --- a/ks-config/src/settings.rs +++ b/ks-config/src/settings.rs @@ -1,16 +1,13 @@ // file: ks-config/src/settings.rs -// version: 22 +// version: 23 //! Resolved runtime configuration models retained during the `0.5.1` source-document split. -use ts_rs::TS; // rust-rules: derive-import - const CONFIG_JSON_SCHEMA: &str = include_str!("../../config/schemas/resolved.app.config.schema.json"); /// Resolved runtime configuration containing every composed named profile. -#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize, TS)] -#[ts(export, export_to = "../frontend/ts/bindings/ks_config/settings/AppConfig.ts")] +#[derive(Clone, serde::Deserialize, Eq, PartialEq)] pub struct AppConfig { /// Active profile name. pub active_profile: std::string::String, @@ -19,16 +16,10 @@ pub struct AppConfig { } /// Resolved runtime profile selected by the root active profile name. -#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_config/settings/ProfileConfig.ts" -)] +#[derive(Clone, serde::Deserialize, Eq, PartialEq)] pub struct ProfileConfig { /// Profile code. pub name: std::string::String, - /// Application metadata. - pub app: AppSectionConfig, /// Database configuration. pub database: DatabaseConfig, /// Solana endpoint and listener configuration. @@ -37,29 +28,10 @@ pub struct ProfileConfig { pub wallet: WalletConfig, /// Execution safety configuration. pub execution: ExecutionConfig, - /// Demo application configuration. - pub demo: DemoConfig, -} - -/// Application metadata for a profile. -#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_config/settings/AppSectionConfig.ts" -)] -pub struct AppSectionConfig { - /// Application name. - pub name: std::string::String, - /// Environment name. - pub environment: std::string::String, } /// Database backend configuration. -#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_config/settings/DatabaseConfig.ts" -)] +#[derive(Clone, serde::Deserialize, Eq, PartialEq)] pub struct DatabaseConfig { /// Enables database storage. pub enabled: bool, @@ -72,33 +44,26 @@ pub struct DatabaseConfig { } /// PostgreSQL backend configuration. -#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_config/settings/PostgresConfig.ts" -)] +#[derive(Clone, serde::Deserialize, Eq, PartialEq)] pub struct PostgresConfig { /// PostgreSQL URL or local placeholder. pub url: std::string::String, /// Maximum connection count. pub max_connections: u32, /// Connection timeout in milliseconds. - #[ts(type = "number")] pub connect_timeout_ms: u64, /// Enables schema initialization at startup. pub auto_initialize_schema: bool, } /// SQLite backend configuration. -#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize, TS)] -#[ts(export, export_to = "../frontend/ts/bindings/ks_config/settings/SqliteConfig.ts")] +#[derive(Clone, serde::Deserialize, Eq, PartialEq)] pub struct SqliteConfig { /// SQLite database path. pub path: std::string::String, /// Creates the file when missing. pub create_if_missing: bool, /// Busy timeout in milliseconds. - #[ts(type = "number")] pub busy_timeout_ms: u64, /// Maximum connection count. pub max_connections: u32, @@ -109,8 +74,7 @@ pub struct SqliteConfig { } /// Solana endpoints and listener configuration. -#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize, TS)] -#[ts(export, export_to = "../frontend/ts/bindings/ks_config/settings/SolanaConfig.ts")] +#[derive(Clone, serde::Deserialize, Eq, PartialEq)] pub struct SolanaConfig { /// HTTP JSON-RPC endpoints. pub http_endpoints: std::vec::Vec, @@ -121,11 +85,7 @@ pub struct SolanaConfig { } /// HTTP JSON-RPC endpoint configuration. -#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_config/settings/HttpEndpointConfig.ts" -)] +#[derive(Clone, serde::Deserialize, Eq, PartialEq)] pub struct HttpEndpointConfig { /// Endpoint name. pub name: std::string::String, @@ -138,10 +98,8 @@ pub struct HttpEndpointConfig { /// Full endpoint URL. pub url: std::string::String, /// Connection timeout in milliseconds. - #[ts(type = "number")] pub connect_timeout_ms: u64, /// Request timeout in milliseconds. - #[ts(type = "number")] pub request_timeout_ms: u64, /// Maximum idle connections per host. pub max_idle_connections_per_host: u32, @@ -150,11 +108,7 @@ pub struct HttpEndpointConfig { } /// Standard Solana WebSocket endpoint configuration. -#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_config/settings/WsEndpointConfig.ts" -)] +#[derive(Clone, serde::Deserialize, Eq, PartialEq)] pub struct WsEndpointConfig { /// Endpoint name. pub name: std::string::String, @@ -167,13 +121,10 @@ pub struct WsEndpointConfig { /// Full endpoint URL. pub url: std::string::String, /// Connection timeout in milliseconds. - #[ts(type = "number")] pub connect_timeout_ms: u64, /// Request timeout in milliseconds. - #[ts(type = "number")] pub request_timeout_ms: u64, /// Unsubscribe timeout in milliseconds. - #[ts(type = "number")] pub unsubscribe_timeout_ms: u64, /// Writer channel capacity. pub write_channel_capacity: u32, @@ -186,11 +137,7 @@ pub struct WsEndpointConfig { } /// Role-specific endpoint limits. -#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_config/settings/EndpointRoleConfig.ts" -)] +#[derive(Clone, serde::Deserialize, Eq, PartialEq)] pub struct EndpointRoleConfig { /// Role code used by endpoint pools. pub role: std::string::String, @@ -209,16 +156,11 @@ pub struct EndpointRoleConfig { /// Maximum subscriptions allowed for this role on this URL. pub max_subscriptions: u32, /// Pause after a rate limit response in milliseconds. - #[ts(type = "number")] pub pause_after_rate_limit_ms: u64, } /// Listener configuration used by standard WebSocket subscriptions. -#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_config/settings/ListenerConfig.ts" -)] +#[derive(Clone, serde::Deserialize, Eq, PartialEq)] pub struct ListenerConfig { /// Enables listener creation. pub enabled: bool, @@ -233,11 +175,7 @@ pub struct ListenerConfig { } /// Log listener filtered by a program identifier mention. -#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_config/settings/LogListenerConfig.ts" -)] +#[derive(Clone, serde::Deserialize, Eq, PartialEq)] pub struct LogListenerConfig { /// Listener name. pub name: std::string::String, @@ -252,11 +190,7 @@ pub struct LogListenerConfig { } /// Program account listener. -#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_config/settings/ProgramListenerConfig.ts" -)] +#[derive(Clone, serde::Deserialize, Eq, PartialEq)] pub struct ProgramListenerConfig { /// Listener name. pub name: std::string::String, @@ -271,11 +205,7 @@ pub struct ProgramListenerConfig { } /// Account listener. -#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_config/settings/AccountListenerConfig.ts" -)] +#[derive(Clone, serde::Deserialize, Eq, PartialEq)] pub struct AccountListenerConfig { /// Listener name. pub name: std::string::String, @@ -290,8 +220,7 @@ pub struct AccountListenerConfig { } /// Wallet configuration. -#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize, TS)] -#[ts(export, export_to = "../frontend/ts/bindings/ks_config/settings/WalletConfig.ts")] +#[derive(Clone, serde::Deserialize, Eq, PartialEq)] pub struct WalletConfig { /// Wallet directory path. pub wallet_dir: std::string::String, @@ -306,11 +235,7 @@ pub struct WalletConfig { } /// Execution safety configuration. -#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_config/settings/ExecutionConfig.ts" -)] +#[derive(Clone, serde::Deserialize, Eq, PartialEq)] pub struct ExecutionConfig { /// Enables local validator transaction sending. pub localnet_send_enabled: bool, @@ -327,48 +252,29 @@ pub struct ExecutionConfig { /// Requires explicit operator confirmation before send. pub require_operator_confirmation: bool, /// Maximum spend in lamports for local validator tests. - #[ts(type = "number")] pub localnet_max_spend_lamports: u64, /// Maximum spend in lamports for devnet tests. - #[ts(type = "number")] pub devnet_max_spend_lamports: u64, /// Maximum spend in lamports for testnet tests. - #[ts(type = "number")] pub testnet_max_spend_lamports: u64, /// Maximum spend in lamports for mainnet operations. - #[ts(type = "number")] pub mainnet_max_spend_lamports: u64, /// Maximum estimated transaction fee in lamports. - #[ts(type = "number")] pub max_fee_lamports: u64, /// Maximum compute-unit price in micro-lamports. - #[ts(type = "number")] pub max_compute_unit_price_micro_lamports: u64, /// Maximum accepted age for a recent blockhash in slots. - #[ts(type = "number")] pub recent_blockhash_max_age_slots: u64, /// Maximum node retransmission retries requested by `sendTransaction`. pub send_max_retries: u32, /// Delay between transaction confirmation polls. - #[ts(type = "number")] pub confirmation_poll_interval_ms: u64, /// Maximum number of transaction confirmation polls. pub confirmation_max_attempts: u32, /// Maximum devnet faucet airdrop allowed for a temporary wallet. - #[ts(type = "number")] pub devnet_airdrop_max_lamports: u64, } -/// Demo application configuration. -#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize, TS)] -#[ts(export, export_to = "../frontend/ts/bindings/ks_config/settings/DemoConfig.ts")] -pub struct DemoConfig { - /// Enables live demo pages. - pub live_demo_enabled: bool, - /// Enables trading demo pages. - pub trading_demo_enabled: bool, -} - /// Returns the embedded JSON Schema text used for resolved runtime contract validation. pub fn config_json_schema_text() -> &'static str { return CONFIG_JSON_SCHEMA; @@ -413,9 +319,9 @@ pub fn validate_config_json_schema(raw_json: &str) -> ks_core::Result<()> { let validation_result = validator.validate(&instance); return match validation_result { std::result::Result::Ok(()) => std::result::Result::Ok(()), - std::result::Result::Err(error) => std::result::Result::Err(ks_core::Error::new( + std::result::Result::Err(_) => std::result::Result::Err(ks_core::Error::new( "config_json_schema_validation_failed", - error.to_string(), + "resolved configuration does not satisfy its schema", )), }; } @@ -428,10 +334,10 @@ pub fn parse_config_json(raw_json: &str) -> ks_core::Result { } let config = match serde_json::from_str::(raw_json) { std::result::Result::Ok(config) => config, - std::result::Result::Err(error) => { + std::result::Result::Err(_) => { return std::result::Result::Err(ks_core::Error::new( "config_json_decode_failed", - error.to_string(), + "resolved configuration could not be decoded", )); }, }; @@ -477,36 +383,6 @@ pub fn read_config_json_file_with_environment( return parse_config_json(&resolved); } -/// Serializes a configuration value to compact JSON. -pub fn serialize_config_json(config: &AppConfig) -> ks_core::Result { - match validate_config(config) { - std::result::Result::Ok(()) => (), - std::result::Result::Err(error) => return std::result::Result::Err(error), - } - return match serde_json::to_string(config) { - std::result::Result::Ok(serialized) => std::result::Result::Ok(serialized), - std::result::Result::Err(error) => std::result::Result::Err(ks_core::Error::new( - "config_json_serialize_failed", - error.to_string(), - )), - }; -} - -/// Serializes a configuration value to pretty JSON. -pub fn serialize_config_json_pretty(config: &AppConfig) -> ks_core::Result { - match validate_config(config) { - std::result::Result::Ok(()) => (), - std::result::Result::Err(error) => return std::result::Result::Err(error), - } - return match serde_json::to_string_pretty(config) { - std::result::Result::Ok(serialized) => std::result::Result::Ok(serialized), - std::result::Result::Err(error) => std::result::Result::Err(ks_core::Error::new( - "config_json_pretty_serialize_failed", - error.to_string(), - )), - }; -} - /// Returns the active profile declared by the root configuration. pub fn active_profile(config: &AppConfig) -> ks_core::Result<&ProfileConfig> { for profile in &config.profiles { @@ -576,10 +452,6 @@ fn validate_root_config(config: &AppConfig) -> ks_core::Result<()> { } fn validate_profile(profile: &ProfileConfig) -> ks_core::Result<()> { - match validate_app_section(&profile.app) { - std::result::Result::Ok(()) => (), - std::result::Result::Err(error) => return std::result::Result::Err(error), - } match validate_database(&profile.database) { std::result::Result::Ok(()) => (), std::result::Result::Err(error) => return std::result::Result::Err(error), @@ -599,14 +471,6 @@ fn validate_profile(profile: &ProfileConfig) -> ks_core::Result<()> { return validate_wallet_execution_pair(&profile.wallet, &profile.execution); } -fn validate_app_section(config: &AppSectionConfig) -> ks_core::Result<()> { - match require_non_empty(&config.name, "app.name") { - std::result::Result::Ok(()) => (), - std::result::Result::Err(error) => return std::result::Result::Err(error), - } - return require_non_empty(&config.environment, "app.environment"); -} - pub(crate) fn validate_database(config: &DatabaseConfig) -> ks_core::Result<()> { if config.backend != "postgres" && config.backend != "sqlite" { return std::result::Result::Err(ks_core::Error::new( @@ -683,7 +547,7 @@ pub(crate) fn validate_http_endpoint(config: &HttpEndpointConfig) -> ks_core::Re if !config.url.starts_with("http://") && !config.url.starts_with("https://") { return std::result::Result::Err(ks_core::Error::new( "http_endpoint_url_invalid", - config.url.clone(), + config.name.clone(), )); } if config.connect_timeout_ms == 0 @@ -714,7 +578,7 @@ pub(crate) fn validate_ws_endpoint(config: &WsEndpointConfig) -> ks_core::Result if !config.url.starts_with("ws://") && !config.url.starts_with("wss://") { return std::result::Result::Err(ks_core::Error::new( "ws_endpoint_url_invalid", - config.url.clone(), + config.name.clone(), )); } if config.connect_timeout_ms == 0 @@ -1006,24 +870,6 @@ pub(crate) fn require_non_empty(value: &str, field_name: &str) -> ks_core::Resul #[cfg(test)] mod tests { - use ts_rs::TS; // rust-rules: derive-import - - #[test] - fn exported_json_configuration_numbers_do_not_use_bigint() { - let config = ts_rs::Config::default(); - let declarations = [ - ::decl(&config), - ::decl(&config), - ::decl(&config), - ::decl(&config), - ::decl(&config), - ::decl(&config), - ]; - for declaration in declarations { - assert!(!declaration.contains("bigint")); - } - } - const DEFAULT_CONFIG: &str = include_str!("../../test-fixtures/config/resolved.app.config.json"); const EXAMPLE_CONFIG: &str = @@ -1077,29 +923,6 @@ mod tests { //assert_eq!(active.name, "local_devnet"); } - #[test] - fn default_app_config_serializes_and_roundtrips() { - let config_result = super::parse_config_json(DEFAULT_CONFIG); - assert!(config_result.is_ok()); - let config = match config_result { - std::result::Result::Ok(config) => config, - std::result::Result::Err(error) => panic!("default app config must parse: {error}"), - }; - let serialized_result = super::serialize_config_json_pretty(&config); - assert!(serialized_result.is_ok()); - let serialized = match serialized_result { - std::result::Result::Ok(serialized) => serialized, - std::result::Result::Err(error) => panic!("default app config must serialize: {error}"), - }; - let reparsed_result = super::parse_config_json(&serialized); - assert!(reparsed_result.is_ok()); - let reparsed = match reparsed_result { - std::result::Result::Ok(reparsed) => reparsed, - std::result::Result::Err(error) => panic!("serialized config must parse: {error}"), - }; - assert_eq!(config, reparsed); - } - #[test] fn parser_rejects_missing_active_profile() { let mut value = parse_default_value(); diff --git a/ks-config/src/store.rs b/ks-config/src/store.rs index 38f77c4..42e8e8c 100644 --- a/ks-config/src/store.rs +++ b/ks-config/src/store.rs @@ -1,12 +1,12 @@ // file: ks-config/src/store.rs -// version: 1 +// version: 2 //! Independent store configuration document loading and validation. const STORE_JSON_SCHEMA: &str = include_str!("../../config/schemas/store.config.schema.json"); /// Root store document with one default profile and named alternatives. -#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize)] +#[derive(Clone, serde::Deserialize, Eq, PartialEq)] pub struct StoreConfigDocument { /// Default store profile used when a composition does not override it. pub default_profile: std::string::String, @@ -15,7 +15,7 @@ pub struct StoreConfigDocument { } /// Named store profile. -#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize)] +#[derive(Clone, serde::Deserialize, Eq, PartialEq)] pub struct StoreProfileConfig { /// Profile code. pub name: std::string::String, @@ -66,9 +66,9 @@ pub fn validate_store_json_schema(raw_json: &str) -> ks_core::Result<()> { }; return match validator.validate(&instance) { std::result::Result::Ok(()) => std::result::Result::Ok(()), - std::result::Result::Err(error) => std::result::Result::Err(ks_core::Error::new( + std::result::Result::Err(_) => std::result::Result::Err(ks_core::Error::new( "store_config_schema_validation_failed", - error.to_string(), + "store configuration does not satisfy its schema", )), }; } @@ -81,10 +81,10 @@ pub fn parse_store_json(raw_json: &str) -> ks_core::Result } let document = match serde_json::from_str::(raw_json) { std::result::Result::Ok(value) => value, - std::result::Result::Err(error) => { + std::result::Result::Err(_) => { return std::result::Result::Err(ks_core::Error::new( "store_config_json_decode_failed", - error.to_string(), + "store configuration could not be decoded", )); }, }; @@ -176,7 +176,7 @@ pub fn validate_store_document(document: &StoreConfigDocument) -> ks_core::Resul #[cfg(test)] mod tests { const DEFAULT_STORE: &str = include_str!("../../config/store.config.json"); - const EXAMPLE_STORE: &str = include_str!("../../config/example.store.config.json"); + const EXAMPLE_STORE: &str = include_str!("../../config/exemples/example.store.config.json"); #[test] fn default_and_example_store_configs_validate() { diff --git a/ks-config/src/transport.rs b/ks-config/src/transport.rs index 9fd963c..c6b5615 100644 --- a/ks-config/src/transport.rs +++ b/ks-config/src/transport.rs @@ -1,5 +1,5 @@ // file: ks-config/src/transport.rs -// version: 2 +// version: 3 //! Independent Solana transport configuration document loading and validation. @@ -7,7 +7,7 @@ const TRANSPORT_JSON_SCHEMA: &str = include_str!("../../config/schemas/transport.config.schema.json"); /// Root transport document containing shared endpoint defaults and named profiles. -#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize)] +#[derive(Clone, serde::Deserialize, Eq, PartialEq)] pub struct TransportConfigDocument { /// Default transport profile used when a composition does not override it. pub default_profile: std::string::String, @@ -18,7 +18,7 @@ pub struct TransportConfigDocument { } /// Default values shared by one class of WebSocket endpoints. -#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize)] +#[derive(Clone, serde::Deserialize, Eq, PartialEq)] pub struct WsEndpointDefaultsConfig { /// Defaults code referenced by endpoints. pub name: std::string::String, @@ -37,7 +37,7 @@ pub struct WsEndpointDefaultsConfig { } /// Endpoint-specific overrides applied on top of named WebSocket defaults. -#[derive(Clone, Debug, Default, serde::Deserialize, Eq, PartialEq, serde::Serialize)] +#[derive(Clone, Default, serde::Deserialize, Eq, PartialEq)] pub struct WsEndpointOverridesConfig { /// Optional connection timeout override. pub connect_timeout_ms: std::option::Option, @@ -54,7 +54,7 @@ pub struct WsEndpointOverridesConfig { } /// Source shape of one WebSocket endpoint before defaults are resolved. -#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize)] +#[derive(Clone, serde::Deserialize, Eq, PartialEq)] pub struct WsEndpointSourceConfig { /// Endpoint name. pub name: std::string::String, @@ -76,7 +76,7 @@ pub struct WsEndpointSourceConfig { } /// Named Solana HTTP and WebSocket endpoint source profile. -#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize)] +#[derive(Clone, serde::Deserialize, Eq, PartialEq)] pub struct TransportProfileSourceConfig { /// Profile code. pub name: std::string::String, @@ -87,7 +87,7 @@ pub struct TransportProfileSourceConfig { } /// Fully resolved transport profile consumed by transport clients. -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Eq, PartialEq)] pub struct TransportProfileConfig { /// Profile code. pub name: std::string::String, @@ -140,9 +140,9 @@ pub fn validate_transport_json_schema(raw_json: &str) -> ks_core::Result<()> { }; return match validator.validate(&instance) { std::result::Result::Ok(()) => std::result::Result::Ok(()), - std::result::Result::Err(error) => std::result::Result::Err(ks_core::Error::new( + std::result::Result::Err(_) => std::result::Result::Err(ks_core::Error::new( "transport_config_schema_validation_failed", - error.to_string(), + "transport configuration does not satisfy its schema", )), }; } @@ -155,10 +155,10 @@ pub fn parse_transport_json(raw_json: &str) -> ks_core::Result(raw_json) { std::result::Result::Ok(value) => value, - std::result::Result::Err(error) => { + std::result::Result::Err(_) => { return std::result::Result::Err(ks_core::Error::new( "transport_config_json_decode_failed", - error.to_string(), + "transport configuration could not be decoded", )); }, }; @@ -393,7 +393,8 @@ pub fn validate_transport_document(document: &TransportConfigDocument) -> ks_cor #[cfg(test)] mod tests { const DEFAULT_TRANSPORT: &str = include_str!("../../config/transport.config.json"); - const EXAMPLE_TRANSPORT: &str = include_str!("../../config/example.transport.config.json"); + const EXAMPLE_TRANSPORT: &str = + include_str!("../../config/exemples/example.transport.config.json"); #[test] fn default_and_example_transport_configs_validate() { diff --git a/ks-config/src/wallet.rs b/ks-config/src/wallet.rs index 9887e16..2db63e0 100644 --- a/ks-config/src/wallet.rs +++ b/ks-config/src/wallet.rs @@ -1,12 +1,12 @@ // file: ks-config/src/wallet.rs -// version: 1 +// version: 3 //! Independent wallet configuration document loading and validation. const WALLET_JSON_SCHEMA: &str = include_str!("../../config/schemas/wallet.config.schema.json"); /// Root wallet document with global storage and named runtime profiles. -#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize)] +#[derive(Clone, serde::Deserialize, Eq, PartialEq)] pub struct WalletConfigDocument { /// Root directory for wallet storage, independent from profile selection. pub wallets_directory: std::string::String, @@ -17,7 +17,7 @@ pub struct WalletConfigDocument { } /// Named wallet profile whose directory is relative to the global wallet root unless absolute. -#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize)] +#[derive(Clone, serde::Deserialize, Eq, PartialEq)] pub struct WalletProfileConfig { /// Profile code. pub name: std::string::String, @@ -67,18 +67,18 @@ pub fn parse_wallet_json(raw_json: &str) -> ks_core::Result(raw_json) { std::result::Result::Ok(value) => value, - std::result::Result::Err(error) => { + std::result::Result::Err(_) => { return std::result::Result::Err(ks_core::Error::new( "wallet_config_json_decode_failed", - error.to_string(), + "wallet configuration could not be decoded", )); }, }; @@ -206,7 +206,7 @@ pub fn validate_wallet_document(document: &WalletConfigDocument) -> ks_core::Res #[cfg(test)] mod tests { const DEFAULT_WALLET: &str = include_str!("../../config/wallet.config.json"); - const EXAMPLE_WALLET: &str = include_str!("../../config/example.wallet.config.json"); + const EXAMPLE_WALLET: &str = include_str!("../../config/exemples/example.wallet.config.json"); #[test] fn default_and_example_wallet_configs_validate() { diff --git a/ks-config/tests/external_composition_api.rs b/ks-config/tests/external_composition_api.rs index d3b34c7..6c6f112 100644 --- a/ks-config/tests/external_composition_api.rs +++ b/ks-config/tests/external_composition_api.rs @@ -6,36 +6,37 @@ #[test] fn external_consumer_resolves_composition_through_crate_root_exports() { let composition = match ks_config::parse_composition_json(include_str!( - "../../config/example.kb-app-demo-desktop.default.config.json" + "../../config/exemples/example.kb-app-demo-desktop.default.config.json" )) { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => panic!("composition must parse: {error}"), }; let transport = match ks_config::parse_transport_json(include_str!( - "../../config/example.transport.config.json" + "../../config/exemples/example.transport.config.json" )) { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => panic!("transport must parse: {error}"), }; let listeners = match ks_config::parse_listeners_json(include_str!( - "../../config/example.listeners.config.json" + "../../config/exemples/example.listeners.config.json" )) { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => panic!("listeners must parse: {error}"), }; - let store = - match ks_config::parse_store_json(include_str!("../../config/example.store.config.json")) { - std::result::Result::Ok(value) => value, - std::result::Result::Err(error) => panic!("store must parse: {error}"), - }; - let wallet = - match ks_config::parse_wallet_json(include_str!("../../config/example.wallet.config.json")) - { - std::result::Result::Ok(value) => value, - std::result::Result::Err(error) => panic!("wallet must parse: {error}"), - }; + let store = match ks_config::parse_store_json(include_str!( + "../../config/exemples/example.store.config.json" + )) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("store must parse: {error}"), + }; + let wallet = match ks_config::parse_wallet_json(include_str!( + "../../config/exemples/example.wallet.config.json" + )) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("wallet must parse: {error}"), + }; let execution = match ks_config::parse_execution_json(include_str!( - "../../config/example.execution.config.json" + "../../config/exemples/example.execution.config.json" )) { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => panic!("execution must parse: {error}"), diff --git a/ks-config/tests/external_sensitivity_api.rs b/ks-config/tests/external_sensitivity_api.rs new file mode 100644 index 0000000..611c88f --- /dev/null +++ b/ks-config/tests/external_sensitivity_api.rs @@ -0,0 +1,18 @@ +// file: ks-config/tests/external_sensitivity_api.rs +// version: 1 + +//! External-contract coverage for configuration sensitivity helpers. + +#[test] +fn external_consumer_classifies_composed_values_and_redacts_secrets() { + let sensitivity = ks_config::classify_environment_template( + "https://provider.invalid/?api-key=${KS_SECRET_HELIUS_API_KEY}", + ); + assert_eq!( + sensitivity, + std::option::Option::Some(ks_config::EnvironmentValueSensitivity::Secret) + ); + let diagnostic = + ks_config::diagnostic_environment_value("KS_SECRET_HELIUS_API_KEY", "SECRET-CANARY"); + assert_eq!(diagnostic, std::option::Option::Some("".to_string())); +} diff --git a/ks-lib/CHANGELOG.md b/ks-lib/CHANGELOG.md index 066b3bc..acdf076 100644 --- a/ks-lib/CHANGELOG.md +++ b/ks-lib/CHANGELOG.md @@ -1,8 +1,13 @@ - + # CHANGELOG — ks-lib +## `0.5.1-pre.008` + +- retire la dépendance `ts-rs`, les dérivations/attributs TS-RS et les bindings TypeScript générés historiques de `ks-lib` ; +- conserve inchangés les contrats Rust de décodage, matérialisation, exécution et modèles : toute exposition TypeScript appartient désormais à un DTO/wrapper de l'application consommatrice. + ## `0.5.1-pre.003` - migre les 255 identités runtime/persistables effectivement présentes de `kb-lib.decoder.*`, `kb-lib.executor.*` et `kb-lib.materializer.*` vers `ks-lib-decoder.*`, `ks-lib-executor.*` et `ks-lib-materializer.*` ; diff --git a/ks-lib/Cargo.toml b/ks-lib/Cargo.toml index cea0fc7..2550a7f 100644 --- a/ks-lib/Cargo.toml +++ b/ks-lib/Cargo.toml @@ -1,5 +1,5 @@ # file: ks-lib/Cargo.toml -# version: 16 +# version: 17 [package] name = "ks-lib" @@ -47,7 +47,6 @@ spl-token-confidential-transfer-proof-extraction.workspace = true spl-token-interface.workspace = true spl-memo-interface.workspace = true tracing.workspace = true -ts-rs.workspace = true wincode.workspace = true [dev-dependencies] diff --git a/ks-lib/README.md b/ks-lib/README.md index f6a2e45..aa34376 100644 --- a/ks-lib/README.md +++ b/ks-lib/README.md @@ -1,5 +1,5 @@ - + # ks-lib @@ -65,6 +65,8 @@ L’inventaire audité comprend 25 composants nommés : onze matérialisateurs a `ks-lib` ne sélectionne aucun endpoint, ne lit pas directement un RPC, ne persiste aucune donnée, ne gère pas les secrets de wallet et ne soumet aucune transaction. +Depuis `0.5.1-pre.008`, `ks-lib` ne dépend plus de TS-RS et ne génère plus de bindings TypeScript. Ses contrats Rust restent généralistes ; une application Tauri qui doit exposer une partie de ces contrats définit un DTO/wrapper applicatif explicite. + ## Surface publique principale - contrats `DcApi*` et décodeurs concrets `Dc*Decoder` ; diff --git a/ks-lib/USAGE.md b/ks-lib/USAGE.md index 8353f5c..e1d014c 100644 --- a/ks-lib/USAGE.md +++ b/ks-lib/USAGE.md @@ -1,5 +1,5 @@ - + # Utilisation de ks-lib @@ -16,6 +16,8 @@ ks-lib = { path = "../ks-lib" } Toutes les fonctions retournant `ks_core::Result` utilisent le contrat d’erreur structuré du workspace. +`ks-lib` n'est pas une frontière TypeScript : depuis `0.5.1-pre.008`, la crate ne dépend plus de TS-RS et ne produit plus de bindings. Les applications exposent uniquement leurs wrappers/DTO propres. + ## Décodage contextualisé `DcApiInstructionDecoder` définit la reconnaissance et le décodage d’une instruction contextualisée. Les décodeurs concrets sont des valeurs sans état. diff --git a/ks-lib/src/executor/api/execution.rs b/ks-lib/src/executor/api/execution.rs index 12a9082..9719459 100644 --- a/ks-lib/src/executor/api/execution.rs +++ b/ks-lib/src/executor/api/execution.rs @@ -1,17 +1,11 @@ // file: ks-lib/src/executor/api/execution.rs -// version: 10 +// version: 11 //! Typed execution plans, policies and result contracts. -use ts_rs::TS; // rust-rules: derive-import - /// Exact capability returned for one program operation. -#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] #[serde(tag = "status", rename_all = "snake_case")] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/api/execution/ExecutionCapability.ts" -)] pub enum ExApiExecutionCapability { /// The operation is implemented by the executor. Supported { @@ -51,12 +45,8 @@ impl crate::ExApiExecutionCapability { } /// Solana cluster expected by an execution plan. -#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Serialize, TS)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Serialize)] #[serde(rename_all = "snake_case")] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/api/execution/ExecutionCluster.ts" -)] pub enum ExApiExecutionCluster { /// Local validator cluster. Localnet, @@ -101,11 +91,7 @@ impl<'de> serde::Deserialize<'de> for crate::ExApiExecutionCluster { } /// Cluster restrictions attached to an execution plan. -#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/api/execution/ExecutionClusterPolicy.ts" -)] +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] pub struct ExApiExecutionClusterPolicy { /// Cluster that the RPC endpoint must report. pub expected_cluster: crate::ExApiExecutionCluster, @@ -126,12 +112,8 @@ impl std::default::Default for crate::ExApiExecutionClusterPolicy { } /// Simulation requirement attached to an execution plan. -#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] #[serde(rename_all = "snake_case")] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/api/execution/ExecutionSimulationPolicy.ts" -)] pub enum ExApiExecutionSimulationPolicy { /// Simulation must succeed before signing or sending. Required, @@ -140,12 +122,8 @@ pub enum ExApiExecutionSimulationPolicy { } /// Blockhash source required when a transaction is assembled. -#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] #[serde(rename_all = "snake_case")] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/api/execution/ExecutionBlockhashKind.ts" -)] pub enum ExApiExecutionBlockhashKind { /// Fetch a recent blockhash and enforce a bounded age. Latest, @@ -154,11 +132,7 @@ pub enum ExApiExecutionBlockhashKind { } /// Blockhash or durable nonce policy attached to an execution plan. -#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/api/execution/ExecutionBlockhashPolicy.ts" -)] +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] pub struct ExApiExecutionBlockhashPolicy { /// Selected blockhash source. pub kind: crate::ExApiExecutionBlockhashKind, @@ -182,11 +156,7 @@ impl std::default::Default for crate::ExApiExecutionBlockhashPolicy { } /// Explicit cost ceilings attached to an execution plan. -#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/api/execution/ExecutionCostLimit.ts" -)] +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] pub struct ExApiExecutionCostLimit { /// Maximum lamports that operation instructions may transfer or lock. pub max_spend_lamports: std::option::Option, @@ -207,11 +177,7 @@ impl std::default::Default for crate::ExApiExecutionCostLimit { } /// Post-execution replay validation policy. -#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/api/execution/PostExecutionValidationPolicy.ts" -)] +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] pub struct ExApiPostExecutionValidationPolicy { /// Whether the sent signature must be inserted into canonical storage. pub canonical_insert_required: bool, @@ -235,11 +201,7 @@ impl std::default::Default for crate::ExApiPostExecutionValidationPolicy { } /// Conservative policy carried from intent creation to transaction assembly. -#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/api/execution/ExecutionPolicy.ts" -)] +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] pub struct ExApiExecutionPolicy { /// Cluster restrictions. pub cluster: crate::ExApiExecutionClusterPolicy, @@ -272,11 +234,7 @@ impl std::default::Default for crate::ExApiExecutionPolicy { } /// One account required by a planned instruction. -#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/api/execution/PlannedAccount.ts" -)] +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] pub struct ExApiPlannedAccount { /// Account public key. pub pubkey: crate::MdPubkey, @@ -287,11 +245,7 @@ pub struct ExApiPlannedAccount { } /// One fully encoded instruction in an execution plan. -#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/api/execution/PlannedInstruction.ts" -)] +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] pub struct ExApiPlannedInstruction { /// Target program id. pub program_id: crate::MdProgramId, @@ -304,11 +258,7 @@ pub struct ExApiPlannedInstruction { } /// Signer required before transaction assembly. -#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/api/execution/RequiredSigner.ts" -)] +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] pub struct ExApiRequiredSigner { /// Signer public key. pub pubkey: crate::MdPubkey, @@ -317,11 +267,7 @@ pub struct ExApiRequiredSigner { } /// Typed execution plan produced before transaction assembly. -#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/api/execution/PreparedExecutionPlan.ts" -)] +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] pub struct ExApiPreparedExecutionPlan { /// Stable executor crate name. pub executor_name: std::string::String, @@ -346,11 +292,7 @@ pub struct ExApiPreparedExecutionPlan { } /// Result returned by a transaction simulation adapter. -#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/api/execution/ExecutionSimulationResult.ts" -)] +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] pub struct ExApiExecutionSimulationResult { /// Whether an RPC simulation call was actually performed. pub simulated: bool, @@ -386,11 +328,7 @@ pub struct ExApiExecutionSimulationResult { } /// Result returned after an adapter submits a signed transaction. -#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/api/execution/ExecutionSendResult.ts" -)] +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] pub struct ExApiExecutionSendResult { /// Cluster used for submission. pub cluster: crate::ExApiExecutionCluster, @@ -401,12 +339,8 @@ pub struct ExApiExecutionSendResult { } /// Confirmation state returned by an execution confirmation adapter. -#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] #[serde(rename_all = "snake_case")] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/api/execution/ExecutionConfirmationStatus.ts" -)] pub enum ExApiExecutionConfirmationStatus { /// The transaction was observed at processed commitment. Processed, @@ -423,11 +357,7 @@ pub enum ExApiExecutionConfirmationStatus { } /// Result returned after waiting for transaction confirmation. -#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/api/execution/ExecutionConfirmationResult.ts" -)] +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] pub struct ExApiExecutionConfirmationResult { /// Cluster queried for confirmation. pub cluster: crate::ExApiExecutionCluster, @@ -446,11 +376,7 @@ pub struct ExApiExecutionConfirmationResult { } /// Diagnostic produced by post-execution canonical replay validation. -#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/api/execution/PostExecutionDiagnostic.ts" -)] +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] pub struct ExApiPostExecutionDiagnostic { /// Executed transaction signature. pub signature: crate::MdSignature, diff --git a/ks-lib/src/executor/api/executor.rs b/ks-lib/src/executor/api/executor.rs index 3f05b47..558110a 100644 --- a/ks-lib/src/executor/api/executor.rs +++ b/ks-lib/src/executor/api/executor.rs @@ -1,16 +1,10 @@ // file: ks-lib/src/executor/api/executor.rs -// version: 5 +// version: 6 //! Shared execution contract and neutral request models. -use ts_rs::TS; // rust-rules: derive-import - /// Support level returned by an executor for a requested operation. -#[derive(Clone, Copy, Debug, Eq, PartialEq, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/api/executor/ExecutionSupport.ts" -)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum ExApiExecutionSupport { /// The executor cannot handle the request. No, @@ -21,11 +15,7 @@ pub enum ExApiExecutionSupport { } /// Generic request used before protocol-specific execution models are introduced. -#[derive(Clone, Debug, Eq, PartialEq, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/api/executor/ExecutionRequest.ts" -)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct ExApiExecutionRequest { /// Target program id. pub program_id: crate::MdProgramId, @@ -36,11 +26,7 @@ pub struct ExApiExecutionRequest { } /// Neutral execution plan placeholder produced by executor crates. -#[derive(Clone, Debug, Eq, PartialEq, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/api/executor/ExecutionPlan.ts" -)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct ExApiExecutionPlan { /// Stable executor crate name. pub executor_name: std::string::String, diff --git a/ks-lib/src/executor/metadata/metaplex_token_metadata/intent.rs b/ks-lib/src/executor/metadata/metaplex_token_metadata/intent.rs index f62b4f4..8092cc3 100644 --- a/ks-lib/src/executor/metadata/metaplex_token_metadata/intent.rs +++ b/ks-lib/src/executor/metadata/metaplex_token_metadata/intent.rs @@ -1,10 +1,8 @@ // file: ks-lib/src/executor/metadata/metaplex_token_metadata/intent.rs -// version: 11 +// version: 12 //! Typed current and deprecated Metaplex Token Metadata execution intents. -use ts_rs::TS; // rust-rules: derive-import - /// Stable operation code for the current Create wrapper, discriminator 42. pub const EX_METAPLEX_TOKEN_METADATA_CREATE_OPERATION: &str = "metadata.metaplex_token_metadata.create"; @@ -164,12 +162,8 @@ pub const EX_METAPLEX_TOKEN_METADATA_SUPPORTED_OPERATION_CODES: &[&str] = &[ ]; /// One current or explicitly deprecated Metaplex Token Metadata operation. -#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] #[serde(tag = "operation", rename_all = "snake_case")] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/metadata/metaplex_token_metadata/intent/ExMetaplexTokenMetadataOperation.ts" -)] pub enum ExMetaplexTokenMetadataOperation { /// Build the current Create wrapper with `CreateArgs::V1`. Create { @@ -190,7 +184,6 @@ pub enum ExMetaplexTokenMetadataOperation { /// Optional SPL Token or Token-2022 program account. spl_token_program: std::option::Option, /// Official Metaplex create arguments. - #[ts(type = "unknown")] create_args: mpl_token_metadata::types::CreateArgs, }, /// Build the current Update wrapper as the update authority. @@ -210,7 +203,6 @@ pub enum ExMetaplexTokenMetadataOperation { /// Optional Token Authorization Rules account. authorization_rules: std::option::Option, /// Official Metaplex update arguments. Only `AsUpdateAuthorityV2` is accepted in this prerelease. - #[ts(type = "unknown")] update_args: mpl_token_metadata::types::UpdateArgs, }, /// Build the obsolete PuffMetadata instruction retained for compatibility workflows. @@ -252,7 +244,6 @@ pub enum ExMetaplexTokenMetadataOperation { /// Optional canonical collection master edition PDA. collection_master_edition: std::option::Option, /// Official unified verification variant. - #[ts(type = "unknown")] verification_args: mpl_token_metadata::types::VerificationArgs, }, /// Build the current unified Unverify wrapper for a creator or collection. @@ -268,7 +259,6 @@ pub enum ExMetaplexTokenMetadataOperation { /// Optional canonical collection metadata PDA. collection_metadata: std::option::Option, /// Official unified verification variant. - #[ts(type = "unknown")] verification_args: mpl_token_metadata::types::VerificationArgs, }, /// Build obsolete SetCollectionSize retained for compatibility workflows. @@ -302,7 +292,6 @@ pub enum ExMetaplexTokenMetadataOperation { master_edition_token: std::option::Option, edition_marker: std::option::Option, token_record: std::option::Option, - #[ts(type = "unknown")] burn_args: mpl_token_metadata::types::BurnArgs, }, #[allow(missing_docs)] @@ -319,7 +308,6 @@ pub enum ExMetaplexTokenMetadataOperation { spl_token_program: std::option::Option, authorization_rules_program: std::option::Option, authorization_rules: std::option::Option, - #[ts(type = "unknown")] delegate_args: mpl_token_metadata::types::DelegateArgs, }, #[allow(missing_docs)] @@ -336,7 +324,6 @@ pub enum ExMetaplexTokenMetadataOperation { spl_token_program: std::option::Option, authorization_rules_program: std::option::Option, authorization_rules: std::option::Option, - #[ts(type = "unknown")] revoke_args: mpl_token_metadata::types::RevokeArgs, }, #[allow(missing_docs)] @@ -352,7 +339,6 @@ pub enum ExMetaplexTokenMetadataOperation { spl_token_program: std::option::Option, authorization_rules_program: std::option::Option, authorization_rules: std::option::Option, - #[ts(type = "unknown")] lock_args: mpl_token_metadata::types::LockArgs, }, #[allow(missing_docs)] @@ -368,7 +354,6 @@ pub enum ExMetaplexTokenMetadataOperation { spl_token_program: std::option::Option, authorization_rules_program: std::option::Option, authorization_rules: std::option::Option, - #[ts(type = "unknown")] unlock_args: mpl_token_metadata::types::UnlockArgs, }, #[allow(missing_docs)] @@ -386,7 +371,6 @@ pub enum ExMetaplexTokenMetadataOperation { authority: crate::MdPubkey, authorization_rules_program: std::option::Option, authorization_rules: std::option::Option, - #[ts(type = "unknown")] transfer_args: mpl_token_metadata::types::TransferArgs, }, #[allow(missing_docs)] @@ -619,7 +603,6 @@ pub enum ExMetaplexTokenMetadataOperation { /// Account `rent` from the official IDL contract. rent: crate::MdPubkey, /// Official Metaplex instruction arguments. - #[ts(type = "unknown")] args: mpl_token_metadata::types::MintNewEditionFromMasterEditionViaTokenArgs, }, /// Build deprecated Utilize, retained for explicit compatibility workflows. @@ -651,7 +634,6 @@ pub enum ExMetaplexTokenMetadataOperation { /// Account `burner` from the official IDL contract. burner: crate::MdPubkey, /// Official Metaplex instruction arguments. - #[ts(type = "unknown")] args: mpl_token_metadata::instructions::UtilizeInstructionArgs, }, /// Build deprecated ApproveUseAuthority, retained for explicit compatibility workflows. @@ -683,7 +665,6 @@ pub enum ExMetaplexTokenMetadataOperation { /// Account `rent` from the official IDL contract. rent: crate::MdPubkey, /// Official Metaplex instruction arguments. - #[ts(type = "unknown")] args: mpl_token_metadata::instructions::ApproveUseAuthorityInstructionArgs, }, /// Build deprecated RevokeUseAuthority, retained for explicit compatibility workflows. @@ -780,7 +761,6 @@ pub enum ExMetaplexTokenMetadataOperation { /// Optional authority from the current SDK contract. authority: std::option::Option, /// Official Metaplex instruction arguments. - #[ts(type = "unknown")] args: mpl_token_metadata::instructions::TransferOutOfEscrowInstructionArgs, }, /// Build the current Mint instruction. @@ -816,7 +796,6 @@ pub enum ExMetaplexTokenMetadataOperation { /// Optional Token Authorization Rules account in the current SDK contract. authorization_rules: std::option::Option, /// Official Metaplex instruction arguments. - #[ts(type = "unknown")] args: mpl_token_metadata::types::MintArgs, }, /// Build the current Migrate instruction. @@ -879,7 +858,6 @@ pub enum ExMetaplexTokenMetadataOperation { /// Optional Token Authorization Rules account in the current SDK contract. authorization_rules: std::option::Option, /// Official Metaplex instruction arguments. - #[ts(type = "unknown")] args: mpl_token_metadata::types::UseArgs, }, /// Build the current Collect instruction. @@ -928,7 +906,6 @@ pub enum ExMetaplexTokenMetadataOperation { /// Account `systemProgram` from the official IDL contract. system_program: crate::MdPubkey, /// Official Metaplex instruction arguments. - #[ts(type = "unknown")] args: mpl_token_metadata::types::PrintArgs, }, /// Build the current Resize instruction. @@ -1027,11 +1004,7 @@ impl crate::ExMetaplexTokenMetadataOperation { } /// Complete typed intent accepted by the Metaplex Token Metadata executor. -#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/metadata/metaplex_token_metadata/intent/ExMetaplexTokenMetadataExecutionIntent.ts" -)] +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] pub struct ExMetaplexTokenMetadataExecutionIntent { /// Stable caller-provided identifier used for logs and replay correlation. pub intent_id: std::string::String, diff --git a/ks-lib/src/executor/metadata/solana_program_metadata/intent.rs b/ks-lib/src/executor/metadata/solana_program_metadata/intent.rs index 3811bdc..934fab1 100644 --- a/ks-lib/src/executor/metadata/solana_program_metadata/intent.rs +++ b/ks-lib/src/executor/metadata/solana_program_metadata/intent.rs @@ -1,18 +1,12 @@ // file: ks-lib/src/executor/metadata/solana_program_metadata/intent.rs -// version: 2 +// version: 3 //! Typed intents for the Solana Program Metadata executor. -use ts_rs::TS; // rust-rules: derive-import - /// Encoding written to a Solana Program Metadata account. -#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] #[repr(u8)] #[serde(rename_all = "snake_case")] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/metadata/solana_program_metadata/intent/ExMetadataSpmEncoding.ts" -)] pub enum ExMetadataSpmEncoding { /// No encoding declaration. None = 0, @@ -32,13 +26,9 @@ impl crate::ExMetadataSpmEncoding { } /// Compression written to a Solana Program Metadata account. -#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] #[repr(u8)] #[serde(rename_all = "snake_case")] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/metadata/solana_program_metadata/intent/ExMetadataSpmCompression.ts" -)] pub enum ExMetadataSpmCompression { /// No compression declaration. None = 0, @@ -56,13 +46,9 @@ impl crate::ExMetadataSpmCompression { } /// Structured format written to a Solana Program Metadata account. -#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] #[repr(u8)] #[serde(rename_all = "snake_case")] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/metadata/solana_program_metadata/intent/ExMetadataSpmFormat.ts" -)] pub enum ExMetadataSpmFormat { /// No structured format declaration. None = 0, @@ -82,13 +68,9 @@ impl crate::ExMetadataSpmFormat { } /// On-chain data source written to a Solana Program Metadata account. -#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] #[repr(u8)] #[serde(rename_all = "snake_case")] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/metadata/solana_program_metadata/intent/ExMetadataSpmDataSource.ts" -)] pub enum ExMetadataSpmDataSource { /// Direct on-chain bytes. Direct = 0, @@ -106,12 +88,8 @@ impl crate::ExMetadataSpmDataSource { } /// Exact inline content accepted by `Initialize` and `SetData`. -#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] #[serde(tag = "source", rename_all = "snake_case")] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/metadata/solana_program_metadata/intent/ExMetadataSpmDataInput.ts" -)] pub enum ExMetadataSpmDataInput { /// Direct on-chain bytes. Direct { @@ -148,12 +126,8 @@ impl crate::ExMetadataSpmDataInput { } /// Source used by the `Write` instruction. -#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] #[serde(tag = "source", rename_all = "snake_case")] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/metadata/solana_program_metadata/intent/ExMetadataSpmWriteSource.ts" -)] pub enum ExMetadataSpmWriteSource { /// Inline bytes placed after the offset argument. Inline { @@ -168,12 +142,8 @@ pub enum ExMetadataSpmWriteSource { } /// Source used by the `SetData` instruction. -#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] #[serde(tag = "source", rename_all = "snake_case")] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/metadata/solana_program_metadata/intent/ExMetadataSpmSetDataSource.ts" -)] pub enum ExMetadataSpmSetDataSource { /// Runtime-supported three-byte variant that preserves current data bytes. PreserveExisting, @@ -192,11 +162,7 @@ pub enum ExMetadataSpmSetDataSource { } /// Optional program-upgrade-authority validation context. -#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/metadata/solana_program_metadata/intent/ExMetadataSpmProgramContext.ts" -)] +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] pub struct ExMetadataSpmProgramContext { /// Executable program account. pub program: crate::MdPubkey, @@ -205,12 +171,8 @@ pub struct ExMetadataSpmProgramContext { } /// One exact Solana Program Metadata operation. -#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] #[serde(tag = "operation", rename_all = "snake_case")] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/metadata/solana_program_metadata/intent/ExMetadataSpmOperation.ts" -)] pub enum ExMetadataSpmOperation { /// Write bytes to a Buffer account. Write { @@ -452,11 +414,7 @@ impl crate::ExMetadataSpmOperation { } /// Complete Solana Program Metadata execution intent. -#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/metadata/solana_program_metadata/intent/ExMetadataSpmExecutionIntent.ts" -)] +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] pub struct ExMetadataSpmExecutionIntent { /// Caller-provided correlation identifier. pub intent_id: std::string::String, diff --git a/ks-lib/src/executor/safety/evaluation.rs b/ks-lib/src/executor/safety/evaluation.rs index c1064d2..9df990f 100644 --- a/ks-lib/src/executor/safety/evaluation.rs +++ b/ks-lib/src/executor/safety/evaluation.rs @@ -1,17 +1,11 @@ // file: ks-lib/src/executor/safety/evaluation.rs -// version: 4 +// version: 5 //! Safety checks applied before simulation, signing or sending. -use ts_rs::TS; // rust-rules: derive-import - /// Safety decision returned before continuing an execution stage. -#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] #[serde(rename_all = "snake_case")] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/safety/evaluation/ExSafetyDecision.ts" -)] pub enum ExSafetyDecision { /// The plan is not allowed to continue. Deny, @@ -22,11 +16,7 @@ pub enum ExSafetyDecision { } /// One stable safety policy violation. -#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/safety/evaluation/ExSafetyViolation.ts" -)] +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] pub struct ExSafetyViolation { /// Stable violation code. pub code: std::string::String, @@ -35,11 +25,7 @@ pub struct ExSafetyViolation { } /// Complete safety evaluation for one execution stage. -#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/safety/evaluation/ExSafetyEvaluation.ts" -)] +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] pub struct ExSafetyEvaluation { /// Aggregate decision. pub decision: crate::ExSafetyDecision, diff --git a/ks-lib/src/executor/solana/core/intent.rs b/ks-lib/src/executor/solana/core/intent.rs index dfe49a8..f6723d3 100644 --- a/ks-lib/src/executor/solana/core/intent.rs +++ b/ks-lib/src/executor/solana/core/intent.rs @@ -1,10 +1,8 @@ // file: ks-lib/src/executor/solana/core/intent.rs -// version: 3 +// version: 4 //! Typed Solana core execution intents. -use ts_rs::TS; // rust-rules: derive-import - /// Stable operation code for a System Program lamport transfer. pub const EX_SOLANA_CORE_SYSTEM_TRANSFER_OPERATION: &str = "solana.core.system.transfer"; /// Stable operation code for a bounded batch of System Program transfers. @@ -412,11 +410,7 @@ pub const EX_SOLANA_CORE_OPERATION_CODES: [&str; 109] = [ ]; /// One destination in a System Program multi-transfer. -#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/solana/core/intent/ExSolanaCoreSystemTransferRecipient.ts" -)] +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] pub struct ExSolanaCoreSystemTransferRecipient { /// Recipient account. pub to: crate::MdPubkey, @@ -425,11 +419,7 @@ pub struct ExSolanaCoreSystemTransferRecipient { } /// One Ed25519 signature verification offset record. -#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/solana/core/intent/ExSolanaCoreEd25519VerificationOffsets.ts" -)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] pub struct ExSolanaCoreEd25519VerificationOffsets { /// Offset to the 64-byte signature. pub signature_offset: u16, @@ -448,11 +438,7 @@ pub struct ExSolanaCoreEd25519VerificationOffsets { } /// One secp256k1 signature verification offset record. -#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/solana/core/intent/ExSolanaCoreSecp256k1VerificationOffsets.ts" -)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] pub struct ExSolanaCoreSecp256k1VerificationOffsets { /// Offset to the 64-byte signature followed by its one-byte recovery id. pub signature_offset: u16, @@ -471,11 +457,7 @@ pub struct ExSolanaCoreSecp256k1VerificationOffsets { } /// One secp256r1 signature verification offset record. -#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/solana/core/intent/ExSolanaCoreSecp256r1VerificationOffsets.ts" -)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] pub struct ExSolanaCoreSecp256r1VerificationOffsets { /// Offset to the compact 64-byte signature. pub signature_offset: u16, @@ -494,12 +476,8 @@ pub struct ExSolanaCoreSecp256r1VerificationOffsets { } /// Stake authority role changed by an authorize instruction. -#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] #[serde(rename_all = "snake_case")] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/solana/core/intent/ExSolanaCoreStakeAuthorizationKind.ts" -)] pub enum ExSolanaCoreStakeAuthorizationKind { /// Staker authority controlling delegation and stake movement. Staker, @@ -517,12 +495,8 @@ impl crate::ExSolanaCoreStakeAuthorizationKind { } /// Vote authority role changed by an authorize instruction. -#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] #[serde(tag = "kind", rename_all = "snake_case")] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/solana/core/intent/ExSolanaCoreVoteAuthorizationKind.ts" -)] pub enum ExSolanaCoreVoteAuthorizationKind { /// Vote authority using an Ed25519 public key. Voter, @@ -538,12 +512,8 @@ pub enum ExSolanaCoreVoteAuthorizationKind { } /// Vote v2 commission category. -#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] #[serde(rename_all = "snake_case")] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/solana/core/intent/ExSolanaCoreVoteCommissionKind.ts" -)] pub enum ExSolanaCoreVoteCommissionKind { /// Inflation-reward commission. InflationRewards, @@ -563,11 +533,7 @@ impl crate::ExSolanaCoreVoteCommissionKind { } /// One lockout entry used by Vote-state and tower synchronization instructions. -#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/solana/core/intent/ExSolanaCoreVoteLockout.ts" -)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] pub struct ExSolanaCoreVoteLockout { /// Voted slot. pub slot: u64, @@ -576,11 +542,7 @@ pub struct ExSolanaCoreVoteLockout { } /// One key entry serialized in the Config Program `ConfigKeys` short vector. -#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/solana/core/intent/ExSolanaCoreConfigKey.ts" -)] +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] pub struct ExSolanaCoreConfigKey { /// Public key stored in the Config Program key list. pub pubkey: crate::MdPubkey, @@ -589,11 +551,7 @@ pub struct ExSolanaCoreConfigKey { } /// Optional context-state accounts attached to a ZK ElGamal proof verification. -#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/solana/core/intent/ExSolanaCoreZkElGamalContextState.ts" -)] +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] pub struct ExSolanaCoreZkElGamalContextState { /// Preallocated writable context-state account. pub account: crate::MdPubkey, @@ -602,12 +560,8 @@ pub struct ExSolanaCoreZkElGamalContextState { } /// Official proof kinds accepted by the native ZK ElGamal Proof program. -#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] #[serde(rename_all = "snake_case")] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/solana/core/intent/ExSolanaCoreZkElGamalProofType.ts" -)] pub enum ExSolanaCoreZkElGamalProofType { /// Zero-ciphertext proof. ZeroCiphertext, @@ -732,12 +686,8 @@ impl crate::ExSolanaCoreZkElGamalProofType { } /// Typed Solana core operation arguments. -#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] #[serde(tag = "operation", rename_all = "snake_case")] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/solana/core/intent/ExSolanaCoreOperation.ts" -)] pub enum ExSolanaCoreOperation { /// Transfer lamports between System Program accounts. SystemTransfer { @@ -2449,11 +2399,7 @@ impl crate::ExSolanaCoreOperation { } /// Complete typed intent accepted by the Solana core executor. -#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/solana/core/intent/ExSolanaCoreExecutionIntent.ts" -)] +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] pub struct ExSolanaCoreExecutionIntent { /// Stable caller-provided identifier used for logs and replay correlation. pub intent_id: std::string::String, diff --git a/ks-lib/src/executor/spl/associated_token_account/intent.rs b/ks-lib/src/executor/spl/associated_token_account/intent.rs index 9fd9b8d..04bd3e8 100644 --- a/ks-lib/src/executor/spl/associated_token_account/intent.rs +++ b/ks-lib/src/executor/spl/associated_token_account/intent.rs @@ -1,10 +1,8 @@ // file: ks-lib/src/executor/spl/associated_token_account/intent.rs -// version: 6 +// version: 7 //! Typed SPL Associated Token Account execution intents. -use ts_rs::TS; // rust-rules: derive-import - /// Stable operation code for strict ATA creation. pub const EX_SPL_ATA_CREATE_OPERATION: &str = "spl.associated_token_account.create"; /// Stable operation code for idempotent ATA creation or reuse. @@ -20,18 +18,13 @@ pub const EX_SPL_ATA_SUPPORTED_OPERATION_CODES: &[&str] = &[ ]; /// Exact Token Program targeted by ATA derivation and runtime CPIs. -#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] #[serde(rename_all = "snake_case")] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/spl/associated_token_account/intent/ExSplAssociatedTokenProgram.ts" -)] pub enum ExSplAssociatedTokenProgram { /// Classic SPL Token program. Classic, /// Token-2022 program without extension-specific executor semantics. #[serde(rename = "token_2022")] - #[ts(rename = "token_2022")] Token2022, } @@ -46,12 +39,8 @@ impl crate::ExSplAssociatedTokenProgram { } /// One officially constructible ATA operation. -#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] #[serde(tag = "operation", rename_all = "snake_case")] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/spl/associated_token_account/intent/ExSplAssociatedTokenAccountOperation.ts" -)] pub enum ExSplAssociatedTokenAccountOperation { /// Strictly create an absent canonical ATA. Create { @@ -109,11 +98,7 @@ impl crate::ExSplAssociatedTokenAccountOperation { } /// Complete typed intent accepted by the ATA executor. -#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/spl/associated_token_account/intent/ExSplAssociatedTokenAccountExecutionIntent.ts" -)] +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] pub struct ExSplAssociatedTokenAccountExecutionIntent { /// Stable caller-provided identifier used for logs and replay correlation. pub intent_id: std::string::String, diff --git a/ks-lib/src/executor/spl/elgamal_registry/intent.rs b/ks-lib/src/executor/spl/elgamal_registry/intent.rs index 1fc888e..0f6cb1c 100644 --- a/ks-lib/src/executor/spl/elgamal_registry/intent.rs +++ b/ks-lib/src/executor/spl/elgamal_registry/intent.rs @@ -1,22 +1,16 @@ // file: ks-lib/src/executor/spl/elgamal_registry/intent.rs -// version: 4 +// version: 5 //! Typed intents for the SPL ElGamal public-key registry. -use ts_rs::TS; // rust-rules: derive-import - /// Stable operation code for registry creation. pub const EX_SPL_ELGAMAL_REGISTRY_CREATE_OPERATION: &str = "spl.elgamal_registry.create_registry"; /// Stable operation code for registry update. pub const EX_SPL_ELGAMAL_REGISTRY_UPDATE_OPERATION: &str = "spl.elgamal_registry.update_registry"; /// Exact location of the public-key validity proof. -#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] #[serde(tag = "location", rename_all = "snake_case")] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/spl/elgamal_registry/intent/ExSplElgamalRegistryProofLocation.ts" -)] pub enum ExSplElgamalRegistryProofLocation { /// A pre-verified proof context-state account. ContextStateAccount { @@ -31,12 +25,8 @@ pub enum ExSplElgamalRegistryProofLocation { } /// Typed registry operation. -#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] #[serde(tag = "operation", rename_all = "snake_case")] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/spl/elgamal_registry/intent/ExSplElgamalRegistryOperation.ts" -)] pub enum ExSplElgamalRegistryOperation { /// Create the deterministic registry PDA for one owner. CreateRegistry { @@ -69,11 +59,7 @@ impl crate::ExSplElgamalRegistryOperation { } /// Complete typed execution intent. -#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/spl/elgamal_registry/intent/ExSplElgamalRegistryExecutionIntent.ts" -)] +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] pub struct ExSplElgamalRegistryExecutionIntent { /// Caller-provided correlation identifier. pub intent_id: std::string::String, diff --git a/ks-lib/src/executor/spl/memo/intent.rs b/ks-lib/src/executor/spl/memo/intent.rs index ff5bb09..bb65dd2 100644 --- a/ks-lib/src/executor/spl/memo/intent.rs +++ b/ks-lib/src/executor/spl/memo/intent.rs @@ -1,10 +1,8 @@ // file: ks-lib/src/executor/spl/memo/intent.rs -// version: 3 +// version: 4 //! Typed SPL Memo execution intents. -use ts_rs::TS; // rust-rules: derive-import - /// Stable operation code for adding one SPL Memo annotation. pub const EX_SPL_MEMO_ADD_MEMO_OPERATION: &str = "spl.memo.add_memo"; /// Conservative UTF-8 payload bound used before transaction assembly. @@ -13,12 +11,8 @@ pub const EX_SPL_MEMO_MAX_MESSAGE_BYTES: usize = 566; pub const EX_SPL_MEMO_MAX_SIGNERS: usize = 32; /// Exact SPL Memo program generation. -#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] #[serde(rename_all = "snake_case")] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/spl/memo/intent/ExSplMemoGeneration.ts" -)] pub enum ExSplMemoGeneration { /// Historical v1 program, whose runtime ignores supplied accounts. V1, @@ -49,23 +43,15 @@ impl crate::ExSplMemoGeneration { } /// One ordered signer account supplied to the Memo instruction. -#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/spl/memo/intent/ExSplMemoSigner.ts" -)] +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] pub struct ExSplMemoSigner { /// Signer public key; duplicates are preserved in instruction order. pub pubkey: crate::MdPubkey, } /// Typed SPL Memo operation arguments. -#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] #[serde(tag = "operation", rename_all = "snake_case")] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/spl/memo/intent/ExSplMemoOperation.ts" -)] pub enum ExSplMemoOperation { /// Add one exact UTF-8 Memo payload with ordered readonly signer accounts. AddMemo { @@ -95,11 +81,7 @@ impl crate::ExSplMemoOperation { } /// Complete typed intent accepted by the SPL Memo executor. -#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/spl/memo/intent/ExSplMemoExecutionIntent.ts" -)] +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] pub struct ExSplMemoExecutionIntent { /// Stable caller-provided identifier used for logs and replay correlation. pub intent_id: std::string::String, diff --git a/ks-lib/src/executor/spl/token/intent.rs b/ks-lib/src/executor/spl/token/intent.rs index 6b6d14f..99740c2 100644 --- a/ks-lib/src/executor/spl/token/intent.rs +++ b/ks-lib/src/executor/spl/token/intent.rs @@ -1,10 +1,8 @@ // file: ks-lib/src/executor/spl/token/intent.rs -// version: 6 +// version: 7 //! Typed classic SPL Token execution intents. -use ts_rs::TS; // rust-rules: derive-import - /// Stable operation code for mint initialization through `InitializeMint2`. pub const EX_SPL_TOKEN_INITIALIZE_MINT_OPERATION: &str = "spl.token.initialize_mint"; /// Stable operation code for token-account initialization through `InitializeAccount3`. @@ -85,22 +83,14 @@ pub const EX_SPL_TOKEN_SUPPORTED_OPERATION_CODES: &[&str] = &[ ]; /// Exact unsigned on-chain amount represented as a decimal JSON string. -#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/spl/token/intent/ExSplClassicTokenAmount.ts" -)] +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] pub struct ExSplClassicTokenAmount( /// Canonical unsigned decimal representation. pub std::string::String, ); /// Ordered simple or multisig authority supplied to an instruction builder. -#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/spl/token/intent/ExSplClassicTokenAuthority.ts" -)] +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] pub struct ExSplClassicTokenAuthority { /// Authority account. It signs only when `multisig_signers` is empty. pub authority: crate::MdPubkey, @@ -109,12 +99,8 @@ pub struct ExSplClassicTokenAuthority { } /// Authority domain used by `SetAuthority`. -#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] #[serde(rename_all = "snake_case")] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/spl/token/intent/ExSplClassicTokenAuthorityType.ts" -)] pub enum ExSplClassicTokenAuthorityType { /// Mint authority. MintTokens, @@ -127,12 +113,8 @@ pub enum ExSplClassicTokenAuthorityType { } /// One officially constructible non-batch instruction. -#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] #[serde(tag = "instruction", rename_all = "snake_case")] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/spl/token/intent/ExSplClassicTokenSingleOperation.ts" -)] pub enum ExSplClassicTokenSingleOperation { /// Initialize a mint with the current no-Rent builder. InitializeMint { @@ -407,12 +389,8 @@ impl crate::ExSplClassicTokenSingleOperation { } /// Top-level SPL Token operation, including the recent bounded Batch builder. -#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] #[serde(tag = "operation", rename_all = "snake_case")] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/spl/token/intent/ExSplClassicTokenOperation.ts" -)] pub enum ExSplClassicTokenOperation { /// Build one non-batch instruction. Instruction { @@ -446,11 +424,7 @@ impl crate::ExSplClassicTokenOperation { } /// Complete typed intent accepted by the classic SPL Token executor. -#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/spl/token/intent/ExSplClassicTokenExecutionIntent.ts" -)] +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] pub struct ExSplClassicTokenExecutionIntent { /// Stable caller-provided identifier used for logs and replay correlation. pub intent_id: std::string::String, diff --git a/ks-lib/src/executor/spl/token_2022/confidential.rs b/ks-lib/src/executor/spl/token_2022/confidential.rs index dfcfb35..8f2357d 100644 --- a/ks-lib/src/executor/spl/token_2022/confidential.rs +++ b/ks-lib/src/executor/spl/token_2022/confidential.rs @@ -1,10 +1,8 @@ // file: ks-lib/src/executor/spl/token_2022/confidential.rs -// version: 10 +// version: 11 //! Audited Confidential Transfer execution contracts. -use ts_rs::TS; // rust-rules: derive-import - /// Maximum number of proof references accepted by one confidential operation. pub const EX_SPL_TOKEN_2022_MAX_CONFIDENTIAL_PROOF_REFERENCES: usize = 5; /// Exact byte length of an ElGamal public key accepted from callers. @@ -27,11 +25,7 @@ pub const EX_SPL_TOKEN_2022_CONFIDENTIAL_TRANSFER_WITH_FEE_INSTRUCTION_DATA_BYTE 2 + EX_SPL_TOKEN_2022_CONFIDENTIAL_TRANSFER_WITH_FEE_PAYLOAD_BYTES; /// Caller-generated ElGamal public key encoded as lowercase or uppercase hexadecimal. -#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/spl/token_2022/confidential/ExSplTokenElGamalPubkey.ts" -)] +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] pub struct ExSplTokenElGamalPubkey(pub std::string::String); impl ExSplTokenElGamalPubkey { @@ -56,11 +50,7 @@ impl ExSplTokenElGamalPubkey { } /// Caller-generated decryptable balance encoded as hexadecimal bytes. -#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/spl/token_2022/confidential/ExSplTokenDecryptableBalance.ts" -)] +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] pub struct ExSplTokenDecryptableBalance(pub std::string::String); impl ExSplTokenDecryptableBalance { @@ -86,11 +76,7 @@ impl ExSplTokenDecryptableBalance { } /// Caller-generated ElGamal ciphertext encoded as hexadecimal bytes. -#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/spl/token_2022/confidential/ExSplTokenElGamalCiphertext.ts" -)] +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] pub struct ExSplTokenElGamalCiphertext(pub std::string::String); impl ExSplTokenElGamalCiphertext { @@ -153,12 +139,8 @@ fn decode_hex_nibble(value: u8) -> Option { } /// Proof statement required by a Confidential Transfer operation. -#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] #[serde(rename_all = "snake_case")] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/spl/token_2022/confidential/ExSplTokenConfidentialProofKind.ts" -)] pub enum ExSplTokenConfidentialProofKind { /// ElGamal public-key validity. PubkeyValidity, @@ -183,12 +165,8 @@ pub enum ExSplTokenConfidentialProofKind { } /// Explicit source for one already-generated zero-knowledge proof statement. -#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] #[serde(rename_all = "snake_case", tag = "mode")] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/spl/token_2022/confidential/ExSplTokenConfidentialProofLocation.ts" -)] pub enum ExSplTokenConfidentialProofLocation { /// Relative transaction offset of a separate ZK proof-program instruction. InstructionOffset { @@ -216,12 +194,8 @@ impl ExSplTokenConfidentialProofLocation { } /// One proof requirement paired with its explicit location. -#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] #[serde(rename_all = "camelCase")] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/spl/token_2022/confidential/ExSplTokenConfidentialProofReference.ts" -)] pub struct ExSplTokenConfidentialProofReference { /// Exact proof statement required by the Token-2022 builder. pub kind: ExSplTokenConfidentialProofKind, @@ -230,12 +204,8 @@ pub struct ExSplTokenConfidentialProofReference { } /// Audited Confidential Transfer operation surface. -#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] #[serde(rename_all = "snake_case")] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/spl/token_2022/confidential/ExSplTokenConfidentialOperation.ts" -)] pub enum ExSplTokenConfidentialOperation { /// Initialize confidential-transfer mint configuration. InitializeMint, @@ -332,12 +302,8 @@ impl ExSplTokenConfidentialOperation { } /// Audited Confidential Mint/Burn operation surface. -#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] #[serde(rename_all = "snake_case")] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/spl/token_2022/confidential/ExSplTokenConfidentialMintBurnOperation.ts" -)] pub enum ExSplTokenConfidentialMintBurnOperation { /// Initialize the mint extension with supply encryption material. InitializeMint, @@ -395,12 +361,8 @@ impl ExSplTokenConfidentialMintBurnOperation { } /// Executor-readiness classification established by the Confidential Transfer audit. -#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] #[serde(rename_all = "snake_case")] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/spl/token_2022/confidential/ExSplTokenConfidentialExecutorSupport.ts" -)] pub enum ExSplTokenConfidentialExecutorSupport { /// The builder needs only public fields and ordinary authorities. PublicBuilderReady, diff --git a/ks-lib/src/executor/spl/token_2022/intent.rs b/ks-lib/src/executor/spl/token_2022/intent.rs index 040fa70..11923eb 100644 --- a/ks-lib/src/executor/spl/token_2022/intent.rs +++ b/ks-lib/src/executor/spl/token_2022/intent.rs @@ -1,10 +1,8 @@ // file: ks-lib/src/executor/spl/token_2022/intent.rs -// version: 28 +// version: 29 //! Typed Token-2022 execution intents. -use ts_rs::TS; // rust-rules: derive-import - /// Stable operation code for mint initialization through `InitializeMint2`. pub const EX_SPL_TOKEN_2022_INITIALIZE_MINT_OPERATION: &str = "spl.token_2022.initialize_mint"; /// Stable operation code for token-account initialization through `InitializeAccount3`. @@ -357,33 +355,21 @@ pub const EX_SPL_TOKEN_2022_SUPPORTED_OPERATION_CODES: &[&str] = &[ ]; /// Exact unsigned on-chain amount represented as a decimal JSON string. -#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/spl/token_2022/intent/ExSplTokenAmount.ts" -)] +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] pub struct ExSplTokenAmount( /// Canonical unsigned decimal representation. pub std::string::String, ); /// Positive normal finite scaled-UI multiplier represented as a decimal JSON string. -#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/spl/token_2022/intent/ExSplTokenMultiplier.ts" -)] +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] pub struct ExSplTokenMultiplier( /// Decimal representation parsed to the official `f64` wire contract. pub std::string::String, ); /// Ordered simple or multisig authority supplied to an instruction builder. -#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/spl/token_2022/intent/ExSplTokenAuthority.ts" -)] +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] pub struct ExSplTokenAuthority { /// Authority account. It signs only when `multisig_signers` is empty. pub authority: crate::MdPubkey, @@ -392,12 +378,8 @@ pub struct ExSplTokenAuthority { } /// Authority domain used by `SetAuthority`. -#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] #[serde(rename_all = "snake_case")] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/spl/token_2022/intent/ExSplTokenAuthorityType.ts" -)] pub enum ExSplTokenAuthorityType { /// Mint authority. MintTokens, @@ -410,12 +392,8 @@ pub enum ExSplTokenAuthorityType { } /// Default state assigned to newly initialized Token-2022 accounts. -#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] #[serde(rename_all = "snake_case")] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/spl/token_2022/intent/ExSplTokenDefaultAccountState.ts" -)] pub enum ExSplTokenDefaultAccountState { /// Newly initialized accounts remain usable. Initialized, @@ -424,12 +402,8 @@ pub enum ExSplTokenDefaultAccountState { } /// Token-metadata field selected for an update. -#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] #[serde(rename_all = "snake_case", tag = "kind", content = "key")] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/spl/token_2022/intent/ExSplTokenMetadataField.ts" -)] pub enum ExSplTokenMetadataField { /// Update the required name field. Name, @@ -442,12 +416,8 @@ pub enum ExSplTokenMetadataField { } /// One officially constructible non-batch instruction. -#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] #[serde(tag = "instruction", rename_all = "snake_case")] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/spl/token_2022/intent/ExSplTokenSingleOperation.ts" -)] pub enum ExSplTokenSingleOperation { /// Initialize a mint with the current no-Rent builder. InitializeMint { @@ -1605,12 +1575,8 @@ impl crate::ExSplTokenSingleOperation { } /// Top-level SPL Token operation, including the recent bounded Batch builder. -#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] #[serde(tag = "operation", rename_all = "snake_case")] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/spl/token_2022/intent/ExSplToken2022Operation.ts" -)] pub enum ExSplToken2022Operation { /// Build one non-batch instruction. Instruction { @@ -1644,11 +1610,7 @@ impl crate::ExSplToken2022Operation { } /// Complete typed intent accepted by the Token-2022 executor. -#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/executor/spl/token_2022/intent/ExSplToken2022ExecutionIntent.ts" -)] +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] pub struct ExSplToken2022ExecutionIntent { /// Stable caller-provided identifier used for logs and replay correlation. pub intent_id: std::string::String, diff --git a/ks-lib/src/model/decoded.rs b/ks-lib/src/model/decoded.rs index 89f6f99..9259f00 100644 --- a/ks-lib/src/model/decoded.rs +++ b/ks-lib/src/model/decoded.rs @@ -1,16 +1,10 @@ // file: ks-lib/src/model/decoded.rs -// version: 6 +// version: 7 //! Shared decoded protocol event model. -use ts_rs::TS; // rust-rules: derive-import - /// Source from which a decoded event was derived. -#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/model/decoded/EventSourceKind.ts" -)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] pub enum MdEventSourceKind { /// Top-level instruction. Instruction, @@ -33,11 +27,7 @@ pub enum MdEventSourceKind { } /// Confidence level for a decoded event. -#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/model/decoded/DecoderConfidence.ts" -)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] pub enum MdDecoderConfidence { /// Exact manual decode. Exact, @@ -56,11 +46,7 @@ pub enum MdDecoderConfidence { } /// Protocol-level decoded event produced by a decoder. -#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/model/decoded/DecodedProtocolEvent.ts" -)] +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] pub struct MdDecodedProtocolEvent { /// Transaction signature. pub signature: crate::MdSignature, diff --git a/ks-lib/src/model/materialized.rs b/ks-lib/src/model/materialized.rs index 9ffa6d3..8a9e8a6 100644 --- a/ks-lib/src/model/materialized.rs +++ b/ks-lib/src/model/materialized.rs @@ -1,16 +1,10 @@ // file: ks-lib/src/model/materialized.rs -// version: 7 +// version: 8 //! Shared materialized business event model. -use ts_rs::TS; // rust-rules: derive-import - /// Materialized event family. -#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/model/materialized/MaterializedEventFamily.ts" -)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] pub enum MdMaterializedEventFamily { /// Trade materialization. Trade, @@ -63,11 +57,7 @@ pub enum MdMaterializedEventFamily { } /// Business-level materialized event placeholder. -#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/model/materialized/MaterializedEvent.ts" -)] +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] pub struct MdMaterializedEvent { /// Source transaction signature. pub signature: crate::MdSignature, diff --git a/ks-lib/src/model/nomenclature.rs b/ks-lib/src/model/nomenclature.rs index 2934e85..b192dcd 100644 --- a/ks-lib/src/model/nomenclature.rs +++ b/ks-lib/src/model/nomenclature.rs @@ -1,56 +1,30 @@ // file: ks-lib/src/model/nomenclature.rs -// version: 6 +// version: 7 //! Shared protocol, surface, and event nomenclature types. -use ts_rs::TS; // rust-rules: derive-import - /// Stable internal program code. -#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/model/nomenclature/ProgramCode.ts" -)] +#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize)] pub struct MdProgramCode(pub std::string::String); /// Protocol family code, for example `raydium` or `meteora`. -#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/model/nomenclature/ProtocolCode.ts" -)] +#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize)] pub struct MdProtocolCode(pub std::string::String); /// Concrete protocol surface code, for example `raydium_amm_v4`. -#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/model/nomenclature/SurfaceCode.ts" -)] +#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize)] pub struct MdSurfaceCode(pub std::string::String); /// Event name without surface prefix. -#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/model/nomenclature/EventName.ts" -)] +#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize)] pub struct MdEventName(pub std::string::String); /// Canonical event code in `.` format. -#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/model/nomenclature/EventCode.ts" -)] +#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize)] pub struct MdEventCode(pub std::string::String); /// High-level event family. -#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/model/nomenclature/EventFamily.ts" -)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize)] pub enum MdEventFamily { /// Swap or buy/sell event. Trade, diff --git a/ks-lib/src/model/observation.rs b/ks-lib/src/model/observation.rs index 97f5f4d..451df4a 100644 --- a/ks-lib/src/model/observation.rs +++ b/ks-lib/src/model/observation.rs @@ -1,16 +1,10 @@ // file: ks-lib/src/model/observation.rs -// version: 6 +// version: 7 //! Shared program observation model. -use ts_rs::TS; // rust-rules: derive-import - /// Generic program observation derived from a Solana instruction. -#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/model/observation/ProgramObservation.ts" -)] +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] pub struct MdProgramObservation { /// Transaction signature. pub signature: crate::MdSignature, diff --git a/ks-lib/src/model/solana.rs b/ks-lib/src/model/solana.rs index 068950d..35a5c40 100644 --- a/ks-lib/src/model/solana.rs +++ b/ks-lib/src/model/solana.rs @@ -1,46 +1,26 @@ // file: ks-lib/src/model/solana.rs -// version: 6 +// version: 7 //! Shared Solana primitive wrapper types. -use ts_rs::TS; // rust-rules: derive-import - /// Solana transaction signature. -#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize, TS)] -#[ts(export, export_to = "../frontend/ts/bindings/ks_lib/model/solana/MdSignature.ts")] +#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize)] pub struct MdSignature(pub std::string::String); /// Solana slot. #[derive( - Clone, - Copy, - Debug, - Eq, - PartialEq, - Ord, - PartialOrd, - Hash, - serde::Deserialize, - serde::Serialize, - TS, + Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, serde::Deserialize, serde::Serialize, )] -#[ts(export, export_to = "../frontend/ts/bindings/ks_lib/model/solana/MdSlot.ts")] pub struct MdSlot(pub u64); /// Solana program id. -#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize, TS)] -#[ts(export, export_to = "../frontend/ts/bindings/ks_lib/model/solana/MdProgramId.ts")] +#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize)] pub struct MdProgramId(pub std::string::String); /// Solana public key. -#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize, TS)] -#[ts(export, export_to = "../frontend/ts/bindings/ks_lib/model/solana/MdPubkey.ts")] +#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize)] pub struct MdPubkey(pub std::string::String); /// Stable top-level or inner instruction path. -#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize, TS)] -#[ts( - export, - export_to = "../frontend/ts/bindings/ks_lib/model/solana/MdInstructionPath.ts" -)] +#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize)] pub struct MdInstructionPath(pub std::string::String); diff --git a/ks-logging/CHANGELOG.md b/ks-logging/CHANGELOG.md index 8883b9e..bd35128 100644 --- a/ks-logging/CHANGELOG.md +++ b/ks-logging/CHANGELOG.md @@ -1,8 +1,14 @@ - + # CHANGELOG — ks-logging +## `0.5.1-pre.008` + +- Fix: remplace un ancien fragment de credential Helius utilisé comme canari négatif par le nom synthétique `KS_SECRET_HELIUS_API_KEY`; aucune donnée réelle ou partielle de credential ne doit être conservée dans les tests. + +- clarifie que la non-divulgation des secrets de configuration est garantie à la source et aux frontières public/diagnostic ; conserve comme défense en profondeur future l'évaluation d'un redactor générique pour des chaînes arbitraires fournies par les appelants. + ## `0.5.1-pre.007` - place `logs_directory` hors profils dans le document logging et le rend configurable via `${KS_LOGS_DIRECTORY:-logs}` ; diff --git a/ks-logging/TODO.md b/ks-logging/TODO.md index afe4a06..70981a7 100644 --- a/ks-logging/TODO.md +++ b/ks-logging/TODO.md @@ -1,12 +1,11 @@ - + # TODO — ks-logging ## Série `0.5.x` -- [ ] `0.5.1` - intégrer la propagation de sensibilité et le camouflage des valeurs provenant de `KS_SECRET_*` / `KB_SECRET_*` avant tout diagnostic ou log. - [ ] Compatibilité - préserver targets, niveaux, filtres, formats, routes et sémantique wildcard pendant les prochaines migrations. - [ ] Contrat - réévaluer `LogFileRoute`, actuellement sans consommateur actif hors de la crate. -- [ ] Tests - ajouter les canaris garantissant qu’un secret résolu n’atteint jamais une sortie logging. +- [ ] Défense en profondeur - évaluer ultérieurement un filtre de sortie générique pour les secrets fournis arbitrairement par les appelants, sans remplacer l’interdiction de journaliser ces valeurs à la source. - [ ] Documentation - maintenir le schéma et les exemples logging alignés avec les types possédés par la crate. diff --git a/ks-logging/src/document.rs b/ks-logging/src/document.rs index cec6970..ecd8335 100644 --- a/ks-logging/src/document.rs +++ b/ks-logging/src/document.rs @@ -1,5 +1,5 @@ // file: ks-logging/src/document.rs -// version: 5 +// version: 6 //! Independent logging configuration document loading and validation. @@ -351,7 +351,8 @@ fn require_non_empty(value: &str, field_name: &str) -> ks_core::Result<()> { #[cfg(test)] mod tests { const DEFAULT_LOGGING_CONFIG: &str = include_str!("../../config/logging.config.json"); - const EXAMPLE_LOGGING_CONFIG: &str = include_str!("../../config/example.logging.config.json"); + const EXAMPLE_LOGGING_CONFIG: &str = + include_str!("../../config/exemples/example.logging.config.json"); fn parse_default_value() -> serde_json::Value { let result = serde_json::from_str::(DEFAULT_LOGGING_CONFIG); @@ -522,7 +523,7 @@ mod tests { #[test] fn default_logging_config_uses_canonical_wallet_and_pipeline_routes() { - assert!(!DEFAULT_LOGGING_CONFIG.contains("api-key=95e73621")); + assert!(!DEFAULT_LOGGING_CONFIG.contains("KS_SECRET_HELIUS_API_KEY")); assert!(!DEFAULT_LOGGING_CONFIG.contains("\"ks_wallet\"")); assert!(DEFAULT_LOGGING_CONFIG.contains("\"ks-wallet\"")); assert!(DEFAULT_LOGGING_CONFIG.contains("\"ks-pipeline\"")); diff --git a/ks-onchain-transport/CHANGELOG.md b/ks-onchain-transport/CHANGELOG.md index 021753f..cf02b35 100644 --- a/ks-onchain-transport/CHANGELOG.md +++ b/ks-onchain-transport/CHANGELOG.md @@ -1,8 +1,15 @@ - + # CHANGELOG — ks-onchain-transport +## `0.5.1-pre.008` + +- Fix: retire complètement les URLs résolues des snapshots HTTP/WS/session, conserve ces snapshots backend-only et non sérialisables directement, sanitise le `Debug` de `WsSession`/`RpcEndpoint`, et ne recopie plus les erreurs brutes de connexion HTTP/WS susceptibles d’inclure une URL résolue. Les corps HTTP non-success et messages JSON-RPC distants ne sont plus recopiés dans les erreurs/logs afin qu’un fournisseur ne puisse pas y réinjecter une URL ou un credential. Le getter d’URL HTTP inutilisé est supprimé et le getter WS nécessaire à la session devient `pub(crate)`. + +- remplace les dérivations `Debug` de `HttpClient` et `WsClient` par des implémentations manuelles sanitisées qui ne recopient aucune chaîne issue de la configuration d’endpoint et utilisent un marqueur `` ; +- conserve ainsi `Debug` sur les clients et pools sans réintroduire `Debug` sur `ks_config::HttpEndpointConfig` / `WsEndpointConfig`, et ajoute des tests canaris HTTP/WS de non-divulgation. + ## `0.5.1-pre.007` - aligne les fixtures d’exécution sur le déplacement des autorisations `*_send_enabled` vers `ExecutionConfig` ; le comportement RPC reste inchangé. diff --git a/ks-onchain-transport/src/client.rs b/ks-onchain-transport/src/client.rs index 5f7ee07..473916f 100644 --- a/ks-onchain-transport/src/client.rs +++ b/ks-onchain-transport/src/client.rs @@ -1,10 +1,10 @@ // file: ks-onchain-transport/src/client.rs -// version: 4 +// version: 5 //! RPC client scaffold for Solana ingestion. /// RPC endpoint configuration. -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Eq, PartialEq)] pub struct RpcEndpoint { /// HTTP RPC URL. pub http_url: std::string::String, @@ -12,6 +12,17 @@ pub struct RpcEndpoint { pub ws_url: std::option::Option, } +impl std::fmt::Debug for crate::RpcEndpoint { + fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let ws_state = if self.ws_url.is_some() { "configured" } else { "missing" }; + return formatter + .debug_struct("RpcEndpoint") + .field("http_url", &"") + .field("ws_url", &ws_state) + .finish(); + } +} + /// Minimal Solana RPC client abstraction. pub trait SolanaRpcClient { /// Fetches a raw transaction payload by signature. @@ -20,3 +31,22 @@ pub trait SolanaRpcClient { signature: &ks_lib::MdSignature, ) -> ks_core::Result>; } + +#[cfg(test)] +mod tests { + #[test] + fn rpc_endpoint_debug_omits_resolved_urls() { + let endpoint = crate::RpcEndpoint { + http_url: "https://HTTP-RPC-SECRET-CANARY.invalid/?api-key=secret".to_string(), + ws_url: std::option::Option::Some( + "wss://WS-RPC-SECRET-CANARY.invalid/?api-key=secret".to_string(), + ), + }; + let rendered = format!("{endpoint:?}"); + assert!(rendered.contains("")); + assert!(rendered.contains("configured")); + assert!(!rendered.contains("HTTP-RPC-SECRET-CANARY")); + assert!(!rendered.contains("WS-RPC-SECRET-CANARY")); + assert!(!rendered.contains("api-key=secret")); + } +} diff --git a/ks-onchain-transport/src/http_client.rs b/ks-onchain-transport/src/http_client.rs index 079f8d1..eddfa54 100644 --- a/ks-onchain-transport/src/http_client.rs +++ b/ks-onchain-transport/src/http_client.rs @@ -1,5 +1,5 @@ // file: ks-onchain-transport/src/http_client.rs -// version: 14 +// version: 20 //! HTTP JSON-RPC client for standard Solana RPC endpoints. @@ -15,21 +15,30 @@ pub enum HttpMethodClass { } /// Snapshot of one pooled HTTP endpoint. -#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize)] -#[serde(rename_all = "camelCase")] +#[derive(Clone, Eq, PartialEq)] pub struct HttpPoolClientSnapshot { /// Logical endpoint name. pub endpoint_name: std::string::String, /// Provider name. pub provider: std::string::String, - /// Endpoint URL. - pub endpoint_url: std::string::String, /// Supported roles. pub roles: std::vec::Vec, /// Status string. pub status: std::string::String, } +impl std::fmt::Debug for crate::HttpPoolClientSnapshot { + fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + return formatter + .debug_struct("HttpPoolClientSnapshot") + .field("endpoint_name", &self.endpoint_name) + .field("provider", &self.provider) + .field("roles", &self.roles) + .field("status", &self.status) + .finish(); + } +} + #[derive(Debug)] struct HttpRequestLimitState { available_tokens: f64, @@ -138,7 +147,7 @@ impl HttpRequestLimiter { } /// HTTP JSON-RPC client bound to one configured endpoint. -#[derive(Clone, Debug)] +#[derive(Clone)] pub struct HttpClient { endpoint: ks_config::HttpEndpointConfig, client: reqwest::Client, @@ -147,6 +156,16 @@ pub struct HttpClient { selected_role: std::option::Option, } +impl std::fmt::Debug for crate::HttpClient { + fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + return formatter + .debug_struct("HttpClient") + .field("endpoint", &"") + .field("selected_role_configured", &self.selected_role.is_some()) + .finish_non_exhaustive(); + } +} + impl crate::HttpClient { /// Creates a new HTTP client bound to one endpoint. pub fn new(endpoint: ks_config::HttpEndpointConfig) -> ks_core::Result { @@ -207,11 +226,6 @@ impl crate::HttpClient { return self.endpoint.provider.as_str(); } - /// Returns the endpoint URL. - pub fn endpoint_url(&self) -> &str { - return self.endpoint.url.as_str(); - } - /// Returns the endpoint configuration. pub fn endpoint_config(&self) -> &ks_config::HttpEndpointConfig { return &self.endpoint; @@ -230,7 +244,7 @@ impl crate::HttpClient { return false; } - /// Returns a serializable endpoint snapshot. + /// Returns a backend-only endpoint snapshot without resolved URL material. pub fn snapshot(&self) -> crate::HttpPoolClientSnapshot { let mut roles = std::vec::Vec::new(); for role in &self.endpoint.roles { @@ -239,7 +253,6 @@ impl crate::HttpClient { return crate::HttpPoolClientSnapshot { endpoint_name: self.endpoint.name.clone(), provider: self.endpoint.provider.clone(), - endpoint_url: self.endpoint.url.clone(), roles, status: "active".to_string(), }; @@ -342,9 +355,10 @@ impl crate::HttpClient { let response = match response_result { std::result::Result::Ok(response) => response, std::result::Result::Err(error) => { - tracing::error!(target: crate::TRACING_TARGET, action = "execute_http_json_rpc", endpoint_name = %self.endpoint.name, provider = %self.endpoint.provider, request_id, method = %method, retry_index, error = %error, "HTTP JSON-RPC transport failed"); + let error_kind = reqwest_error_kind(&error); + tracing::error!(target: crate::TRACING_TARGET, action = "execute_http_json_rpc", endpoint_name = %self.endpoint.name, provider = %self.endpoint.provider, request_id, method = %method, retry_index, error_kind, "HTTP JSON-RPC transport failed"); return std::result::Result::Err(ks_core::Error::http(format!( - "http json-rpc request '{}' failed on endpoint '{}': {error}", + "http json-rpc request '{}' failed on endpoint '{}' ({error_kind})", method, self.endpoint.name ))); }, @@ -355,9 +369,10 @@ impl crate::HttpClient { let text = match text_result { std::result::Result::Ok(text) => text, std::result::Result::Err(error) => { - tracing::error!(target: crate::TRACING_TARGET, action = "execute_http_json_rpc", endpoint_name = %self.endpoint.name, provider = %self.endpoint.provider, request_id, method = %method, http_status = %status, retry_index, error = %error, "HTTP JSON-RPC response body read failed"); + let error_kind = reqwest_error_kind(&error); + tracing::error!(target: crate::TRACING_TARGET, action = "execute_http_json_rpc", endpoint_name = %self.endpoint.name, provider = %self.endpoint.provider, request_id, method = %method, http_status = %status, retry_index, error_kind, "HTTP JSON-RPC response body read failed"); return std::result::Result::Err(ks_core::Error::http(format!( - "cannot read http json-rpc response '{}' from endpoint '{}': {error}", + "cannot read http json-rpc response '{}' from endpoint '{}' ({error_kind})", method, self.endpoint.name ))); }, @@ -383,8 +398,8 @@ impl crate::HttpClient { if !status.is_success() { tracing::error!(target: crate::TRACING_TARGET, action = "execute_http_json_rpc", endpoint_name = %self.endpoint.name, provider = %self.endpoint.provider, request_id, method = %method, http_status = %status, retry_index, response_byte_length = text.len(), "HTTP JSON-RPC endpoint returned non-success status"); return std::result::Result::Err(ks_core::Error::http(format!( - "http json-rpc endpoint '{}' returned status {} after {} retries: {}", - self.endpoint.name, status, retry_index, text + "http json-rpc endpoint '{}' returned status {} after {} retries", + self.endpoint.name, status, retry_index ))); } let parsed = match crate::parse_json_rpc_text(&text) { @@ -412,7 +427,7 @@ impl crate::HttpClient { }, std::option::Option::None => configured_pause_ms, }; - tracing::warn!(target: crate::TRACING_TARGET, action = "retry_http_json_rpc_after_rate_limit", endpoint_name = %self.endpoint.name, provider = %self.endpoint.provider, request_id, method = %method, rpc_error_code = error_response.error.code, rpc_error_message = %error_response.error.message, retry_index, pause_ms, "HTTP JSON-RPC endpoint returned a rate-limit RPC error; retrying with bounded backoff"); + tracing::warn!(target: crate::TRACING_TARGET, action = "retry_http_json_rpc_after_rate_limit", endpoint_name = %self.endpoint.name, provider = %self.endpoint.provider, request_id, method = %method, rpc_error_code = error_response.error.code, retry_index, pause_ms, "HTTP JSON-RPC endpoint returned a rate-limit RPC error; retrying with bounded backoff"); if let std::option::Option::Some(limiter) = &request_limiter { limiter.block_for(pause_ms).await; } @@ -420,13 +435,10 @@ impl crate::HttpClient { continue; }, crate::JsonRpcResponse::Error(error_response) => { - tracing::error!(target: crate::TRACING_TARGET, action = "execute_http_json_rpc", endpoint_name = %self.endpoint.name, provider = %self.endpoint.provider, request_id, method = %method, retry_index, rpc_error_code = error_response.error.code, rpc_error_message = %error_response.error.message, "HTTP JSON-RPC endpoint returned an RPC error"); + tracing::error!(target: crate::TRACING_TARGET, action = "execute_http_json_rpc", endpoint_name = %self.endpoint.name, provider = %self.endpoint.provider, request_id, method = %method, retry_index, rpc_error_code = error_response.error.code, "HTTP JSON-RPC endpoint returned an RPC error"); return std::result::Result::Err(ks_core::Error::http(format!( - "json-rpc error {} from '{}' after {} retries: {}", - error_response.error.code, - self.endpoint.name, - retry_index, - error_response.error.message + "json-rpc error {} from '{}' after {} retries", + error_response.error.code, self.endpoint.name, retry_index ))); }, crate::JsonRpcResponse::Notification(_) => { @@ -515,6 +527,25 @@ impl crate::HttpClient { } } +fn reqwest_error_kind(error: &reqwest::Error) -> &'static str { + if error.is_timeout() { + return "timeout"; + } + if error.is_connect() { + return "connect"; + } + if error.is_request() { + return "request"; + } + if error.is_body() { + return "body"; + } + if error.is_decode() { + return "decode"; + } + return "transport"; +} + #[cfg(test)] mod tests { fn role_config( @@ -577,6 +608,27 @@ mod tests { assert!(client.can_handle("http_any", "send_transaction")); } + #[test] + fn debug_output_omits_endpoint_url() { + let mut endpoint = endpoint(true); + endpoint.url = "https://HTTP-SECRET-CANARY.invalid/?api-key=secret".to_string(); + let client = match crate::HttpClient::new(endpoint) { + std::result::Result::Ok(client) => client, + std::result::Result::Err(error) => panic!("client creation failed: {error}"), + }; + let rendered = format!("{client:?}"); + assert!(rendered.contains("")); + assert!(rendered.contains("selected_role_configured: false")); + assert!(!rendered.contains("HTTP-SECRET-CANARY")); + assert!(!rendered.contains("api-key=secret")); + let snapshot = client.snapshot(); + let snapshot_rendered = format!("{snapshot:?}"); + assert!(!snapshot_rendered.contains("endpoint_url")); + assert!(!snapshot_rendered.contains("endpointUrl")); + assert!(!snapshot_rendered.contains("HTTP-SECRET-CANARY")); + assert!(!snapshot_rendered.contains("api-key=secret")); + } + #[test] fn snapshot_preserves_endpoint_metadata() { let client = match crate::HttpClient::new(endpoint(true)) { @@ -586,7 +638,6 @@ mod tests { let snapshot = client.snapshot(); assert_eq!(snapshot.endpoint_name, "http_a"); assert_eq!(snapshot.provider, "test"); - assert_eq!(snapshot.endpoint_url, "https://example.invalid"); assert_eq!(snapshot.roles.len(), 3); } diff --git a/ks-onchain-transport/src/http_pool.rs b/ks-onchain-transport/src/http_pool.rs index 0e90565..93890c4 100644 --- a/ks-onchain-transport/src/http_pool.rs +++ b/ks-onchain-transport/src/http_pool.rs @@ -1,5 +1,5 @@ // file: ks-onchain-transport/src/http_pool.rs -// version: 10 +// version: 11 //! HTTP endpoint pool and role-based routing. @@ -53,7 +53,7 @@ impl crate::HttpEndpointPool { }); } - /// Returns a serializable snapshot of every endpoint in the pool. + /// Returns backend-only endpoint metadata without resolved URL material. pub fn snapshot(&self) -> std::vec::Vec { let mut snapshots = std::vec::Vec::new(); for client in &self.clients { diff --git a/ks-onchain-transport/src/ws_client.rs b/ks-onchain-transport/src/ws_client.rs index e76e016..256360a 100644 --- a/ks-onchain-transport/src/ws_client.rs +++ b/ks-onchain-transport/src/ws_client.rs @@ -1,5 +1,5 @@ // file: ks-onchain-transport/src/ws_client.rs -// version: 9 +// version: 15 //! Standard Solana WebSocket client helpers. @@ -7,28 +7,46 @@ use futures_util::SinkExt; // rust-rules: trait-import use futures_util::StreamExt; // rust-rules: trait-import /// Snapshot of one pooled WebSocket endpoint. -#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize)] -#[serde(rename_all = "camelCase")] +#[derive(Clone, Eq, PartialEq)] pub struct WsPoolClientSnapshot { /// Logical endpoint name. pub endpoint_name: std::string::String, /// Provider name. pub provider: std::string::String, - /// Endpoint URL. - pub endpoint_url: std::string::String, /// Supported roles. pub roles: std::vec::Vec, /// Status string. pub status: std::string::String, } +impl std::fmt::Debug for crate::WsPoolClientSnapshot { + fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + return formatter + .debug_struct("WsPoolClientSnapshot") + .field("endpoint_name", &self.endpoint_name) + .field("provider", &self.provider) + .field("roles", &self.roles) + .field("status", &self.status) + .finish(); + } +} + /// Standard Solana WebSocket client bound to one configured endpoint. -#[derive(Clone, Debug)] +#[derive(Clone)] pub struct WsClient { endpoint: ks_config::WsEndpointConfig, next_request_id: std::sync::Arc, } +impl std::fmt::Debug for crate::WsClient { + fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + return formatter + .debug_struct("WsClient") + .field("endpoint", &"") + .finish_non_exhaustive(); + } +} + impl crate::WsClient { /// Creates a new WebSocket client bound to one endpoint. pub fn new(endpoint: ks_config::WsEndpointConfig) -> ks_core::Result { @@ -56,8 +74,7 @@ impl crate::WsClient { return self.endpoint.provider.as_str(); } - /// Returns the endpoint URL. - pub fn endpoint_url(&self) -> &str { + pub(crate) fn endpoint_url(&self) -> &str { return self.endpoint.url.as_str(); } @@ -79,7 +96,7 @@ impl crate::WsClient { return false; } - /// Returns a serializable endpoint snapshot. + /// Returns a backend-only endpoint snapshot without resolved URL material. pub fn snapshot(&self) -> crate::WsPoolClientSnapshot { let mut roles = std::vec::Vec::new(); for role in &self.endpoint.roles { @@ -88,7 +105,6 @@ impl crate::WsClient { return crate::WsPoolClientSnapshot { endpoint_name: self.endpoint.name.clone(), provider: self.endpoint.provider.clone(), - endpoint_url: self.endpoint.url.clone(), roles, status: "idle".to_string(), }; @@ -156,10 +172,10 @@ impl crate::WsClient { }; let (mut stream, _response) = match connect_result { std::result::Result::Ok(pair) => pair, - std::result::Result::Err(error) => { - tracing::error!(target: crate::TRACING_TARGET, action = "connect_ws_endpoint", endpoint_name = %self.endpoint.name, provider = %self.endpoint.provider, request_id = ?request.id, method = %method, error = %error, "WebSocket endpoint connection failed"); + std::result::Result::Err(_error) => { + tracing::error!(target: crate::TRACING_TARGET, action = "connect_ws_endpoint", endpoint_name = %self.endpoint.name, provider = %self.endpoint.provider, request_id = ?request.id, method = %method, error_kind = "connect", "WebSocket endpoint connection failed"); return std::result::Result::Err(ks_core::Error::ws(format!( - "cannot connect websocket endpoint '{}': {error}", + "cannot connect websocket endpoint '{}'", self.endpoint.name ))); }, @@ -217,7 +233,7 @@ impl crate::WsClient { match parse_result { std::result::Result::Ok(response) => { if let crate::JsonRpcResponse::Error(error_response) = &response { - tracing::error!(target: crate::TRACING_TARGET, action = "execute_ws_json_rpc_once", endpoint_name = %self.endpoint.name, provider = %self.endpoint.provider, request_id = ?request.id, method = %method, response_kind = response.kind_name(), rpc_error_code = error_response.error.code, rpc_error_message = %error_response.error.message, "WebSocket JSON-RPC endpoint returned an RPC error"); + tracing::error!(target: crate::TRACING_TARGET, action = "execute_ws_json_rpc_once", endpoint_name = %self.endpoint.name, provider = %self.endpoint.provider, request_id = ?request.id, method = %method, response_kind = response.kind_name(), rpc_error_code = error_response.error.code, "WebSocket JSON-RPC endpoint returned an RPC error"); } else { tracing::debug!(target: crate::TRACING_TARGET, action = "execute_ws_json_rpc_once", endpoint_name = %self.endpoint.name, provider = %self.endpoint.provider, request_id = ?request.id, method = %method, response_kind = response.kind_name(), "one-shot WebSocket JSON-RPC request completed"); } @@ -334,6 +350,26 @@ mod tests { assert!(client.can_handle("ws_any", "logs_subscribe_mentions")); } + #[test] + fn debug_output_omits_endpoint_url() { + let mut endpoint = endpoint(true); + endpoint.url = "wss://WS-SECRET-CANARY.invalid/?api-key=secret".to_string(); + let client = match crate::WsClient::new(endpoint) { + std::result::Result::Ok(client) => client, + std::result::Result::Err(error) => panic!("client creation failed: {error}"), + }; + let rendered = format!("{client:?}"); + assert!(rendered.contains("")); + assert!(!rendered.contains("WS-SECRET-CANARY")); + assert!(!rendered.contains("api-key=secret")); + let snapshot = client.snapshot(); + let snapshot_rendered = format!("{snapshot:?}"); + assert!(!snapshot_rendered.contains("endpoint_url")); + assert!(!snapshot_rendered.contains("endpointUrl")); + assert!(!snapshot_rendered.contains("WS-SECRET-CANARY")); + assert!(!snapshot_rendered.contains("api-key=secret")); + } + #[test] fn snapshot_preserves_endpoint_metadata() { let client = match crate::WsClient::new(endpoint(true)) { @@ -343,7 +379,6 @@ mod tests { let snapshot = client.snapshot(); assert_eq!(snapshot.endpoint_name, "ws_a"); assert_eq!(snapshot.provider, "test"); - assert_eq!(snapshot.endpoint_url, "wss://example.invalid"); assert_eq!(snapshot.roles.len(), 3); } diff --git a/ks-onchain-transport/src/ws_pool.rs b/ks-onchain-transport/src/ws_pool.rs index 92ba166..609555d 100644 --- a/ks-onchain-transport/src/ws_pool.rs +++ b/ks-onchain-transport/src/ws_pool.rs @@ -1,5 +1,5 @@ // file: ks-onchain-transport/src/ws_pool.rs -// version: 8 +// version: 9 //! WebSocket endpoint pool and role-based routing. @@ -53,7 +53,7 @@ impl crate::WsEndpointPool { }); } - /// Returns a serializable snapshot of every endpoint in the pool. + /// Returns backend-only endpoint metadata without resolved URL material. pub fn snapshot(&self) -> std::vec::Vec { let mut snapshots = std::vec::Vec::new(); for client in &self.clients { diff --git a/ks-onchain-transport/src/ws_session.rs b/ks-onchain-transport/src/ws_session.rs index 58ab05e..1502398 100644 --- a/ks-onchain-transport/src/ws_session.rs +++ b/ks-onchain-transport/src/ws_session.rs @@ -1,5 +1,5 @@ // file: ks-onchain-transport/src/ws_session.rs -// version: 6 +// version: 9 //! Persistent multiplexed WebSocket session with bounded reconnect and resubscription. @@ -100,15 +100,12 @@ pub struct WsSubscriptionSnapshot { } /// Current snapshot of one persistent WebSocket session. -#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "camelCase")] +#[derive(Clone, Eq, PartialEq)] pub struct WsSessionSnapshot { /// Endpoint name. pub endpoint_name: std::string::String, /// Provider name. pub provider: std::string::String, - /// Endpoint URL. - pub endpoint_url: std::string::String, /// Current lifecycle state. pub state: crate::WsSessionState, /// Number of successful reconnects since session creation. @@ -119,6 +116,20 @@ pub struct WsSessionSnapshot { pub subscriptions: std::vec::Vec, } +impl std::fmt::Debug for crate::WsSessionSnapshot { + fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + return formatter + .debug_struct("WsSessionSnapshot") + .field("endpoint_name", &self.endpoint_name) + .field("provider", &self.provider) + .field("state", &self.state) + .field("reconnect_count", &self.reconnect_count) + .field("capabilities", &self.capabilities) + .field("subscriptions", &self.subscriptions) + .finish(); + } +} + /// Successful subscription acknowledgement. #[derive(Clone, Debug, PartialEq)] pub struct WsSubscriptionAck { @@ -202,7 +213,6 @@ impl std::fmt::Debug for crate::WsSession { .debug_struct("WsSession") .field("endpoint_name", &self.endpoint.name) .field("provider", &self.endpoint.provider) - .field("endpoint_url", &self.endpoint.url) .finish_non_exhaustive(); } } @@ -238,7 +248,6 @@ impl crate::WsSession { let snapshot = std::sync::Arc::new(tokio::sync::RwLock::new(crate::WsSessionSnapshot { endpoint_name: endpoint.name.clone(), provider: endpoint.provider.clone(), - endpoint_url: endpoint.url.clone(), state: crate::WsSessionState::Connected, reconnect_count: 0, capabilities: initial_capabilities, @@ -788,10 +797,8 @@ impl WsSessionRuntime { async fn handle_error_response(&mut self, error_response: crate::JsonRpcErrorResponse) { let request_id = error_response.id.as_u64(); - let error = ks_core::Error::ws(format!( - "WebSocket JSON-RPC error {}: {}", - error_response.error.code, error_response.error.message - )); + let error = + ks_core::Error::ws(format!("WebSocket JSON-RPC error {}", error_response.error.code)); if let std::option::Option::Some(request_id) = request_id { if let std::option::Option::Some(pending) = self.pending.remove(&request_id) { match pending { @@ -852,10 +859,8 @@ impl WsSessionRuntime { self.emit_diagnostic( "ws_capability_disabled", format!( - "endpoint '{}' rejected unstable method '{method}'; capability disabled for this session: JSON-RPC {} {}", - self.client.endpoint_name(), - error_response.error.code, - error_response.error.message + "endpoint '{}' rejected unstable method '{method}'; capability disabled for this session: JSON-RPC {}", + self.client.endpoint_name(), error_response.error.code ), ); } @@ -1180,8 +1185,8 @@ async fn connect_stream(client: &crate::WsClient) -> ks_core::Result { }; return match connect_result { std::result::Result::Ok((stream, _response)) => std::result::Result::Ok(stream), - std::result::Result::Err(error) => std::result::Result::Err(ks_core::Error::ws(format!( - "cannot connect WebSocket endpoint '{}': {error}", + std::result::Result::Err(_error) => std::result::Result::Err(ks_core::Error::ws(format!( + "cannot connect WebSocket endpoint '{}'", client.endpoint_name() ))), }; @@ -1281,6 +1286,21 @@ mod tests { ); } + #[test] + fn session_snapshot_debug_contains_no_endpoint_url_field() { + let snapshot = crate::WsSessionSnapshot { + endpoint_name: "helius".to_string(), + provider: "helius".to_string(), + state: crate::WsSessionState::Connected, + reconnect_count: 0, + capabilities: crate::StandardWsCapabilities::default(), + subscriptions: std::vec::Vec::new(), + }; + let rendered = format!("{snapshot:?}"); + assert!(!rendered.contains("endpoint_url")); + assert!(!rendered.contains("endpointUrl")); + } + #[tokio::test] async fn persistent_session_multiplexes_notification_and_explicit_unsubscribe() { let listener = match tokio::net::TcpListener::bind("127.0.0.1:0").await { diff --git a/olddocs/archivekbot3/docs/plans/V0_5_0_PRE_003_STORE_SCENARIOS_EXECUTION_AUDIT.md b/olddocs/archivekbot3/docs/plans/V0_5_0_PRE_003_STORE_SCENARIOS_EXECUTION_AUDIT.md index 125561f..c95d493 100644 --- a/olddocs/archivekbot3/docs/plans/V0_5_0_PRE_003_STORE_SCENARIOS_EXECUTION_AUDIT.md +++ b/olddocs/archivekbot3/docs/plans/V0_5_0_PRE_003_STORE_SCENARIOS_EXECUTION_AUDIT.md @@ -32,17 +32,17 @@ Cette orientation générale est aussi conservée dans `docs/IDEA_REMINDERS.md`. Les crates suivantes sont des bibliothèques ou composants de support Solana généralistes et sont donc candidates au renommage direct en `0.5.1` : -| Actuel | Cible de travail | -|---|---| -| `kb-core` | `ks-core` | -| `kb-config` | `ks-config` | -| `kb-lib` | `ks-lib` | -| `kb-logging` | `ks-logging` | -| `kb-program-ids` | `ks-program-ids` | -| `kb-pipeline` | `ks-pipeline` | -| `kb-onchain-transport` | `ks-onchain-transport` | -| `kb-store` | `ks-store` | -| `kb-wallet` | `ks-wallet` | +| Actuel | Cible de travail | +|------------------------------|------------------------------------------------------------------------------------------| +| `kb-core` | `ks-core` | +| `kb-config` | `ks-config` | +| `kb-lib` | `ks-lib` | +| `kb-logging` | `ks-logging` | +| `kb-program-ids` | `ks-program-ids` | +| `kb-pipeline` | `ks-pipeline` | +| `kb-onchain-transport` | `ks-onchain-transport` | +| `kb-store` | `ks-store` | +| `kb-wallet` | `ks-wallet` | | `kb-pipeline-demo-scenarios` | `ks-pipeline-demo-scenarios` à confirmer comme composant de validation Khadhroony Solana | `kb-app-demo-desktop` reste hors de cette substitution automatique : c'est une application Khadhroony Bot, même si son rôle courant est surtout de tester les composants `ks-*`. @@ -121,13 +121,13 @@ En revanche : Le contrat `0.5.3` doit donc imposer la distinction suivante : -| Dimension | Sens | -|---|---| -| `slot` | ordre/position Solana ; jamais assimilé à un temps civil | -| `block_time` | temps on-chain Unix optionnel observé depuis le cluster | -| `detected_at` / `received_at` / `normalized_at` | temps locaux d'acquisition | -| `persisted_at` | instant d'écriture de l'observation | -| `created_at` / `updated_at` | cycle de vie de la ligne SQL | +| Dimension | Sens | +|-------------------------------------------------|----------------------------------------------------------| +| `slot` | ordre/position Solana ; jamais assimilé à un temps civil | +| `block_time` | temps on-chain Unix optionnel observé depuis le cluster | +| `detected_at` / `received_at` / `normalized_at` | temps locaux d'acquisition | +| `persisted_at` | instant d'écriture de l'observation | +| `created_at` / `updated_at` | cycle de vie de la ligne SQL | Une requête de prix ou série temporelle ne doit jamais prendre `created_at` comme substitut implicite de `block_time`. @@ -211,16 +211,16 @@ L'inventaire statique de `kb-lib` trouve : Les huit exécuteurs actifs sont : -| Exécuteur actif | Scénario réutilisable actuel | Qualification | -|---|---|---| -| Solana Core | oui | plusieurs parcours Devnet ; matrice native existante | -| SPL Memo v4 | oui | Devnet ; v1/v3 restent decode-only | -| SPL Associated Token Account | oui | scénario Devnet | -| SPL Token classique | oui | scénarios et lifecycle Devnet | -| SPL Token-2022 | oui | scénarios Devnet et campagne Token Metadata | -| SPL ElGamal registry | non | implémenté + synthétique seulement ; preuve réseau indisponible | -| Metaplex Token Metadata | oui | matrice `15 confirmed / 5 unavailable` | -| Solana Program Metadata | oui | 9 opérations confirmées Devnet | +| Exécuteur actif | Scénario réutilisable actuel | Qualification | +|------------------------------|------------------------------|-----------------------------------------------------------------| +| Solana Core | oui | plusieurs parcours Devnet ; matrice native existante | +| SPL Memo v4 | oui | Devnet ; v1/v3 restent decode-only | +| SPL Associated Token Account | oui | scénario Devnet | +| SPL Token classique | oui | scénarios et lifecycle Devnet | +| SPL Token-2022 | oui | scénarios Devnet et campagne Token Metadata | +| SPL ElGamal registry | non | implémenté + synthétique seulement ; preuve réseau indisponible | +| Metaplex Token Metadata | oui | matrice `15 confirmed / 5 unavailable` | +| Solana Program Metadata | oui | 9 opérations confirmées Devnet | Le seul exécuteur actif sans scénario réseau réutilisable est donc ElGamal, et son absence est **`unavailable`/report conditionnel**, pas `implement`, tant qu'une nouvelle possibilité de preuve n'existe pas. @@ -284,18 +284,18 @@ La future matrice transversale doit comparer uniquement les **surfaces actives o Pour chaque capacité, enregistrer : -| Dimension | Valeur attendue | -|---|---| -| decoder | actif / réservé / absent | -| materializer | actif / state-only / réservé / non applicable | -| executor | actif / decode-only / deprecated / réservé / absent | -| synthetic | présent / absent / non applicable | -| reusable scenario | présent / absent / unavailable / non applicable | -| simulation | prouvée / non exécutée / unavailable | -| submission | confirmed / non exécutée / unsafe / unavailable | -| postcondition | stateful / replay/materialization / non applicable | -| desktop | adaptateur / logique réutilisable résiduelle / absent | -| final status | `implement`, `decode-only`, `deprecated`, `unavailable`, `not-applicable`, `deferred` | +| Dimension | Valeur attendue | +|-------------------|---------------------------------------------------------------------------------------| +| decoder | actif / réservé / absent | +| materializer | actif / state-only / réservé / non applicable | +| executor | actif / decode-only / deprecated / réservé / absent | +| synthetic | présent / absent / non applicable | +| reusable scenario | présent / absent / unavailable / non applicable | +| simulation | prouvée / non exécutée / unavailable | +| submission | confirmed / non exécutée / unsafe / unavailable | +| postcondition | stateful / replay/materialization / non applicable | +| desktop | adaptateur / logique réutilisable résiduelle / absent | +| final status | `implement`, `decode-only`, `deprecated`, `unavailable`, `not-applicable`, `deferred` | ### Classification actuelle de départ diff --git a/prompts/030_v0_5_1_khadhroony_solana_namespace_and_config.md b/prompts/030_v0_5_1_khadhroony_solana_namespace_and_config.md index 3d60a14..5c3bffe 100644 --- a/prompts/030_v0_5_1_khadhroony_solana_namespace_and_config.md +++ b/prompts/030_v0_5_1_khadhroony_solana_namespace_and_config.md @@ -1,5 +1,5 @@ - + # Khadhroony Bot3 — `0.5.1` — migration Khadhroony Solana et configuration sûre @@ -351,11 +351,12 @@ Le détail doit être confirmé par `0.5.1-pre.001`, mais la trajectoire cible e ### `0.5.1-pre.008` — runtime/public, camouflage et Tauri sûr -- séparer source/runtime/public/diagnostic ; -- propager la sensibilité des valeurs composées ; -- supprimer les payloads de config complète ; -- diagnostics sûrs et camouflage systématique ; -- tests de non-divulgation et TS-RS aligné. +- **réalisé** : rendre les contrats `ks-config` source/runtime sensibles backend-only, sans `Serialize`/`Debug` ; +- **réalisé** : rendre `application` opaque pour `ks-config` et valider le fragment desktop avec son schéma propriétaire ; +- **réalisé** : remplacer l'exposition `AppConfig/ProfileConfig` par des DTO public/diagnostic construits explicitement ; +- **réalisé** : propager `Secret > Internal > Public` aux chaînes composées et supprimer l'écho de valeurs rejetées dans les erreurs de validation ; +- **réalisé** : retirer TS-RS et les bindings générés de `ks-config`/`ks-lib`, avec audit empêchant leur réintroduction implicite ; +- **réalisé** : ajouter les canaris de non-divulgation et le test d'API externe de sensibilité. ### `0.5.1-pre.009` — réconciliation et clôture diff --git a/scripts/audit_khadhroony_workspace_rules.py b/scripts/audit_khadhroony_workspace_rules.py index 1a4e13b..de7acd2 100644 --- a/scripts/audit_khadhroony_workspace_rules.py +++ b/scripts/audit_khadhroony_workspace_rules.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # file: scripts/audit_khadhroony_workspace_rules.py -# version: 23 +# version: 28 """Audit mechanically verifiable rules specific to khadhroony-bot3.""" @@ -915,13 +915,13 @@ def audit_environment_namespaces(root: pathlib.Path) -> list[Violation]: "config/store.config.json", "config/wallet.config.json", "config/execution.config.json", - "config/example.kb-app-demo-desktop.default.config.json", - "config/example.logging.config.json", - "config/example.transport.config.json", - "config/example.listeners.config.json", - "config/example.store.config.json", - "config/example.wallet.config.json", - "config/example.execution.config.json", + "config/exemples/example.kb-app-demo-desktop.default.config.json", + "config/exemples/example.logging.config.json", + "config/exemples/example.transport.config.json", + "config/exemples/example.listeners.config.json", + "config/exemples/example.store.config.json", + "config/exemples/example.wallet.config.json", + "config/exemples/example.execution.config.json", ]: config = root / relative if not config.is_file(): @@ -970,14 +970,15 @@ def audit_configuration_split(root: pathlib.Path) -> list[Violation]: "config/store.config.json", "config/wallet.config.json", "config/execution.config.json", - "config/example.kb-app-demo-desktop.default.config.json", - "config/example.logging.config.json", - "config/example.transport.config.json", - "config/example.listeners.config.json", - "config/example.store.config.json", - "config/example.wallet.config.json", - "config/example.execution.config.json", + "config/exemples/example.kb-app-demo-desktop.default.config.json", + "config/exemples/example.logging.config.json", + "config/exemples/example.transport.config.json", + "config/exemples/example.listeners.config.json", + "config/exemples/example.store.config.json", + "config/exemples/example.wallet.config.json", + "config/exemples/example.execution.config.json", "config/schemas/composition.config.schema.json", + "config/schemas/kb-app-demo-desktop.application.config.schema.json", "config/schemas/logging.config.schema.json", "config/schemas/transport.config.schema.json", "config/schemas/listeners.config.schema.json", @@ -1062,6 +1063,13 @@ def audit_configuration_split(root: pathlib.Path) -> list[Violation]: "config/example.app.config.json", "config/schemas/app.config.schema.json", "config/example.config.json", + "config/example.kb-app-demo-desktop.default.config.json", + "config/example.logging.config.json", + "config/example.transport.config.json", + "config/example.listeners.config.json", + "config/example.store.config.json", + "config/example.wallet.config.json", + "config/example.execution.config.json", "config/schema.config.json", "config/ks-pipeline-demo-scenarios.default.config.json", ] @@ -1069,9 +1077,238 @@ def audit_configuration_split(root: pathlib.Path) -> list[Violation]: if not (root / relative).exists(): continue violations.append(Violation("KH_CFG007", relative, 1, "legacy or unnecessary configuration composition file must be removed")) + protected_runtime_files = [ + "ks-config/src/composition.rs", + "ks-config/src/execution.rs", + "ks-config/src/listeners.rs", + "ks-config/src/settings.rs", + "ks-config/src/store.rs", + "ks-config/src/transport.rs", + "ks-config/src/wallet.rs", + ] + for relative in protected_runtime_files: + path = root / relative + if not path.is_file(): + continue + for index, line in enumerate(path.read_text(encoding="utf-8").splitlines(), 1): + if "#[derive(" not in line: + continue + if "serde::Serialize" in line: + violations.append(Violation("KH_CFG013", relative, index, "resolved/source configuration contracts must not derive Serialize because they may contain resolved secrets")) + if re.search(r"\bDebug\b", line): + violations.append(Violation("KH_CFG014", relative, index, "resolved/source configuration contracts must not derive Debug because they may contain resolved secrets or internal values")) return violations + +def audit_ts_rs_boundaries(root: pathlib.Path) -> list[Violation]: + """Keep TS-RS generation at explicit application boundaries.""" + + violations: list[Violation] = [] + for cargo in sorted(root.glob("ks-*/Cargo.toml")): + crate = cargo.parent + data = tomllib.loads(cargo.read_text(encoding="utf-8")) + for section in ["dependencies", "dev-dependencies", "build-dependencies"]: + dependencies = data.get(section, {}) + if "ts-rs" in dependencies: + violations.append( + Violation( + "KH_TS001", + cargo.relative_to(root).as_posix(), + 1, + "generic ks-* crates must not depend on ts-rs without an explicit approved external TypeScript contract", + ) + ) + for path in sorted((crate / "src").rglob("*.rs")): + relative = path.relative_to(root).as_posix() + for index, line in enumerate(path.read_text(encoding="utf-8").splitlines(), 1): + if "ts_rs::" in line or "#[ts(" in line or re.search(r"#\[derive\([^]]*\bTS\b", line): + violations.append( + Violation( + "KH_TS002", + relative, + index, + "TS-RS derives/attributes belong to application DTO boundaries, not generic ks-* runtime types", + ) + ) + for generated in [crate / "frontend/ts/bindings", crate / "bindings"]: + if generated.exists(): + violations.append( + Violation( + "KH_TS003", + generated.relative_to(root).as_posix(), + 1, + "generic ks-* crates must not own generated TypeScript bindings", + ) + ) + desktop = root / "kb-app-demo-desktop/src" + if desktop.exists(): + field_pattern = re.compile(r"^\s*pub\(crate\)\s+[A-Za-z0-9_]+:\s+ks_[a-z0-9_]+::") + for path in sorted(desktop.rglob("*.rs")): + relative = path.relative_to(root).as_posix() + for index, line in enumerate(path.read_text(encoding="utf-8").splitlines(), 1): + if field_pattern.search(line): + violations.append( + Violation( + "KH_TS004", + relative, + index, + "desktop DTO/state fields that cross application boundaries must use application-owned wrappers rather than direct ks-* field types", + ) + ) + tauri_path = root / "kb-app-demo-desktop/src/tauri.rs" + if tauri_path.is_file(): + lines = tauri_path.read_text(encoding="utf-8").splitlines() + for index, line in enumerate(lines): + if line.strip() != "#[tauri::command]": + continue + signature_lines: list[str] = [] + for candidate in lines[index + 1 : index + 20]: + signature_lines.append(candidate) + if "{" in candidate: + break + signature = "\n".join(signature_lines) + if re.search(r"->[^\{]*\bks_[a-z0-9_]+::", signature, re.DOTALL): + violations.append( + Violation( + "KH_TS005", + "kb-app-demo-desktop/src/tauri.rs", + index + 1, + "Tauri command return types must use desktop-owned DTO wrappers instead of direct ks-* contracts", + ) + ) + return violations + +def audit_sensitive_transport_surfaces(root: pathlib.Path) -> list[Violation]: + """Keep resolved transport URLs and other internal paths away from public boundaries.""" + + violations: list[Violation] = [] + protected = { + "ks-onchain-transport/src/http_client.rs": "HttpPoolClientSnapshot", + "ks-onchain-transport/src/ws_client.rs": "WsPoolClientSnapshot", + "ks-onchain-transport/src/ws_session.rs": "WsSessionSnapshot", + } + for relative, struct_name in protected.items(): + path = root / relative + if not path.is_file(): + continue + text = path.read_text(encoding="utf-8") + struct_match = re.search( + rf"pub struct {struct_name}\s*\{{(?P.*?)\n\}}", + text, + re.DOTALL, + ) + if struct_match is not None and re.search(r"\bendpoint_url\s*:", struct_match.group("body")): + violations.append( + Violation( + "KH_SEC001", + relative, + text[: struct_match.start()].count("\n") + 1, + f"backend snapshot `{struct_name}` must not retain a resolved endpoint URL", + ) + ) + derive_match = re.search( + rf"#\[derive\(([^)]*)\)\]\s*(?:#\[[^\]]+\]\s*)*pub struct {struct_name}\b", + text, + re.DOTALL, + ) + if derive_match is not None and "serde::Serialize" in derive_match.group(1): + violations.append( + Violation( + "KH_SEC002", + relative, + text[: derive_match.start()].count("\n") + 1, + f"backend transport snapshot `{struct_name}` must not derive Serialize", + ) + ) + desktop_root = root / "kb-app-demo-desktop/src" + if desktop_root.is_dir(): + forbidden_fields = { + "endpoint_url": "resolved endpoint URLs must not cross the Tauri boundary", + "fixture_path": "wallet-derived fixture paths must not cross functional Tauri payloads", + } + field_pattern = re.compile(r"pub\(crate\)\s+([a-z][a-z0-9_]*):") + for path in sorted(desktop_root.rglob("*.rs")): + relative = path.relative_to(root).as_posix() + for index, line in enumerate(path.read_text(encoding="utf-8").splitlines(), 1): + match = field_pattern.search(line) + if match is None or match.group(1) not in forbidden_fields: + continue + violations.append( + Violation( + "KH_SEC003", + relative, + index, + forbidden_fields[match.group(1)], + ) + ) + transport_root = root / "ks-onchain-transport/src" + if transport_root.is_dir(): + forbidden_fragments = { + "rpc_error_message = %error_response.error.message": "remote JSON-RPC messages must not be copied into transport logs", + '.field("endpoint_url", &self.endpoint.url)': "transport Debug must not expose a resolved endpoint URL", + "pub fn endpoint_url(&self) -> &str": "resolved endpoint URL getters must remain crate-private", + } + for path in sorted(transport_root.rglob("*.rs")): + relative = path.relative_to(root).as_posix() + text = path.read_text(encoding="utf-8") + for fragment, message in forbidden_fragments.items(): + start = 0 + while True: + offset = text.find(fragment, start) + if offset < 0: + break + violations.append( + Violation( + "KH_SEC004", + relative, + text[:offset].count("\n") + 1, + message, + ) + ) + start = offset + len(fragment) + concrete_api_key = re.compile(r"api-key=([0-9a-fA-F]{8,}(?:-[0-9a-fA-F]+)*)") + secret_scan_roots = [root / "config", root / "ks-config", root / "ks-logging", root / "ks-onchain-transport", root / "kb-app-demo-desktop"] + for scan_root in secret_scan_roots: + if not scan_root.is_dir(): + continue + for path in sorted(scan_root.rglob("*")): + if not path.is_file() or "frontend/ts/bindings" in path.as_posix(): + continue + if path.suffix not in {".rs", ".json", ".md", ".ts", ".html"}: + continue + text = path.read_text(encoding="utf-8", errors="ignore") + match = concrete_api_key.search(text) + if match is None: + continue + violations.append( + Violation( + "KH_SEC006", + path.relative_to(root).as_posix(), + text[: match.start()].count("\n") + 1, + "concrete API-key material is forbidden in active source, configuration, documentation and tests", + ) + ) + http_client_path = root / "ks-onchain-transport/src/http_client.rs" + if http_client_path.is_file(): + text = http_client_path.read_text(encoding="utf-8") + forbidden_http = [ + "retry_index, text\n", + "error_response.error.message\n ))", + ] + for fragment in forbidden_http: + offset = text.find(fragment) + if offset >= 0: + violations.append( + Violation( + "KH_SEC005", + "ks-onchain-transport/src/http_client.rs", + text[:offset].count("\n") + 1, + "HTTP transport errors must not copy remote response bodies or JSON-RPC messages", + ) + ) + return violations + def audit_wincode_resolution(root: pathlib.Path) -> list[Violation]: """Require the currently compatible Solana wincode dependency family.""" @@ -1155,7 +1392,9 @@ def main() -> int: +audit_private_ks_lib_paths_in_active_docs(root) +audit_solana_types(root) +audit_environment_namespaces(root) + +audit_ts_rs_boundaries(root) +audit_configuration_split(root) + +audit_sensitive_transport_surfaces(root) +audit_wincode_resolution(root) ) violations.sort(key=lambda item: (item.code, item.path, item.line, item.message)) diff --git a/test-fixtures/config/example.resolved.app.config.json b/test-fixtures/config/example.resolved.app.config.json index f2ccfbe..4e46557 100644 --- a/test-fixtures/config/example.resolved.app.config.json +++ b/test-fixtures/config/example.resolved.app.config.json @@ -3,10 +3,6 @@ "profiles": [ { "name": "local_devnet", - "app": { - "name": "khadhroony-bot3", - "environment": "development" - }, "database": { "enabled": true, "backend": "postgres", @@ -225,10 +221,6 @@ "devnet_send_enabled": true, "testnet_send_enabled": false, "mainnet_send_enabled": false - }, - "demo": { - "live_demo_enabled": true, - "trading_demo_enabled": false } } ] diff --git a/test-fixtures/config/resolved.app.config.json b/test-fixtures/config/resolved.app.config.json index 99d813c..e8917c5 100644 --- a/test-fixtures/config/resolved.app.config.json +++ b/test-fixtures/config/resolved.app.config.json @@ -3,10 +3,6 @@ "profiles": [ { "name": "local_devnet", - "app": { - "name": "khadhroony-bot3", - "environment": "development" - }, "database": { "enabled": true, "backend": "postgres", @@ -225,18 +221,10 @@ "devnet_send_enabled": true, "testnet_send_enabled": false, "mainnet_send_enabled": false - }, - "demo": { - "live_demo_enabled": true, - "trading_demo_enabled": false } }, { "name": "mainnet_research", - "app": { - "name": "khadhroony-bot3", - "environment": "research" - }, "database": { "enabled": true, "backend": "postgres", @@ -655,18 +643,10 @@ "devnet_send_enabled": false, "testnet_send_enabled": false, "mainnet_send_enabled": false - }, - "demo": { - "live_demo_enabled": true, - "trading_demo_enabled": false } }, { "name": "mainnet", - "app": { - "name": "khadhroony-bot3", - "environment": "mainnet" - }, "database": { "enabled": true, "backend": "postgres", @@ -1085,10 +1065,6 @@ "devnet_send_enabled": false, "testnet_send_enabled": false, "mainnet_send_enabled": false - }, - "demo": { - "live_demo_enabled": true, - "trading_demo_enabled": false } } ]