From a2820062ebc4a051d88cbf778cdee4e85f7815a8 Mon Sep 17 00:00:00 2001 From: SinuS Von SifriduS Date: Mon, 10 Aug 2026 09:28:57 +0200 Subject: [PATCH] 0.5.1-pre.007 --- .env.example | 4 +- Cargo.toml | 4 +- config/README.md | 103 +-- config/example.execution.config.json | 26 + ...le.kb-app-demo-desktop.default.config.json | 60 +- config/example.listeners.config.json | 2 +- config/example.logging.config.json | 5 +- config/example.store.config.json | 26 + config/example.transport.config.json | 29 +- config/example.wallet.config.json | 14 + config/execution.config.json | 68 ++ .../kb-app-demo-desktop.default.config.json | 186 +---- ...ipeline-demo-scenarios.default.config.json | 202 ----- config/listeners.config.json | 2 +- config/logging.config.json | 531 ++++++------- config/schemas/composition.config.schema.json | 277 +------ config/schemas/execution.config.schema.json | 127 ++++ config/schemas/listeners.config.schema.json | 4 +- config/schemas/logging.config.schema.json | 8 +- .../schemas/resolved.app.config.schema.json | 60 +- config/schemas/store.config.schema.json | 133 ++++ config/schemas/transport.config.schema.json | 194 +++-- config/schemas/wallet.config.schema.json | 68 ++ config/store.config.json | 68 ++ config/transport.config.json | 57 +- config/wallet.config.json | 30 + .../KHADHROONY_SOLANA_NAMESPACE_POLICY.md | 35 +- docs/guides/CONFIGURATION.md | 147 ++-- docs/guides/LOGGING.md | 99 +-- ...HROONY_SOLANA_NAMESPACE_AND_CONFIG_PLAN.md | 696 +++++++++--------- docs/rules/RULES_SPECIFIC_KHADHROONY.md | 25 +- kb-app-demo-desktop/CHANGELOG.md | 7 +- kb-app-demo-desktop/src/app_state.rs | 28 +- ...cution_metadata_metaplex_token_metadata.rs | 4 +- .../src/demo_execution_solana_core.rs | 4 +- ks-config/CHANGELOG.md | 10 +- ks-config/README.md | 43 +- ks-config/TODO.md | 4 +- ks-config/USAGE.md | 105 ++- ks-config/src/composition.rs | 405 ++++++++-- ks-config/src/execution.rs | 172 +++++ ks-config/src/lib.rs | 111 ++- ks-config/src/listeners.rs | 16 +- ks-config/src/settings.rs | 64 +- ks-config/src/store.rs | 186 +++++ ks-config/src/transport.rs | 296 ++++++-- ks-config/src/wallet.rs | 216 ++++++ ks-config/tests/external_composition_api.rs | 28 +- ks-logging/CHANGELOG.md | 9 +- ks-logging/README.md | 38 +- ks-logging/USAGE.md | 108 +-- ks-logging/src/document.rs | 113 +-- ks-logging/src/lib.rs | 6 +- ks-onchain-transport/CHANGELOG.md | 6 +- ks-onchain-transport/src/execution_rpc.rs | 6 +- ks-pipeline-demo-scenarios/CHANGELOG.md | 8 +- ks-pipeline-demo-scenarios/USAGE.md | 4 +- ks-pipeline-demo-scenarios/src/environment.rs | 32 +- ks-pipeline-demo-scenarios/src/lib.rs | 4 +- .../collection_verify_campaign.rs | 19 +- .../create_mint_campaign.rs | 21 +- .../devnet_execution.rs | 4 +- .../escrow_campaign.rs | 21 +- .../metaplex_token_metadata/fixture.rs | 4 +- .../maintenance_campaign.rs | 19 +- .../pnft_lifecycle_campaign.rs | 19 +- .../print_burn_campaign.rs | 19 +- .../metaplex_token_metadata/use_campaign.rs | 19 +- .../solana_program/devnet_execution.rs | 4 +- .../src/metadata/solana_program/fixture.rs | 4 +- ks-pipeline-demo-scenarios/src/solana.rs | 4 +- .../src/spl/associated_token_account.rs | 4 +- ks-pipeline-demo-scenarios/src/spl/memo.rs | 4 +- .../src/spl/token/execution.rs | 4 +- .../src/spl/token/lifecycle.rs | 4 +- .../src/spl/token_2022/devnet_execution.rs | 4 +- .../src/spl/token_2022/metadata/campaign.rs | 19 +- .../src/spl/token_2022/metadata/fixture.rs | 4 +- scripts/audit_khadhroony_workspace_rules.py | 139 ++-- .../config/example.resolved.app.config.json | 19 +- test-fixtures/config/resolved.app.config.json | 57 +- 81 files changed, 3357 insertions(+), 2381 deletions(-) create mode 100644 config/example.execution.config.json create mode 100644 config/example.store.config.json create mode 100644 config/example.wallet.config.json create mode 100644 config/execution.config.json delete mode 100644 config/ks-pipeline-demo-scenarios.default.config.json create mode 100644 config/schemas/execution.config.schema.json create mode 100644 config/schemas/store.config.schema.json create mode 100644 config/schemas/wallet.config.schema.json create mode 100644 config/store.config.json create mode 100644 config/wallet.config.json create mode 100644 ks-config/src/execution.rs create mode 100644 ks-config/src/store.rs create mode 100644 ks-config/src/wallet.rs diff --git a/.env.example b/.env.example index 682a561..a5db952 100644 --- a/.env.example +++ b/.env.example @@ -19,9 +19,11 @@ KS_SECRET_POSTGRES_TEST_URL=postgres://solana:solana@localhost:5432/khadhroony_t # Optional direct logging configuration override. When absent, the desktop uses the path declared by its composition. # KS_LOGGING_CONFIG_PATH=config/logging.config.json +# KS_LOGS_DIRECTORY=logs +# KS_WALLETS_DIRECTORY=wallets # Optional composition override used by opt-in Devnet scenario tests and the scenarios CLI. -# KS_DEVNET_CONFIG_PATH=config/ks-pipeline-demo-scenarios.default.config.json +# KS_DEVNET_CONFIG_PATH=/path/to/custom.devnet.composition.json # Optional Devnet profile selected by CLI and opt-in scenario tests. # When absent, the first compatible Devnet profile is selected. diff --git a/Cargo.toml b/Cargo.toml index ff4728b..cd211c4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ # file: Cargo.toml -# version: 50 +# version: 51 [workspace] resolver = "3" @@ -18,7 +18,7 @@ members = [ ] [workspace.package] -version = "0.5.1-pre.6" +version = "0.5.1-pre.7" edition = "2024" license = "MIT" repository = "https://git.sasedev.com/Sasedev/khadhroony-bot3" diff --git a/config/README.md b/config/README.md index 2a7d103..b0529cf 100644 --- a/config/README.md +++ b/config/README.md @@ -1,110 +1,123 @@ - + # Configuration locale -Ce dossier contient les compositions propres aux binaires, les documents de configuration partagés, leurs exemples et les schémas JSON actifs. +Ce dossier contient les compositions propres aux binaires, les documents de configuration Khadhroony Solana partagés, leurs exemples et les schémas JSON actifs. -## Principe de composition +## Modèle de composition -Un binaire ne possède pas une copie complète de toutes les configurations partagées. Il charge un fichier de composition `.default.config.json` qui référence les documents spécialisés et sélectionne explicitement un profil dans chacun. +Les documents spécialisés possèdent leur propre configuration globale et un `default_profile`. Ils peuvent donc être consommés directement sans fichier de composition. -Le desktop utilise par défaut : +Un fichier `.default.config.json` n'est nécessaire que lorsqu'un binaire veut : + +- choisir des fichiers spécialisés différents des fichiers canoniques ; +- sélectionner un profil différent du `default_profile` d'un document ; +- ajouter des paramètres propres au binaire. + +La composition du desktop est : ```text config/kb-app-demo-desktop.default.config.json ``` -Le CLI des scénarios possède également une composition explicite : +Elle peut être remplacée par `KB_APP_DEMO_DESKTOP_CONFIG_PATH`. -```text -config/ks-pipeline-demo-scenarios.default.config.json -``` +`ks-pipeline-demo-scenarios` n'a plus de composition dédiée par défaut : il consomme directement les defaults des documents partagés. `KS_DEVNET_CONFIG_PATH` reste disponible pour fournir explicitement une composition alternative lorsque cela est nécessaire. -La composition desktop peut être remplacée par `KB_APP_DEMO_DESKTOP_CONFIG_PATH`. Les tests/scénarios Devnet opt-in peuvent remplacer leur composition avec `KS_DEVNET_CONFIG_PATH`. - -## Documents partagés actuels +## Fichiers actifs ```text config/ ├── kb-app-demo-desktop.default.config.json -├── ks-pipeline-demo-scenarios.default.config.json ├── logging.config.json ├── transport.config.json ├── listeners.config.json +├── 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 └── schemas/ ├── composition.config.schema.json ├── logging.config.schema.json ├── transport.config.schema.json ├── listeners.config.schema.json + ├── store.config.schema.json + ├── wallet.config.schema.json + ├── execution.config.schema.json └── resolved.app.config.schema.json ``` -`resolved.app.config.schema.json` décrit uniquement le contrat runtime `AppConfig/ProfileConfig` reconstruit par `ks-config`. Il ne correspond pas à un fichier runtime chargé directement. +`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. -## Sélection des profils +## Defaults et overrides + +Chaque document partagé définit un `default_profile`. Une composition peut remplacer uniquement les sélections nécessaires. Elle ne devient pas une seconde copie des paramètres spécialisés : un override de champ reste dans le document propriétaire, tandis qu’une valeur globale déclarée comme configurable par environnement utilise son contrat `KS_*`/`KB_*`. -Les documents partagés conservent un `active_profile` pour les consommateurs autonomes. Une composition binaire peut cependant sélectionner un autre profil explicitement : ```text -composition profile mainnet_research +transport.default_profile = local_devnet +listeners.default_profile = local_devnet +store.default_profile = local_devnet +wallet.default_profile = local_devnet +execution.default_profile = local_devnet +logging.default_profile = local_devnet + +kb-app-demo-desktop profile mainnet_research logging_profile = mainnet_research transport_profile = mainnet_research listeners_profile = mainnet_research + store_profile = mainnet_research + wallet_profile = mainnet_research + execution_profile = mainnet_research ``` -Lorsqu'une composition est utilisée, sa sélection explicite prime pour le binaire. Le même `transport.config.json` ou `listeners.config.json` peut donc être réutilisé par plusieurs binaires avec des combinaisons différentes. +`ks-config` valide l'existence des fichiers référencés et des profils sélectionnés avant de construire le contrat runtime. -## Transport +## Valeurs globales hors profils -`transport.config.json` possède les endpoints HTTP et WebSocket, leurs providers, clusters, timeouts, capacités, rôles, limites et politique de reconnexion propre aux endpoints. +Une valeur qui ne dépend pas du profil reste au niveau racine de son document spécialisé. -`ks-onchain-transport` peut consommer directement un `TransportProfileConfig`. Un futur worker n'a donc pas besoin de dépendre d'un `ProfileConfig` applicatif complet pour construire ses pools réseau. +Actuellement : -## Listeners +- `logging.config.json.logs_directory` vaut `${KS_LOGS_DIRECTORY:-logs}` ; +- `wallet.config.json.wallets_directory` vaut `${KS_WALLETS_DIRECTORY:-wallets}`. -`listeners.config.json` possède les ensembles de listeners Solana : +Ces valeurs peuvent être remplacées par l'environnement ou `.env` sans dupliquer un chemin dans tous les profils. -- `logsSubscribe` par mention de programme ; -- `programSubscribe` ; -- `accountSubscribe` ; -- commitment et rôles d'endpoint associés. +## Transport WebSocket -La séparation prépare les futurs workers de capture, qui pourront associer un profil transport et un profil listeners sans recopier les définitions dans leur propre configuration. +`transport.config.json` possède des classes de defaults WebSocket nommées. Un endpoint référence une classe puis peut fournir des `overrides` propres : -## Logging +```text +standard_rpc_ws +high_capacity_rpc_ws +``` -`logging.config.json` reste possédé par `ks-logging`. Une composition sélectionne le profil logging souhaité. `KS_LOGGING_CONFIG_PATH` reste un override direct du chemin logging pour l'exploitation ; sans override, le chemin déclaré par la composition est utilisé. +Les defaults portent les timeouts, capacités de channels et `auto_reconnect`. Une future surface WebSocket avancée peut ajouter une classe dédiée sans modifier les endpoints RPC standard existants. -## Configuration encore transitoire dans la composition +## Wallet et exécution -Après `pre.006`, les sections suivantes restent temporairement dans les profils de composition : +`wallet.config.json` possède la racine de stockage et les paramètres d'identité/persistance des wallets. Les autorisations `*_send_enabled` appartiennent désormais à `execution.config.json`, avec les plafonds de dépense, frais et confirmation. -- `database` et `data` ; -- `wallet` ; -- `execution` ; -- `demo`. +## Store -Elles seront séparées dans la prerelease suivante. En particulier, `demo` est propre au desktop et ne doit pas rester durablement dans un contrat généraliste `ks-config`. +`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`. ## 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 propagation de sensibilité et les DTO publics sûrs sont traités après la fin du découpage des fichiers de configuration. +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. ## 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 fichiers historiques `app.config.json`, `example.app.config.json`, `schemas/app.config.schema.json`, `example.config.json` et `schema.config.json` sont obsolètes et ne doivent pas coexister avec cette architecture. - -## Logging de développement - -Le fichier `logging.config.json` conserve les routes globales ainsi que trois fichiers dédiés par crate opérationnelle utilisant `tracing` : `debug.log`, `info.log` et `error.jsonl`. - -Les logs ne doivent contenir ni secret, ni DSN non masqué, ni keypair, ni payload de configuration résolue complet. +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/example.execution.config.json new file mode 100644 index 0000000..cd0aa30 --- /dev/null +++ b/config/example.execution.config.json @@ -0,0 +1,26 @@ +{ + "default_profile": "local_devnet", + "profiles": [ + { + "name": "local_devnet", + "dry_run_default": true, + "require_simulation": true, + "require_operator_confirmation": true, + "localnet_max_spend_lamports": 0, + "devnet_max_spend_lamports": 100000000, + "testnet_max_spend_lamports": 0, + "mainnet_max_spend_lamports": 0, + "max_fee_lamports": 1000000, + "max_compute_unit_price_micro_lamports": 100000, + "recent_blockhash_max_age_slots": 150, + "send_max_retries": 3, + "confirmation_poll_interval_ms": 500, + "confirmation_max_attempts": 120, + "devnet_airdrop_max_lamports": 2000000000, + "localnet_send_enabled": false, + "devnet_send_enabled": true, + "testnet_send_enabled": false, + "mainnet_send_enabled": false + } + ] +} diff --git a/config/example.kb-app-demo-desktop.default.config.json b/config/example.kb-app-demo-desktop.default.config.json index ff51ed9..aed9066 100644 --- a/config/example.kb-app-demo-desktop.default.config.json +++ b/config/example.kb-app-demo-desktop.default.config.json @@ -3,67 +3,17 @@ "sources": { "logging": "config/logging.config.json", "transport": "config/transport.config.json", - "listeners": "config/listeners.config.json" + "listeners": "config/listeners.config.json", + "store": "config/store.config.json", + "wallet": "config/wallet.config.json", + "execution": "config/execution.config.json" }, "profiles": [ { "name": "local_devnet", "app": { "name": "khadhroony-bot3", - "environment": "development", - "auto_reconnect_default": false - }, - "logging_profile": "local_devnet", - "transport_profile": "local_devnet", - "listeners_profile": "local_devnet", - "database": { - "enabled": true, - "backend": "postgres", - "postgres": { - "url": "${KS_SECRET_POSTGRES_DEVNET_URL}", - "max_connections": 8, - "connect_timeout_ms": 5000, - "auto_initialize_schema": true - }, - "sqlite": { - "path": "data/local.sqlite", - "create_if_missing": true, - "busy_timeout_ms": 5000, - "max_connections": 1, - "auto_initialize_schema": true, - "use_wal": true - } - }, - "data": { - "wallets_directory": "wallets", - "logs_directory": "logs" - }, - "wallet": { - "wallet_dir": "wallets/temporary/local_devnet", - "cluster": "devnet", - "temporary_wallet_enabled": true, - "temporary_wallet_alias": "local-devnet-operator", - "temporary_wallet_persist": true, - "localnet_send_enabled": false, - "devnet_send_enabled": true, - "testnet_send_enabled": false, - "mainnet_send_enabled": false - }, - "execution": { - "dry_run_default": true, - "require_simulation": true, - "require_operator_confirmation": true, - "localnet_max_spend_lamports": 0, - "devnet_max_spend_lamports": 100000000, - "testnet_max_spend_lamports": 0, - "mainnet_max_spend_lamports": 0, - "max_fee_lamports": 1000000, - "max_compute_unit_price_micro_lamports": 100000, - "recent_blockhash_max_age_slots": 150, - "send_max_retries": 3, - "confirmation_poll_interval_ms": 500, - "confirmation_max_attempts": 120, - "devnet_airdrop_max_lamports": 2000000000 + "environment": "development" }, "demo": { "live_demo_enabled": true, diff --git a/config/example.listeners.config.json b/config/example.listeners.config.json index b1a40a2..3b20a75 100644 --- a/config/example.listeners.config.json +++ b/config/example.listeners.config.json @@ -1,5 +1,5 @@ { - "active_profile": "local_devnet", + "default_profile": "local_devnet", "profiles": [ { "name": "local_devnet", diff --git a/config/example.logging.config.json b/config/example.logging.config.json index 1025d17..cb44904 100644 --- a/config/example.logging.config.json +++ b/config/example.logging.config.json @@ -1,5 +1,6 @@ { - "active_profile": "local_devnet", + "logs_directory": "${KS_LOGS_DIRECTORY:-logs}", + "default_profile": "local_devnet", "profiles": [ { "name": "local_devnet", @@ -23,7 +24,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/devnet/debug.log", + "path": "devnet/debug.log", "rotation": "daily", "format": "human", "ansi": false, diff --git a/config/example.store.config.json b/config/example.store.config.json new file mode 100644 index 0000000..10961cb --- /dev/null +++ b/config/example.store.config.json @@ -0,0 +1,26 @@ +{ + "default_profile": "local_devnet", + "profiles": [ + { + "name": "local_devnet", + "database": { + "enabled": true, + "backend": "postgres", + "postgres": { + "url": "${KS_SECRET_POSTGRES_DEVNET_URL}", + "max_connections": 8, + "connect_timeout_ms": 5000, + "auto_initialize_schema": true + }, + "sqlite": { + "path": "data/local.sqlite", + "create_if_missing": true, + "busy_timeout_ms": 5000, + "max_connections": 1, + "auto_initialize_schema": true, + "use_wal": true + } + } + } + ] +} diff --git a/config/example.transport.config.json b/config/example.transport.config.json index 230211a..2d4b8c7 100644 --- a/config/example.transport.config.json +++ b/config/example.transport.config.json @@ -1,5 +1,25 @@ { - "active_profile": "local_devnet", + "default_profile": "local_devnet", + "ws_endpoint_defaults": [ + { + "name": "standard_rpc_ws", + "connect_timeout_ms": 5000, + "request_timeout_ms": 10000, + "unsubscribe_timeout_ms": 1500, + "write_channel_capacity": 256, + "event_channel_capacity": 1024, + "auto_reconnect": false + }, + { + "name": "high_capacity_rpc_ws", + "connect_timeout_ms": 5000, + "request_timeout_ms": 10000, + "unsubscribe_timeout_ms": 1500, + "write_channel_capacity": 512, + "event_channel_capacity": 4096, + "auto_reconnect": false + } + ], "profiles": [ { "name": "local_devnet", @@ -97,12 +117,7 @@ "provider": "solana-public", "cluster": "devnet", "url": "wss://api.devnet.solana.com", - "connect_timeout_ms": 5000, - "request_timeout_ms": 10000, - "unsubscribe_timeout_ms": 1500, - "write_channel_capacity": 256, - "event_channel_capacity": 1024, - "auto_reconnect": false, + "defaults": "standard_rpc_ws", "roles": [ { "role": "slot_notifications", diff --git a/config/example.wallet.config.json b/config/example.wallet.config.json new file mode 100644 index 0000000..883fc26 --- /dev/null +++ b/config/example.wallet.config.json @@ -0,0 +1,14 @@ +{ + "wallets_directory": "${KS_WALLETS_DIRECTORY:-wallets}", + "default_profile": "local_devnet", + "profiles": [ + { + "name": "local_devnet", + "directory": "temporary/local_devnet", + "cluster": "devnet", + "temporary_wallet_enabled": true, + "temporary_wallet_alias": "local-devnet-operator", + "temporary_wallet_persist": true + } + ] +} diff --git a/config/execution.config.json b/config/execution.config.json new file mode 100644 index 0000000..f14413c --- /dev/null +++ b/config/execution.config.json @@ -0,0 +1,68 @@ +{ + "default_profile": "local_devnet", + "profiles": [ + { + "name": "local_devnet", + "dry_run_default": true, + "require_simulation": true, + "require_operator_confirmation": true, + "localnet_max_spend_lamports": 0, + "devnet_max_spend_lamports": 100000000, + "testnet_max_spend_lamports": 0, + "mainnet_max_spend_lamports": 0, + "max_fee_lamports": 1000000, + "max_compute_unit_price_micro_lamports": 100000, + "recent_blockhash_max_age_slots": 150, + "send_max_retries": 3, + "confirmation_poll_interval_ms": 500, + "confirmation_max_attempts": 120, + "devnet_airdrop_max_lamports": 2000000000, + "localnet_send_enabled": false, + "devnet_send_enabled": true, + "testnet_send_enabled": false, + "mainnet_send_enabled": false + }, + { + "name": "mainnet_research", + "dry_run_default": true, + "require_simulation": true, + "require_operator_confirmation": true, + "localnet_max_spend_lamports": 0, + "devnet_max_spend_lamports": 0, + "testnet_max_spend_lamports": 0, + "mainnet_max_spend_lamports": 0, + "max_fee_lamports": 1000000, + "max_compute_unit_price_micro_lamports": 100000, + "recent_blockhash_max_age_slots": 150, + "send_max_retries": 3, + "confirmation_poll_interval_ms": 500, + "confirmation_max_attempts": 120, + "devnet_airdrop_max_lamports": 0, + "localnet_send_enabled": false, + "devnet_send_enabled": false, + "testnet_send_enabled": false, + "mainnet_send_enabled": false + }, + { + "name": "mainnet", + "dry_run_default": true, + "require_simulation": true, + "require_operator_confirmation": true, + "localnet_max_spend_lamports": 0, + "devnet_max_spend_lamports": 0, + "testnet_max_spend_lamports": 0, + "mainnet_max_spend_lamports": 0, + "max_fee_lamports": 1000000, + "max_compute_unit_price_micro_lamports": 100000, + "recent_blockhash_max_age_slots": 150, + "send_max_retries": 3, + "confirmation_poll_interval_ms": 500, + "confirmation_max_attempts": 120, + "devnet_airdrop_max_lamports": 0, + "localnet_send_enabled": false, + "devnet_send_enabled": false, + "testnet_send_enabled": false, + "mainnet_send_enabled": false + } + ] +} diff --git a/config/kb-app-demo-desktop.default.config.json b/config/kb-app-demo-desktop.default.config.json index 7101a88..6bfabbe 100644 --- a/config/kb-app-demo-desktop.default.config.json +++ b/config/kb-app-demo-desktop.default.config.json @@ -3,67 +3,17 @@ "sources": { "logging": "config/logging.config.json", "transport": "config/transport.config.json", - "listeners": "config/listeners.config.json" + "listeners": "config/listeners.config.json", + "store": "config/store.config.json", + "wallet": "config/wallet.config.json", + "execution": "config/execution.config.json" }, "profiles": [ { "name": "local_devnet", "app": { "name": "khadhroony-bot3", - "environment": "development", - "auto_reconnect_default": false - }, - "logging_profile": "local_devnet", - "transport_profile": "local_devnet", - "listeners_profile": "local_devnet", - "database": { - "enabled": true, - "backend": "postgres", - "postgres": { - "url": "${KS_SECRET_POSTGRES_DEVNET_URL}", - "max_connections": 8, - "connect_timeout_ms": 5000, - "auto_initialize_schema": true - }, - "sqlite": { - "path": "data/local.sqlite", - "create_if_missing": true, - "busy_timeout_ms": 5000, - "max_connections": 1, - "auto_initialize_schema": true, - "use_wal": true - } - }, - "data": { - "wallets_directory": "wallets", - "logs_directory": "logs" - }, - "wallet": { - "wallet_dir": "wallets/temporary/local_devnet", - "cluster": "devnet", - "temporary_wallet_enabled": true, - "temporary_wallet_alias": "local-devnet-operator", - "temporary_wallet_persist": true, - "localnet_send_enabled": false, - "devnet_send_enabled": true, - "testnet_send_enabled": false, - "mainnet_send_enabled": false - }, - "execution": { - "dry_run_default": true, - "require_simulation": true, - "require_operator_confirmation": true, - "localnet_max_spend_lamports": 0, - "devnet_max_spend_lamports": 100000000, - "testnet_max_spend_lamports": 0, - "mainnet_max_spend_lamports": 0, - "max_fee_lamports": 1000000, - "max_compute_unit_price_micro_lamports": 100000, - "recent_blockhash_max_age_slots": 150, - "send_max_retries": 3, - "confirmation_poll_interval_ms": 500, - "confirmation_max_attempts": 120, - "devnet_airdrop_max_lamports": 2000000000 + "environment": "development" }, "demo": { "live_demo_enabled": true, @@ -74,129 +24,35 @@ "name": "mainnet_research", "app": { "name": "khadhroony-bot3", - "environment": "research", - "auto_reconnect_default": false - }, - "logging_profile": "mainnet_research", - "transport_profile": "mainnet_research", - "listeners_profile": "mainnet_research", - "database": { - "enabled": true, - "backend": "postgres", - "postgres": { - "url": "${KS_SECRET_POSTGRES_MAINNET_URL}", - "max_connections": 16, - "connect_timeout_ms": 5000, - "auto_initialize_schema": true - }, - "sqlite": { - "path": "data/mainnet-import.sqlite", - "create_if_missing": true, - "busy_timeout_ms": 5000, - "max_connections": 1, - "auto_initialize_schema": true, - "use_wal": true - } - }, - "data": { - "wallets_directory": "wallets", - "logs_directory": "logs" - }, - "wallet": { - "wallet_dir": "wallets/temporary/mainnet_research", - "cluster": "mainnet-beta", - "temporary_wallet_enabled": false, - "temporary_wallet_alias": "mainnet_research-disabled", - "temporary_wallet_persist": false, - "localnet_send_enabled": false, - "devnet_send_enabled": false, - "testnet_send_enabled": false, - "mainnet_send_enabled": false - }, - "execution": { - "dry_run_default": true, - "require_simulation": true, - "require_operator_confirmation": true, - "localnet_max_spend_lamports": 0, - "devnet_max_spend_lamports": 0, - "testnet_max_spend_lamports": 0, - "mainnet_max_spend_lamports": 0, - "max_fee_lamports": 1000000, - "max_compute_unit_price_micro_lamports": 100000, - "recent_blockhash_max_age_slots": 150, - "send_max_retries": 3, - "confirmation_poll_interval_ms": 500, - "confirmation_max_attempts": 120, - "devnet_airdrop_max_lamports": 0 + "environment": "research" }, "demo": { "live_demo_enabled": true, "trading_demo_enabled": false - } + }, + "logging_profile": "mainnet_research", + "transport_profile": "mainnet_research", + "listeners_profile": "mainnet_research", + "store_profile": "mainnet_research", + "wallet_profile": "mainnet_research", + "execution_profile": "mainnet_research" }, { "name": "mainnet", "app": { "name": "khadhroony-bot3", - "environment": "mainnet", - "auto_reconnect_default": false - }, - "logging_profile": "mainnet", - "transport_profile": "mainnet", - "listeners_profile": "mainnet", - "database": { - "enabled": true, - "backend": "postgres", - "postgres": { - "url": "${KS_SECRET_POSTGRES_MAINNET_URL}", - "max_connections": 16, - "connect_timeout_ms": 5000, - "auto_initialize_schema": true - }, - "sqlite": { - "path": "data/mainnet.sqlite", - "create_if_missing": true, - "busy_timeout_ms": 5000, - "max_connections": 1, - "auto_initialize_schema": true, - "use_wal": true - } - }, - "data": { - "wallets_directory": "wallets", - "logs_directory": "logs" - }, - "wallet": { - "wallet_dir": "wallets/temporary/mainnet", - "cluster": "mainnet-beta", - "temporary_wallet_enabled": false, - "temporary_wallet_alias": "mainnet-disabled", - "temporary_wallet_persist": false, - "localnet_send_enabled": false, - "devnet_send_enabled": false, - "testnet_send_enabled": false, - "mainnet_send_enabled": false - }, - "execution": { - "dry_run_default": true, - "require_simulation": true, - "require_operator_confirmation": true, - "localnet_max_spend_lamports": 0, - "devnet_max_spend_lamports": 0, - "testnet_max_spend_lamports": 0, - "mainnet_max_spend_lamports": 0, - "max_fee_lamports": 1000000, - "max_compute_unit_price_micro_lamports": 100000, - "recent_blockhash_max_age_slots": 150, - "send_max_retries": 3, - "confirmation_poll_interval_ms": 500, - "confirmation_max_attempts": 120, - "devnet_airdrop_max_lamports": 0 + "environment": "mainnet" }, "demo": { "live_demo_enabled": true, "trading_demo_enabled": false - } + }, + "logging_profile": "mainnet", + "transport_profile": "mainnet", + "listeners_profile": "mainnet", + "store_profile": "mainnet", + "wallet_profile": "mainnet", + "execution_profile": "mainnet" } ] } diff --git a/config/ks-pipeline-demo-scenarios.default.config.json b/config/ks-pipeline-demo-scenarios.default.config.json deleted file mode 100644 index 97d9eaf..0000000 --- a/config/ks-pipeline-demo-scenarios.default.config.json +++ /dev/null @@ -1,202 +0,0 @@ -{ - "active_profile": "local_devnet", - "sources": { - "logging": "config/logging.config.json", - "transport": "config/transport.config.json", - "listeners": "config/listeners.config.json" - }, - "profiles": [ - { - "name": "local_devnet", - "app": { - "name": "khadhroony-bot3", - "environment": "development", - "auto_reconnect_default": false - }, - "logging_profile": "local_devnet", - "transport_profile": "local_devnet", - "listeners_profile": "local_devnet", - "database": { - "enabled": true, - "backend": "postgres", - "postgres": { - "url": "${KS_SECRET_POSTGRES_DEVNET_URL}", - "max_connections": 8, - "connect_timeout_ms": 5000, - "auto_initialize_schema": true - }, - "sqlite": { - "path": "data/local.sqlite", - "create_if_missing": true, - "busy_timeout_ms": 5000, - "max_connections": 1, - "auto_initialize_schema": true, - "use_wal": true - } - }, - "data": { - "wallets_directory": "wallets", - "logs_directory": "logs" - }, - "wallet": { - "wallet_dir": "wallets/temporary/local_devnet", - "cluster": "devnet", - "temporary_wallet_enabled": true, - "temporary_wallet_alias": "local-devnet-operator", - "temporary_wallet_persist": true, - "localnet_send_enabled": false, - "devnet_send_enabled": true, - "testnet_send_enabled": false, - "mainnet_send_enabled": false - }, - "execution": { - "dry_run_default": true, - "require_simulation": true, - "require_operator_confirmation": true, - "localnet_max_spend_lamports": 0, - "devnet_max_spend_lamports": 100000000, - "testnet_max_spend_lamports": 0, - "mainnet_max_spend_lamports": 0, - "max_fee_lamports": 1000000, - "max_compute_unit_price_micro_lamports": 100000, - "recent_blockhash_max_age_slots": 150, - "send_max_retries": 3, - "confirmation_poll_interval_ms": 500, - "confirmation_max_attempts": 120, - "devnet_airdrop_max_lamports": 2000000000 - }, - "demo": { - "live_demo_enabled": true, - "trading_demo_enabled": false - } - }, - { - "name": "mainnet_research", - "app": { - "name": "khadhroony-bot3", - "environment": "research", - "auto_reconnect_default": false - }, - "logging_profile": "mainnet_research", - "transport_profile": "mainnet_research", - "listeners_profile": "mainnet_research", - "database": { - "enabled": true, - "backend": "postgres", - "postgres": { - "url": "${KS_SECRET_POSTGRES_MAINNET_URL}", - "max_connections": 16, - "connect_timeout_ms": 5000, - "auto_initialize_schema": true - }, - "sqlite": { - "path": "data/mainnet-import.sqlite", - "create_if_missing": true, - "busy_timeout_ms": 5000, - "max_connections": 1, - "auto_initialize_schema": true, - "use_wal": true - } - }, - "data": { - "wallets_directory": "wallets", - "logs_directory": "logs" - }, - "wallet": { - "wallet_dir": "wallets/temporary/mainnet_research", - "cluster": "mainnet-beta", - "temporary_wallet_enabled": false, - "temporary_wallet_alias": "mainnet_research-disabled", - "temporary_wallet_persist": false, - "localnet_send_enabled": false, - "devnet_send_enabled": false, - "testnet_send_enabled": false, - "mainnet_send_enabled": false - }, - "execution": { - "dry_run_default": true, - "require_simulation": true, - "require_operator_confirmation": true, - "localnet_max_spend_lamports": 0, - "devnet_max_spend_lamports": 0, - "testnet_max_spend_lamports": 0, - "mainnet_max_spend_lamports": 0, - "max_fee_lamports": 1000000, - "max_compute_unit_price_micro_lamports": 100000, - "recent_blockhash_max_age_slots": 150, - "send_max_retries": 3, - "confirmation_poll_interval_ms": 500, - "confirmation_max_attempts": 120, - "devnet_airdrop_max_lamports": 0 - }, - "demo": { - "live_demo_enabled": true, - "trading_demo_enabled": false - } - }, - { - "name": "mainnet", - "app": { - "name": "khadhroony-bot3", - "environment": "mainnet", - "auto_reconnect_default": false - }, - "logging_profile": "mainnet", - "transport_profile": "mainnet", - "listeners_profile": "mainnet", - "database": { - "enabled": true, - "backend": "postgres", - "postgres": { - "url": "${KS_SECRET_POSTGRES_MAINNET_URL}", - "max_connections": 16, - "connect_timeout_ms": 5000, - "auto_initialize_schema": true - }, - "sqlite": { - "path": "data/mainnet.sqlite", - "create_if_missing": true, - "busy_timeout_ms": 5000, - "max_connections": 1, - "auto_initialize_schema": true, - "use_wal": true - } - }, - "data": { - "wallets_directory": "wallets", - "logs_directory": "logs" - }, - "wallet": { - "wallet_dir": "wallets/temporary/mainnet", - "cluster": "mainnet-beta", - "temporary_wallet_enabled": false, - "temporary_wallet_alias": "mainnet-disabled", - "temporary_wallet_persist": false, - "localnet_send_enabled": false, - "devnet_send_enabled": false, - "testnet_send_enabled": false, - "mainnet_send_enabled": false - }, - "execution": { - "dry_run_default": true, - "require_simulation": true, - "require_operator_confirmation": true, - "localnet_max_spend_lamports": 0, - "devnet_max_spend_lamports": 0, - "testnet_max_spend_lamports": 0, - "mainnet_max_spend_lamports": 0, - "max_fee_lamports": 1000000, - "max_compute_unit_price_micro_lamports": 100000, - "recent_blockhash_max_age_slots": 150, - "send_max_retries": 3, - "confirmation_poll_interval_ms": 500, - "confirmation_max_attempts": 120, - "devnet_airdrop_max_lamports": 0 - }, - "demo": { - "live_demo_enabled": true, - "trading_demo_enabled": false - } - } - ] -} diff --git a/config/listeners.config.json b/config/listeners.config.json index 0bf9388..3d97f6b 100644 --- a/config/listeners.config.json +++ b/config/listeners.config.json @@ -1,5 +1,5 @@ { - "active_profile": "mainnet_research", + "default_profile": "local_devnet", "profiles": [ { "name": "local_devnet", diff --git a/config/logging.config.json b/config/logging.config.json index f702ef9..ebc0e9e 100644 --- a/config/logging.config.json +++ b/config/logging.config.json @@ -1,5 +1,6 @@ { - "active_profile": "mainnet_research", + "logs_directory": "${KS_LOGS_DIRECTORY:-logs}", + "default_profile": "local_devnet", "profiles": [ { "name": "local_devnet", @@ -23,7 +24,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/devnet/debug.log", + "path": "devnet/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -36,7 +37,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/devnet/info.log", + "path": "devnet/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -49,7 +50,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/devnet/error.jsonl", + "path": "devnet/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -62,7 +63,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/devnet/app.log", + "path": "devnet/app.log", "rotation": "daily", "format": "human", "ansi": false, @@ -75,7 +76,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/devnet/ks-lib/debug.log", + "path": "devnet/ks-lib/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -88,7 +89,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/devnet/ks-lib/info.log", + "path": "devnet/ks-lib/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -101,7 +102,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/devnet/ks-lib/error.jsonl", + "path": "devnet/ks-lib/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -114,7 +115,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/devnet/kb-app-demo-desktop/debug.log", + "path": "devnet/kb-app-demo-desktop/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -127,7 +128,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/devnet/kb-app-demo-desktop/info.log", + "path": "devnet/kb-app-demo-desktop/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -140,7 +141,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/devnet/kb-app-demo-desktop/error.jsonl", + "path": "devnet/kb-app-demo-desktop/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -153,7 +154,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/devnet/ks-lib/decoder/solana/core/debug.log", + "path": "devnet/ks-lib/decoder/solana/core/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -166,7 +167,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/devnet/ks-lib/decoder/solana/core/info.log", + "path": "devnet/ks-lib/decoder/solana/core/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -179,7 +180,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/devnet/ks-lib/decoder/solana/core/error.jsonl", + "path": "devnet/ks-lib/decoder/solana/core/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -192,7 +193,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/devnet/ks-lib/decoder/spl/memo/debug.log", + "path": "devnet/ks-lib/decoder/spl/memo/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -205,7 +206,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/devnet/ks-lib/decoder/spl/memo/info.log", + "path": "devnet/ks-lib/decoder/spl/memo/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -218,7 +219,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/devnet/ks-lib/decoder/spl/memo/error.jsonl", + "path": "devnet/ks-lib/decoder/spl/memo/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -231,7 +232,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/devnet/ks-lib/decoder/spl/associated_token_account/debug.log", + "path": "devnet/ks-lib/decoder/spl/associated_token_account/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -244,7 +245,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/devnet/ks-lib/decoder/spl/associated_token_account/info.log", + "path": "devnet/ks-lib/decoder/spl/associated_token_account/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -257,7 +258,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/devnet/ks-lib/decoder/spl/associated_token_account/error.jsonl", + "path": "devnet/ks-lib/decoder/spl/associated_token_account/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -270,7 +271,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/devnet/ks-lib/decoder/spl/token/debug.log", + "path": "devnet/ks-lib/decoder/spl/token/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -283,7 +284,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/devnet/ks-lib/decoder/spl/token/info.log", + "path": "devnet/ks-lib/decoder/spl/token/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -296,7 +297,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/devnet/ks-lib/decoder/spl/token/error.jsonl", + "path": "devnet/ks-lib/decoder/spl/token/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -309,7 +310,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/devnet/ks-lib/executor/solana/core/debug.log", + "path": "devnet/ks-lib/executor/solana/core/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -322,7 +323,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/devnet/ks-lib/executor/solana/core/info.log", + "path": "devnet/ks-lib/executor/solana/core/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -335,7 +336,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/devnet/ks-lib/executor/solana/core/error.jsonl", + "path": "devnet/ks-lib/executor/solana/core/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -348,7 +349,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/devnet/ks-lib/executor/solana/transaction/debug.log", + "path": "devnet/ks-lib/executor/solana/transaction/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -361,7 +362,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/devnet/ks-lib/executor/solana/transaction/info.log", + "path": "devnet/ks-lib/executor/solana/transaction/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -374,7 +375,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/devnet/ks-lib/executor/solana/transaction/error.jsonl", + "path": "devnet/ks-lib/executor/solana/transaction/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -387,7 +388,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/devnet/ks-lib/executor/spl/memo/debug.log", + "path": "devnet/ks-lib/executor/spl/memo/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -400,7 +401,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/devnet/ks-lib/executor/spl/memo/info.log", + "path": "devnet/ks-lib/executor/spl/memo/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -413,7 +414,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/devnet/ks-lib/executor/spl/memo/error.jsonl", + "path": "devnet/ks-lib/executor/spl/memo/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -426,7 +427,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/devnet/ks-lib/executor/spl/associated_token_account/debug.log", + "path": "devnet/ks-lib/executor/spl/associated_token_account/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -439,7 +440,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/devnet/ks-lib/executor/spl/associated_token_account/info.log", + "path": "devnet/ks-lib/executor/spl/associated_token_account/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -452,7 +453,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/devnet/ks-lib/executor/spl/associated_token_account/error.jsonl", + "path": "devnet/ks-lib/executor/spl/associated_token_account/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -465,7 +466,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/devnet/ks-lib/executor/spl/token/debug.log", + "path": "devnet/ks-lib/executor/spl/token/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -478,7 +479,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/devnet/ks-lib/executor/spl/token/info.log", + "path": "devnet/ks-lib/executor/spl/token/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -491,7 +492,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/devnet/ks-lib/executor/spl/token/error.jsonl", + "path": "devnet/ks-lib/executor/spl/token/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -504,7 +505,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/devnet/ks-logging/debug.log", + "path": "devnet/ks-logging/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -517,7 +518,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/devnet/ks-logging/info.log", + "path": "devnet/ks-logging/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -530,7 +531,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/devnet/ks-logging/error.jsonl", + "path": "devnet/ks-logging/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -543,7 +544,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/devnet/ks-lib/materializer/admin/debug.log", + "path": "devnet/ks-lib/materializer/admin/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -556,7 +557,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/devnet/ks-lib/materializer/admin/info.log", + "path": "devnet/ks-lib/materializer/admin/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -569,7 +570,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/devnet/ks-lib/materializer/admin/error.jsonl", + "path": "devnet/ks-lib/materializer/admin/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -582,7 +583,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/devnet/ks-lib/materializer/compliance/audit/debug.log", + "path": "devnet/ks-lib/materializer/compliance/audit/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -595,7 +596,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/devnet/ks-lib/materializer/compliance/audit/info.log", + "path": "devnet/ks-lib/materializer/compliance/audit/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -608,7 +609,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/devnet/ks-lib/materializer/compliance/audit/error.jsonl", + "path": "devnet/ks-lib/materializer/compliance/audit/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -621,7 +622,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/devnet/ks-lib/materializer/lifecycle/debug.log", + "path": "devnet/ks-lib/materializer/lifecycle/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -634,7 +635,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/devnet/ks-lib/materializer/lifecycle/info.log", + "path": "devnet/ks-lib/materializer/lifecycle/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -647,7 +648,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/devnet/ks-lib/materializer/lifecycle/error.jsonl", + "path": "devnet/ks-lib/materializer/lifecycle/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -660,7 +661,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/devnet/ks-lib/materializer/transaction/annotations/debug.log", + "path": "devnet/ks-lib/materializer/transaction/annotations/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -673,7 +674,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/devnet/ks-lib/materializer/transaction/annotations/info.log", + "path": "devnet/ks-lib/materializer/transaction/annotations/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -686,7 +687,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/devnet/ks-lib/materializer/transaction/annotations/error.jsonl", + "path": "devnet/ks-lib/materializer/transaction/annotations/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -699,7 +700,7 @@ "enabled": true, "level": "debug", "format": "human", - "path": "logs/devnet/ks-lib/materializer/staking/debug.log", + "path": "devnet/ks-lib/materializer/staking/debug.log", "targets": [ "ks-lib-materializer.staking" ], @@ -712,7 +713,7 @@ "enabled": true, "level": "info", "format": "human", - "path": "logs/devnet/ks-lib/materializer/staking/info.log", + "path": "devnet/ks-lib/materializer/staking/info.log", "targets": [ "ks-lib-materializer.staking" ], @@ -725,7 +726,7 @@ "enabled": true, "level": "error", "format": "json", - "path": "logs/devnet/ks-lib/materializer/staking/error.jsonl", + "path": "devnet/ks-lib/materializer/staking/error.jsonl", "targets": [ "ks-lib-materializer.staking" ], @@ -738,7 +739,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/devnet/ks-pipeline/debug.log", + "path": "devnet/ks-pipeline/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -752,7 +753,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/devnet/ks-pipeline/info.log", + "path": "devnet/ks-pipeline/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -766,7 +767,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/devnet/ks-pipeline/error.jsonl", + "path": "devnet/ks-pipeline/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -780,7 +781,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/devnet/ks-onchain-transport/debug.log", + "path": "devnet/ks-onchain-transport/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -793,7 +794,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/devnet/ks-onchain-transport/info.log", + "path": "devnet/ks-onchain-transport/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -806,7 +807,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/devnet/ks-onchain-transport/error.jsonl", + "path": "devnet/ks-onchain-transport/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -819,7 +820,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/devnet/ks-store/debug.log", + "path": "devnet/ks-store/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -832,7 +833,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/devnet/ks-store/info.log", + "path": "devnet/ks-store/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -845,7 +846,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/devnet/ks-store/error.jsonl", + "path": "devnet/ks-store/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -858,7 +859,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/devnet/ks-wallet/debug.log", + "path": "devnet/ks-wallet/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -871,7 +872,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/devnet/ks-wallet/info.log", + "path": "devnet/ks-wallet/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -884,7 +885,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/devnet/ks-wallet/error.jsonl", + "path": "devnet/ks-wallet/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -897,7 +898,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/devnet/ks-lib/materializer/token/accounts/debug.log", + "path": "devnet/ks-lib/materializer/token/accounts/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -910,7 +911,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/devnet/ks-lib/materializer/token/accounts/info.log", + "path": "devnet/ks-lib/materializer/token/accounts/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -923,7 +924,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/devnet/ks-lib/materializer/token/accounts/error.jsonl", + "path": "devnet/ks-lib/materializer/token/accounts/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -936,7 +937,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/devnet/ks-lib/materializer/risk/debug.log", + "path": "devnet/ks-lib/materializer/risk/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -949,7 +950,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/devnet/ks-lib/materializer/risk/info.log", + "path": "devnet/ks-lib/materializer/risk/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -962,7 +963,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/devnet/ks-lib/materializer/risk/error.jsonl", + "path": "devnet/ks-lib/materializer/risk/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -975,7 +976,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/devnet/ks-lib/decoder/spl/token_2022/debug.log", + "path": "devnet/ks-lib/decoder/spl/token_2022/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -988,7 +989,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/devnet/ks-lib/decoder/spl/token_2022/info.log", + "path": "devnet/ks-lib/decoder/spl/token_2022/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -1001,7 +1002,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/devnet/ks-lib/decoder/spl/token_2022/error.jsonl", + "path": "devnet/ks-lib/decoder/spl/token_2022/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -1014,7 +1015,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/devnet/ks-lib/decoder/metadata/metaplex_token_metadata/debug.log", + "path": "devnet/ks-lib/decoder/metadata/metaplex_token_metadata/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -1027,7 +1028,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/devnet/ks-lib/decoder/metadata/metaplex_token_metadata/info.log", + "path": "devnet/ks-lib/decoder/metadata/metaplex_token_metadata/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -1040,7 +1041,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/devnet/ks-lib/decoder/metadata/metaplex_token_metadata/error.jsonl", + "path": "devnet/ks-lib/decoder/metadata/metaplex_token_metadata/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -1053,7 +1054,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/devnet/ks-lib/decoder/spl/elgamal_registry/debug.log", + "path": "devnet/ks-lib/decoder/spl/elgamal_registry/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -1066,7 +1067,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/devnet/ks-lib/decoder/spl/elgamal_registry/info.log", + "path": "devnet/ks-lib/decoder/spl/elgamal_registry/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -1079,7 +1080,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/devnet/ks-lib/decoder/spl/elgamal_registry/error.jsonl", + "path": "devnet/ks-lib/decoder/spl/elgamal_registry/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -1092,7 +1093,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/devnet/ks-lib/executor/spl/elgamal_registry/debug.log", + "path": "devnet/ks-lib/executor/spl/elgamal_registry/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -1105,7 +1106,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/devnet/ks-lib/executor/spl/elgamal_registry/info.log", + "path": "devnet/ks-lib/executor/spl/elgamal_registry/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -1118,7 +1119,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/devnet/ks-lib/executor/spl/elgamal_registry/error.jsonl", + "path": "devnet/ks-lib/executor/spl/elgamal_registry/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -1131,7 +1132,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/devnet/ks-lib/executor/spl/token_2022/debug.log", + "path": "devnet/ks-lib/executor/spl/token_2022/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -1144,7 +1145,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/devnet/ks-lib/executor/spl/token_2022/info.log", + "path": "devnet/ks-lib/executor/spl/token_2022/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -1157,7 +1158,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/devnet/ks-lib/executor/spl/token_2022/error.jsonl", + "path": "devnet/ks-lib/executor/spl/token_2022/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -1207,7 +1208,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/mainnet_research/debug.log", + "path": "mainnet_research/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -1220,7 +1221,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/mainnet_research/info.log", + "path": "mainnet_research/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -1233,7 +1234,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/mainnet_research/error.jsonl", + "path": "mainnet_research/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -1246,7 +1247,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/mainnet_research/app.log", + "path": "mainnet_research/app.log", "rotation": "daily", "format": "human", "ansi": false, @@ -1259,7 +1260,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/mainnet_research/ks-lib/debug.log", + "path": "mainnet_research/ks-lib/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -1272,7 +1273,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/mainnet_research/ks-lib/info.log", + "path": "mainnet_research/ks-lib/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -1285,7 +1286,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/mainnet_research/ks-lib/error.jsonl", + "path": "mainnet_research/ks-lib/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -1298,7 +1299,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/mainnet_research/kb-app-demo-desktop/debug.log", + "path": "mainnet_research/kb-app-demo-desktop/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -1311,7 +1312,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/mainnet_research/kb-app-demo-desktop/info.log", + "path": "mainnet_research/kb-app-demo-desktop/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -1324,7 +1325,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/mainnet_research/kb-app-demo-desktop/error.jsonl", + "path": "mainnet_research/kb-app-demo-desktop/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -1337,7 +1338,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/mainnet_research/ks-lib/decoder/solana/core/debug.log", + "path": "mainnet_research/ks-lib/decoder/solana/core/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -1350,7 +1351,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/mainnet_research/ks-lib/decoder/solana/core/info.log", + "path": "mainnet_research/ks-lib/decoder/solana/core/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -1363,7 +1364,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/mainnet_research/ks-lib/decoder/solana/core/error.jsonl", + "path": "mainnet_research/ks-lib/decoder/solana/core/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -1376,7 +1377,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/mainnet_research/ks-lib/decoder/spl/memo/debug.log", + "path": "mainnet_research/ks-lib/decoder/spl/memo/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -1389,7 +1390,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/mainnet_research/ks-lib/decoder/spl/memo/info.log", + "path": "mainnet_research/ks-lib/decoder/spl/memo/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -1402,7 +1403,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/mainnet_research/ks-lib/decoder/spl/memo/error.jsonl", + "path": "mainnet_research/ks-lib/decoder/spl/memo/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -1415,7 +1416,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/mainnet_research/ks-lib/decoder/spl/associated_token_account/debug.log", + "path": "mainnet_research/ks-lib/decoder/spl/associated_token_account/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -1428,7 +1429,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/mainnet_research/ks-lib/decoder/spl/associated_token_account/info.log", + "path": "mainnet_research/ks-lib/decoder/spl/associated_token_account/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -1441,7 +1442,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/mainnet_research/ks-lib/decoder/spl/associated_token_account/error.jsonl", + "path": "mainnet_research/ks-lib/decoder/spl/associated_token_account/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -1454,7 +1455,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/mainnet_research/ks-lib/decoder/spl/token/debug.log", + "path": "mainnet_research/ks-lib/decoder/spl/token/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -1467,7 +1468,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/mainnet_research/ks-lib/decoder/spl/token/info.log", + "path": "mainnet_research/ks-lib/decoder/spl/token/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -1480,7 +1481,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/mainnet_research/ks-lib/decoder/spl/token/error.jsonl", + "path": "mainnet_research/ks-lib/decoder/spl/token/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -1493,7 +1494,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/mainnet_research/ks-lib/executor/solana/core/debug.log", + "path": "mainnet_research/ks-lib/executor/solana/core/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -1506,7 +1507,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/mainnet_research/ks-lib/executor/solana/core/info.log", + "path": "mainnet_research/ks-lib/executor/solana/core/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -1519,7 +1520,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/mainnet_research/ks-lib/executor/solana/core/error.jsonl", + "path": "mainnet_research/ks-lib/executor/solana/core/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -1532,7 +1533,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/mainnet_research/ks-lib/executor/solana/transaction/debug.log", + "path": "mainnet_research/ks-lib/executor/solana/transaction/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -1545,7 +1546,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/mainnet_research/ks-lib/executor/solana/transaction/info.log", + "path": "mainnet_research/ks-lib/executor/solana/transaction/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -1558,7 +1559,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/mainnet_research/ks-lib/executor/solana/transaction/error.jsonl", + "path": "mainnet_research/ks-lib/executor/solana/transaction/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -1571,7 +1572,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/mainnet_research/ks-lib/executor/spl/memo/debug.log", + "path": "mainnet_research/ks-lib/executor/spl/memo/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -1584,7 +1585,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/mainnet_research/ks-lib/executor/spl/memo/info.log", + "path": "mainnet_research/ks-lib/executor/spl/memo/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -1597,7 +1598,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/mainnet_research/ks-lib/executor/spl/memo/error.jsonl", + "path": "mainnet_research/ks-lib/executor/spl/memo/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -1610,7 +1611,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/mainnet_research/ks-lib/executor/spl/associated_token_account/debug.log", + "path": "mainnet_research/ks-lib/executor/spl/associated_token_account/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -1623,7 +1624,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/mainnet_research/ks-lib/executor/spl/associated_token_account/info.log", + "path": "mainnet_research/ks-lib/executor/spl/associated_token_account/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -1636,7 +1637,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/mainnet_research/ks-lib/executor/spl/associated_token_account/error.jsonl", + "path": "mainnet_research/ks-lib/executor/spl/associated_token_account/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -1649,7 +1650,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/mainnet_research/ks-lib/executor/spl/token/debug.log", + "path": "mainnet_research/ks-lib/executor/spl/token/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -1662,7 +1663,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/mainnet_research/ks-lib/executor/spl/token/info.log", + "path": "mainnet_research/ks-lib/executor/spl/token/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -1675,7 +1676,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/mainnet_research/ks-lib/executor/spl/token/error.jsonl", + "path": "mainnet_research/ks-lib/executor/spl/token/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -1688,7 +1689,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/mainnet_research/ks-logging/debug.log", + "path": "mainnet_research/ks-logging/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -1701,7 +1702,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/mainnet_research/ks-logging/info.log", + "path": "mainnet_research/ks-logging/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -1714,7 +1715,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/mainnet_research/ks-logging/error.jsonl", + "path": "mainnet_research/ks-logging/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -1727,7 +1728,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/mainnet_research/ks-lib/materializer/admin/debug.log", + "path": "mainnet_research/ks-lib/materializer/admin/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -1740,7 +1741,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/mainnet_research/ks-lib/materializer/admin/info.log", + "path": "mainnet_research/ks-lib/materializer/admin/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -1753,7 +1754,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/mainnet_research/ks-lib/materializer/admin/error.jsonl", + "path": "mainnet_research/ks-lib/materializer/admin/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -1766,7 +1767,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/mainnet_research/ks-lib/materializer/compliance/audit/debug.log", + "path": "mainnet_research/ks-lib/materializer/compliance/audit/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -1779,7 +1780,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/mainnet_research/ks-lib/materializer/compliance/audit/info.log", + "path": "mainnet_research/ks-lib/materializer/compliance/audit/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -1792,7 +1793,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/mainnet_research/ks-lib/materializer/compliance/audit/error.jsonl", + "path": "mainnet_research/ks-lib/materializer/compliance/audit/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -1805,7 +1806,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/mainnet_research/ks-lib/materializer/lifecycle/debug.log", + "path": "mainnet_research/ks-lib/materializer/lifecycle/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -1818,7 +1819,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/mainnet_research/ks-lib/materializer/lifecycle/info.log", + "path": "mainnet_research/ks-lib/materializer/lifecycle/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -1831,7 +1832,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/mainnet_research/ks-lib/materializer/lifecycle/error.jsonl", + "path": "mainnet_research/ks-lib/materializer/lifecycle/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -1844,7 +1845,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/mainnet_research/ks-lib/materializer/transaction/annotations/debug.log", + "path": "mainnet_research/ks-lib/materializer/transaction/annotations/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -1857,7 +1858,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/mainnet_research/ks-lib/materializer/transaction/annotations/info.log", + "path": "mainnet_research/ks-lib/materializer/transaction/annotations/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -1870,7 +1871,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/mainnet_research/ks-lib/materializer/transaction/annotations/error.jsonl", + "path": "mainnet_research/ks-lib/materializer/transaction/annotations/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -1883,7 +1884,7 @@ "enabled": true, "level": "debug", "format": "human", - "path": "logs/mainnet_research/ks-lib/materializer/staking/debug.log", + "path": "mainnet_research/ks-lib/materializer/staking/debug.log", "targets": [ "ks-lib-materializer.staking" ], @@ -1896,7 +1897,7 @@ "enabled": true, "level": "info", "format": "human", - "path": "logs/mainnet_research/ks-lib/materializer/staking/info.log", + "path": "mainnet_research/ks-lib/materializer/staking/info.log", "targets": [ "ks-lib-materializer.staking" ], @@ -1909,7 +1910,7 @@ "enabled": true, "level": "error", "format": "json", - "path": "logs/mainnet_research/ks-lib/materializer/staking/error.jsonl", + "path": "mainnet_research/ks-lib/materializer/staking/error.jsonl", "targets": [ "ks-lib-materializer.staking" ], @@ -1922,7 +1923,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/mainnet_research/ks-pipeline/debug.log", + "path": "mainnet_research/ks-pipeline/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -1936,7 +1937,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/mainnet_research/ks-pipeline/info.log", + "path": "mainnet_research/ks-pipeline/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -1950,7 +1951,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/mainnet_research/ks-pipeline/error.jsonl", + "path": "mainnet_research/ks-pipeline/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -1964,7 +1965,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/mainnet_research/ks-onchain-transport/debug.log", + "path": "mainnet_research/ks-onchain-transport/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -1977,7 +1978,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/mainnet_research/ks-onchain-transport/info.log", + "path": "mainnet_research/ks-onchain-transport/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -1990,7 +1991,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/mainnet_research/ks-onchain-transport/error.jsonl", + "path": "mainnet_research/ks-onchain-transport/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -2003,7 +2004,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/mainnet_research/ks-store/debug.log", + "path": "mainnet_research/ks-store/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -2016,7 +2017,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/mainnet_research/ks-store/info.log", + "path": "mainnet_research/ks-store/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -2029,7 +2030,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/mainnet_research/ks-store/error.jsonl", + "path": "mainnet_research/ks-store/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -2042,7 +2043,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/mainnet_research/ks-wallet/debug.log", + "path": "mainnet_research/ks-wallet/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -2055,7 +2056,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/mainnet_research/ks-wallet/info.log", + "path": "mainnet_research/ks-wallet/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -2068,7 +2069,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/mainnet_research/ks-wallet/error.jsonl", + "path": "mainnet_research/ks-wallet/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -2081,7 +2082,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/mainnet_research/ks-lib/materializer/token/accounts/debug.log", + "path": "mainnet_research/ks-lib/materializer/token/accounts/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -2094,7 +2095,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/mainnet_research/ks-lib/materializer/token/accounts/info.log", + "path": "mainnet_research/ks-lib/materializer/token/accounts/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -2107,7 +2108,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/mainnet_research/ks-lib/materializer/token/accounts/error.jsonl", + "path": "mainnet_research/ks-lib/materializer/token/accounts/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -2120,7 +2121,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/mainnet_research/ks-lib/materializer/risk/debug.log", + "path": "mainnet_research/ks-lib/materializer/risk/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -2133,7 +2134,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/mainnet_research/ks-lib/materializer/risk/info.log", + "path": "mainnet_research/ks-lib/materializer/risk/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -2146,7 +2147,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/mainnet_research/ks-lib/materializer/risk/error.jsonl", + "path": "mainnet_research/ks-lib/materializer/risk/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -2159,7 +2160,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/mainnet_research/ks-lib/decoder/spl/token_2022/debug.log", + "path": "mainnet_research/ks-lib/decoder/spl/token_2022/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -2172,7 +2173,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/mainnet_research/ks-lib/decoder/spl/token_2022/info.log", + "path": "mainnet_research/ks-lib/decoder/spl/token_2022/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -2185,7 +2186,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/mainnet_research/ks-lib/decoder/spl/token_2022/error.jsonl", + "path": "mainnet_research/ks-lib/decoder/spl/token_2022/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -2198,7 +2199,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/mainnet_research/ks-lib/decoder/metadata/metaplex_token_metadata/debug.log", + "path": "mainnet_research/ks-lib/decoder/metadata/metaplex_token_metadata/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -2211,7 +2212,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/mainnet_research/ks-lib/decoder/metadata/metaplex_token_metadata/info.log", + "path": "mainnet_research/ks-lib/decoder/metadata/metaplex_token_metadata/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -2224,7 +2225,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/mainnet_research/ks-lib/decoder/metadata/metaplex_token_metadata/error.jsonl", + "path": "mainnet_research/ks-lib/decoder/metadata/metaplex_token_metadata/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -2237,7 +2238,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/mainnet_research/ks-lib/decoder/spl/elgamal_registry/debug.log", + "path": "mainnet_research/ks-lib/decoder/spl/elgamal_registry/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -2250,7 +2251,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/mainnet_research/ks-lib/decoder/spl/elgamal_registry/info.log", + "path": "mainnet_research/ks-lib/decoder/spl/elgamal_registry/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -2263,7 +2264,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/mainnet_research/ks-lib/decoder/spl/elgamal_registry/error.jsonl", + "path": "mainnet_research/ks-lib/decoder/spl/elgamal_registry/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -2276,7 +2277,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/mainnet_research/ks-lib/executor/spl/elgamal_registry/debug.log", + "path": "mainnet_research/ks-lib/executor/spl/elgamal_registry/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -2289,7 +2290,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/mainnet_research/ks-lib/executor/spl/elgamal_registry/info.log", + "path": "mainnet_research/ks-lib/executor/spl/elgamal_registry/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -2302,7 +2303,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/mainnet_research/ks-lib/executor/spl/elgamal_registry/error.jsonl", + "path": "mainnet_research/ks-lib/executor/spl/elgamal_registry/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -2315,7 +2316,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/mainnet_research/ks-lib/executor/spl/token_2022/debug.log", + "path": "mainnet_research/ks-lib/executor/spl/token_2022/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -2328,7 +2329,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/mainnet_research/ks-lib/executor/spl/token_2022/info.log", + "path": "mainnet_research/ks-lib/executor/spl/token_2022/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -2341,7 +2342,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/mainnet_research/ks-lib/executor/spl/token_2022/error.jsonl", + "path": "mainnet_research/ks-lib/executor/spl/token_2022/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -2391,7 +2392,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/mainnet/debug.log", + "path": "mainnet/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -2404,7 +2405,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/mainnet/info.log", + "path": "mainnet/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -2417,7 +2418,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/mainnet/error.jsonl", + "path": "mainnet/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -2430,7 +2431,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/mainnet/app.log", + "path": "mainnet/app.log", "rotation": "daily", "format": "human", "ansi": false, @@ -2443,7 +2444,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/mainnet/ks-lib/debug.log", + "path": "mainnet/ks-lib/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -2456,7 +2457,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/mainnet/ks-lib/info.log", + "path": "mainnet/ks-lib/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -2469,7 +2470,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/mainnet/ks-lib/error.jsonl", + "path": "mainnet/ks-lib/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -2482,7 +2483,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/mainnet/kb-app-demo-desktop/debug.log", + "path": "mainnet/kb-app-demo-desktop/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -2495,7 +2496,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/mainnet/kb-app-demo-desktop/info.log", + "path": "mainnet/kb-app-demo-desktop/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -2508,7 +2509,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/mainnet/kb-app-demo-desktop/error.jsonl", + "path": "mainnet/kb-app-demo-desktop/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -2521,7 +2522,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/mainnet/ks-lib/decoder/solana/core/debug.log", + "path": "mainnet/ks-lib/decoder/solana/core/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -2534,7 +2535,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/mainnet/ks-lib/decoder/solana/core/info.log", + "path": "mainnet/ks-lib/decoder/solana/core/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -2547,7 +2548,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/mainnet/ks-lib/decoder/solana/core/error.jsonl", + "path": "mainnet/ks-lib/decoder/solana/core/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -2560,7 +2561,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/mainnet/ks-lib/decoder/spl/memo/debug.log", + "path": "mainnet/ks-lib/decoder/spl/memo/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -2573,7 +2574,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/mainnet/ks-lib/decoder/spl/memo/info.log", + "path": "mainnet/ks-lib/decoder/spl/memo/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -2586,7 +2587,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/mainnet/ks-lib/decoder/spl/memo/error.jsonl", + "path": "mainnet/ks-lib/decoder/spl/memo/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -2599,7 +2600,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/mainnet/ks-lib/decoder/spl/associated_token_account/debug.log", + "path": "mainnet/ks-lib/decoder/spl/associated_token_account/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -2612,7 +2613,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/mainnet/ks-lib/decoder/spl/associated_token_account/info.log", + "path": "mainnet/ks-lib/decoder/spl/associated_token_account/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -2625,7 +2626,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/mainnet/ks-lib/decoder/spl/associated_token_account/error.jsonl", + "path": "mainnet/ks-lib/decoder/spl/associated_token_account/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -2638,7 +2639,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/mainnet/ks-lib/decoder/spl/token/debug.log", + "path": "mainnet/ks-lib/decoder/spl/token/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -2651,7 +2652,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/mainnet/ks-lib/decoder/spl/token/info.log", + "path": "mainnet/ks-lib/decoder/spl/token/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -2664,7 +2665,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/mainnet/ks-lib/decoder/spl/token/error.jsonl", + "path": "mainnet/ks-lib/decoder/spl/token/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -2677,7 +2678,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/mainnet/ks-lib/executor/solana/core/debug.log", + "path": "mainnet/ks-lib/executor/solana/core/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -2690,7 +2691,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/mainnet/ks-lib/executor/solana/core/info.log", + "path": "mainnet/ks-lib/executor/solana/core/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -2703,7 +2704,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/mainnet/ks-lib/executor/solana/core/error.jsonl", + "path": "mainnet/ks-lib/executor/solana/core/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -2716,7 +2717,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/mainnet/ks-lib/executor/solana/transaction/debug.log", + "path": "mainnet/ks-lib/executor/solana/transaction/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -2729,7 +2730,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/mainnet/ks-lib/executor/solana/transaction/info.log", + "path": "mainnet/ks-lib/executor/solana/transaction/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -2742,7 +2743,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/mainnet/ks-lib/executor/solana/transaction/error.jsonl", + "path": "mainnet/ks-lib/executor/solana/transaction/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -2755,7 +2756,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/mainnet/ks-lib/executor/spl/memo/debug.log", + "path": "mainnet/ks-lib/executor/spl/memo/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -2768,7 +2769,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/mainnet/ks-lib/executor/spl/memo/info.log", + "path": "mainnet/ks-lib/executor/spl/memo/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -2781,7 +2782,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/mainnet/ks-lib/executor/spl/memo/error.jsonl", + "path": "mainnet/ks-lib/executor/spl/memo/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -2794,7 +2795,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/mainnet/ks-lib/executor/spl/associated_token_account/debug.log", + "path": "mainnet/ks-lib/executor/spl/associated_token_account/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -2807,7 +2808,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/mainnet/ks-lib/executor/spl/associated_token_account/info.log", + "path": "mainnet/ks-lib/executor/spl/associated_token_account/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -2820,7 +2821,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/mainnet/ks-lib/executor/spl/associated_token_account/error.jsonl", + "path": "mainnet/ks-lib/executor/spl/associated_token_account/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -2833,7 +2834,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/mainnet/ks-lib/executor/spl/token/debug.log", + "path": "mainnet/ks-lib/executor/spl/token/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -2846,7 +2847,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/mainnet/ks-lib/executor/spl/token/info.log", + "path": "mainnet/ks-lib/executor/spl/token/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -2859,7 +2860,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/mainnet/ks-lib/executor/spl/token/error.jsonl", + "path": "mainnet/ks-lib/executor/spl/token/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -2872,7 +2873,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/mainnet/ks-logging/debug.log", + "path": "mainnet/ks-logging/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -2885,7 +2886,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/mainnet/ks-logging/info.log", + "path": "mainnet/ks-logging/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -2898,7 +2899,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/mainnet/ks-logging/error.jsonl", + "path": "mainnet/ks-logging/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -2911,7 +2912,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/mainnet/ks-lib/materializer/admin/debug.log", + "path": "mainnet/ks-lib/materializer/admin/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -2924,7 +2925,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/mainnet/ks-lib/materializer/admin/info.log", + "path": "mainnet/ks-lib/materializer/admin/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -2937,7 +2938,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/mainnet/ks-lib/materializer/admin/error.jsonl", + "path": "mainnet/ks-lib/materializer/admin/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -2950,7 +2951,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/mainnet/ks-lib/materializer/compliance/audit/debug.log", + "path": "mainnet/ks-lib/materializer/compliance/audit/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -2963,7 +2964,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/mainnet/ks-lib/materializer/compliance/audit/info.log", + "path": "mainnet/ks-lib/materializer/compliance/audit/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -2976,7 +2977,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/mainnet/ks-lib/materializer/compliance/audit/error.jsonl", + "path": "mainnet/ks-lib/materializer/compliance/audit/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -2989,7 +2990,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/mainnet/ks-lib/materializer/lifecycle/debug.log", + "path": "mainnet/ks-lib/materializer/lifecycle/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -3002,7 +3003,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/mainnet/ks-lib/materializer/lifecycle/info.log", + "path": "mainnet/ks-lib/materializer/lifecycle/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -3015,7 +3016,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/mainnet/ks-lib/materializer/lifecycle/error.jsonl", + "path": "mainnet/ks-lib/materializer/lifecycle/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -3028,7 +3029,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/mainnet/ks-lib/materializer/transaction/annotations/debug.log", + "path": "mainnet/ks-lib/materializer/transaction/annotations/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -3041,7 +3042,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/mainnet/ks-lib/materializer/transaction/annotations/info.log", + "path": "mainnet/ks-lib/materializer/transaction/annotations/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -3054,7 +3055,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/mainnet/ks-lib/materializer/transaction/annotations/error.jsonl", + "path": "mainnet/ks-lib/materializer/transaction/annotations/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -3067,7 +3068,7 @@ "enabled": true, "level": "debug", "format": "human", - "path": "logs/mainnet/ks-lib/materializer/staking/debug.log", + "path": "mainnet/ks-lib/materializer/staking/debug.log", "targets": [ "ks-lib-materializer.staking" ], @@ -3080,7 +3081,7 @@ "enabled": true, "level": "info", "format": "human", - "path": "logs/mainnet/ks-lib/materializer/staking/info.log", + "path": "mainnet/ks-lib/materializer/staking/info.log", "targets": [ "ks-lib-materializer.staking" ], @@ -3093,7 +3094,7 @@ "enabled": true, "level": "error", "format": "json", - "path": "logs/mainnet/ks-lib/materializer/staking/error.jsonl", + "path": "mainnet/ks-lib/materializer/staking/error.jsonl", "targets": [ "ks-lib-materializer.staking" ], @@ -3106,7 +3107,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/mainnet/ks-pipeline/debug.log", + "path": "mainnet/ks-pipeline/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -3120,7 +3121,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/mainnet/ks-pipeline/info.log", + "path": "mainnet/ks-pipeline/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -3134,7 +3135,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/mainnet/ks-pipeline/error.jsonl", + "path": "mainnet/ks-pipeline/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -3148,7 +3149,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/mainnet/ks-onchain-transport/debug.log", + "path": "mainnet/ks-onchain-transport/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -3161,7 +3162,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/mainnet/ks-onchain-transport/info.log", + "path": "mainnet/ks-onchain-transport/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -3174,7 +3175,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/mainnet/ks-onchain-transport/error.jsonl", + "path": "mainnet/ks-onchain-transport/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -3187,7 +3188,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/mainnet/ks-store/debug.log", + "path": "mainnet/ks-store/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -3200,7 +3201,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/mainnet/ks-store/info.log", + "path": "mainnet/ks-store/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -3213,7 +3214,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/mainnet/ks-store/error.jsonl", + "path": "mainnet/ks-store/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -3226,7 +3227,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/mainnet/ks-wallet/debug.log", + "path": "mainnet/ks-wallet/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -3239,7 +3240,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/mainnet/ks-wallet/info.log", + "path": "mainnet/ks-wallet/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -3252,7 +3253,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/mainnet/ks-wallet/error.jsonl", + "path": "mainnet/ks-wallet/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -3265,7 +3266,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/mainnet/ks-lib/materializer/token/accounts/debug.log", + "path": "mainnet/ks-lib/materializer/token/accounts/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -3278,7 +3279,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/mainnet/ks-lib/materializer/token/accounts/info.log", + "path": "mainnet/ks-lib/materializer/token/accounts/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -3291,7 +3292,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/mainnet/ks-lib/materializer/token/accounts/error.jsonl", + "path": "mainnet/ks-lib/materializer/token/accounts/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -3304,7 +3305,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/mainnet/ks-lib/materializer/risk/debug.log", + "path": "mainnet/ks-lib/materializer/risk/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -3317,7 +3318,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/mainnet/ks-lib/materializer/risk/info.log", + "path": "mainnet/ks-lib/materializer/risk/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -3330,7 +3331,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/mainnet/ks-lib/materializer/risk/error.jsonl", + "path": "mainnet/ks-lib/materializer/risk/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -3343,7 +3344,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/mainnet/ks-lib/decoder/spl/token_2022/debug.log", + "path": "mainnet/ks-lib/decoder/spl/token_2022/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -3356,7 +3357,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/mainnet/ks-lib/decoder/spl/token_2022/info.log", + "path": "mainnet/ks-lib/decoder/spl/token_2022/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -3369,7 +3370,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/mainnet/ks-lib/decoder/spl/token_2022/error.jsonl", + "path": "mainnet/ks-lib/decoder/spl/token_2022/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -3382,7 +3383,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/mainnet/ks-lib/decoder/metadata/metaplex_token_metadata/debug.log", + "path": "mainnet/ks-lib/decoder/metadata/metaplex_token_metadata/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -3395,7 +3396,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/mainnet/ks-lib/decoder/metadata/metaplex_token_metadata/info.log", + "path": "mainnet/ks-lib/decoder/metadata/metaplex_token_metadata/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -3408,7 +3409,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/mainnet/ks-lib/decoder/metadata/metaplex_token_metadata/error.jsonl", + "path": "mainnet/ks-lib/decoder/metadata/metaplex_token_metadata/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -3421,7 +3422,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/mainnet/ks-lib/decoder/spl/elgamal_registry/debug.log", + "path": "mainnet/ks-lib/decoder/spl/elgamal_registry/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -3434,7 +3435,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/mainnet/ks-lib/decoder/spl/elgamal_registry/info.log", + "path": "mainnet/ks-lib/decoder/spl/elgamal_registry/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -3447,7 +3448,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/mainnet/ks-lib/decoder/spl/elgamal_registry/error.jsonl", + "path": "mainnet/ks-lib/decoder/spl/elgamal_registry/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -3460,7 +3461,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/mainnet/ks-lib/executor/spl/elgamal_registry/debug.log", + "path": "mainnet/ks-lib/executor/spl/elgamal_registry/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -3473,7 +3474,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/mainnet/ks-lib/executor/spl/elgamal_registry/info.log", + "path": "mainnet/ks-lib/executor/spl/elgamal_registry/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -3486,7 +3487,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/mainnet/ks-lib/executor/spl/elgamal_registry/error.jsonl", + "path": "mainnet/ks-lib/executor/spl/elgamal_registry/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, @@ -3499,7 +3500,7 @@ "enabled": true, "sink": "file", "level": "debug", - "path": "logs/mainnet/ks-lib/executor/spl/token_2022/debug.log", + "path": "mainnet/ks-lib/executor/spl/token_2022/debug.log", "rotation": "daily", "format": "human", "ansi": false, @@ -3512,7 +3513,7 @@ "enabled": true, "sink": "file", "level": "info", - "path": "logs/mainnet/ks-lib/executor/spl/token_2022/info.log", + "path": "mainnet/ks-lib/executor/spl/token_2022/info.log", "rotation": "daily", "format": "human", "ansi": false, @@ -3525,7 +3526,7 @@ "enabled": true, "sink": "file", "level": "error", - "path": "logs/mainnet/ks-lib/executor/spl/token_2022/error.jsonl", + "path": "mainnet/ks-lib/executor/spl/token_2022/error.jsonl", "rotation": "daily", "format": "json", "ansi": false, diff --git a/config/schemas/composition.config.schema.json b/config/schemas/composition.config.schema.json index c2fd311..111fb93 100644 --- a/config/schemas/composition.config.schema.json +++ b/config/schemas/composition.config.schema.json @@ -34,8 +34,7 @@ "additionalProperties": false, "required": [ "name", - "environment", - "auto_reconnect_default" + "environment" ], "properties": { "name": { @@ -43,238 +42,6 @@ }, "environment": { "$ref": "#/$defs/non_empty_string" - }, - "auto_reconnect_default": { - "type": "boolean" - } - } - }, - "database": { - "type": "object", - "additionalProperties": false, - "required": [ - "enabled", - "backend", - "postgres", - "sqlite" - ], - "properties": { - "enabled": { - "type": "boolean" - }, - "backend": { - "type": "string", - "enum": [ - "postgres", - "sqlite" - ] - }, - "postgres": { - "$ref": "#/$defs/postgres" - }, - "sqlite": { - "$ref": "#/$defs/sqlite" - } - } - }, - "postgres": { - "type": "object", - "additionalProperties": false, - "required": [ - "url", - "max_connections", - "connect_timeout_ms", - "auto_initialize_schema" - ], - "properties": { - "url": { - "$ref": "#/$defs/non_empty_string" - }, - "max_connections": { - "type": "integer", - "minimum": 1 - }, - "connect_timeout_ms": { - "type": "integer", - "minimum": 1 - }, - "auto_initialize_schema": { - "type": "boolean" - } - } - }, - "sqlite": { - "type": "object", - "additionalProperties": false, - "required": [ - "path", - "create_if_missing", - "busy_timeout_ms", - "max_connections", - "auto_initialize_schema", - "use_wal" - ], - "properties": { - "path": { - "$ref": "#/$defs/non_empty_string" - }, - "create_if_missing": { - "type": "boolean" - }, - "busy_timeout_ms": { - "type": "integer", - "minimum": 1 - }, - "max_connections": { - "type": "integer", - "minimum": 1 - }, - "auto_initialize_schema": { - "type": "boolean" - }, - "use_wal": { - "type": "boolean" - } - } - }, - "data": { - "type": "object", - "additionalProperties": false, - "required": [ - "wallets_directory", - "logs_directory" - ], - "properties": { - "wallets_directory": { - "$ref": "#/$defs/non_empty_string" - }, - "logs_directory": { - "$ref": "#/$defs/non_empty_string" - } - } - }, - "wallet": { - "type": "object", - "additionalProperties": false, - "required": [ - "wallet_dir", - "cluster", - "temporary_wallet_enabled", - "temporary_wallet_alias", - "temporary_wallet_persist", - "localnet_send_enabled", - "devnet_send_enabled", - "testnet_send_enabled", - "mainnet_send_enabled" - ], - "properties": { - "wallet_dir": { - "$ref": "#/$defs/non_empty_string" - }, - "cluster": { - "$ref": "#/$defs/non_empty_string" - }, - "temporary_wallet_enabled": { - "type": "boolean" - }, - "temporary_wallet_alias": { - "type": "string", - "minLength": 1, - "maxLength": 64, - "pattern": "^[A-Za-z0-9][A-Za-z0-9_-]*$" - }, - "temporary_wallet_persist": { - "type": "boolean" - }, - "localnet_send_enabled": { - "type": "boolean" - }, - "devnet_send_enabled": { - "type": "boolean" - }, - "testnet_send_enabled": { - "type": "boolean" - }, - "mainnet_send_enabled": { - "type": "boolean" - } - } - }, - "execution": { - "type": "object", - "additionalProperties": false, - "required": [ - "dry_run_default", - "require_simulation", - "require_operator_confirmation", - "localnet_max_spend_lamports", - "devnet_max_spend_lamports", - "testnet_max_spend_lamports", - "mainnet_max_spend_lamports", - "max_fee_lamports", - "max_compute_unit_price_micro_lamports", - "recent_blockhash_max_age_slots", - "send_max_retries", - "confirmation_poll_interval_ms", - "confirmation_max_attempts", - "devnet_airdrop_max_lamports" - ], - "properties": { - "dry_run_default": { - "type": "boolean" - }, - "require_simulation": { - "type": "boolean" - }, - "require_operator_confirmation": { - "type": "boolean" - }, - "localnet_max_spend_lamports": { - "type": "integer", - "minimum": 0 - }, - "devnet_max_spend_lamports": { - "type": "integer", - "minimum": 0 - }, - "testnet_max_spend_lamports": { - "type": "integer", - "minimum": 0 - }, - "mainnet_max_spend_lamports": { - "type": "integer", - "minimum": 0 - }, - "max_fee_lamports": { - "type": "integer", - "minimum": 1 - }, - "max_compute_unit_price_micro_lamports": { - "type": "integer", - "minimum": 0 - }, - "recent_blockhash_max_age_slots": { - "type": "integer", - "minimum": 1 - }, - "send_max_retries": { - "type": "integer", - "minimum": 0, - "maximum": 4294967295 - }, - "confirmation_poll_interval_ms": { - "type": "integer", - "minimum": 1, - "maximum": 60000 - }, - "confirmation_max_attempts": { - "type": "integer", - "minimum": 1, - "maximum": 10000 - }, - "devnet_airdrop_max_lamports": { - "type": "integer", - "minimum": 0 } } }, @@ -300,7 +67,10 @@ "required": [ "logging", "transport", - "listeners" + "listeners", + "store", + "wallet", + "execution" ], "properties": { "logging": { @@ -311,6 +81,15 @@ }, "listeners": { "$ref": "#/$defs/non_empty_string" + }, + "store": { + "$ref": "#/$defs/non_empty_string" + }, + "wallet": { + "$ref": "#/$defs/non_empty_string" + }, + "execution": { + "$ref": "#/$defs/non_empty_string" } } }, @@ -320,13 +99,6 @@ "required": [ "name", "app", - "logging_profile", - "transport_profile", - "listeners_profile", - "database", - "data", - "wallet", - "execution", "demo" ], "properties": { @@ -336,6 +108,9 @@ "app": { "$ref": "#/$defs/app_section" }, + "demo": { + "$ref": "#/$defs/demo" + }, "logging_profile": { "$ref": "#/$defs/non_empty_string" }, @@ -345,20 +120,14 @@ "listeners_profile": { "$ref": "#/$defs/non_empty_string" }, - "database": { - "$ref": "#/$defs/database" + "store_profile": { + "$ref": "#/$defs/non_empty_string" }, - "data": { - "$ref": "#/$defs/data" + "wallet_profile": { + "$ref": "#/$defs/non_empty_string" }, - "wallet": { - "$ref": "#/$defs/wallet" - }, - "execution": { - "$ref": "#/$defs/execution" - }, - "demo": { - "$ref": "#/$defs/demo" + "execution_profile": { + "$ref": "#/$defs/non_empty_string" } } } diff --git a/config/schemas/execution.config.schema.json b/config/schemas/execution.config.schema.json new file mode 100644 index 0000000..03395cb --- /dev/null +++ b/config/schemas/execution.config.schema.json @@ -0,0 +1,127 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://khadhroony.local/schemas/execution.config.schema.json", + "title": "Khadhroony Solana execution policy configuration", + "type": "object", + "additionalProperties": false, + "required": [ + "default_profile", + "profiles" + ], + "properties": { + "default_profile": { + "$ref": "#/$defs/non_empty_string" + }, + "profiles": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/$defs/profile" + } + } + }, + "$defs": { + "non_empty_string": { + "type": "string", + "minLength": 1 + }, + "profile": { + "type": "object", + "additionalProperties": false, + "required": [ + "name", + "localnet_send_enabled", + "devnet_send_enabled", + "testnet_send_enabled", + "mainnet_send_enabled", + "dry_run_default", + "require_simulation", + "require_operator_confirmation", + "localnet_max_spend_lamports", + "devnet_max_spend_lamports", + "testnet_max_spend_lamports", + "mainnet_max_spend_lamports", + "max_fee_lamports", + "max_compute_unit_price_micro_lamports", + "recent_blockhash_max_age_slots", + "send_max_retries", + "confirmation_poll_interval_ms", + "confirmation_max_attempts", + "devnet_airdrop_max_lamports" + ], + "properties": { + "name": { + "$ref": "#/$defs/non_empty_string" + }, + "dry_run_default": { + "type": "boolean" + }, + "require_simulation": { + "type": "boolean" + }, + "require_operator_confirmation": { + "type": "boolean" + }, + "localnet_max_spend_lamports": { + "type": "integer", + "minimum": 0 + }, + "devnet_max_spend_lamports": { + "type": "integer", + "minimum": 0 + }, + "testnet_max_spend_lamports": { + "type": "integer", + "minimum": 0 + }, + "mainnet_max_spend_lamports": { + "type": "integer", + "minimum": 0 + }, + "max_fee_lamports": { + "type": "integer", + "minimum": 1 + }, + "max_compute_unit_price_micro_lamports": { + "type": "integer", + "minimum": 0 + }, + "recent_blockhash_max_age_slots": { + "type": "integer", + "minimum": 1 + }, + "send_max_retries": { + "type": "integer", + "minimum": 0, + "maximum": 4294967295 + }, + "confirmation_poll_interval_ms": { + "type": "integer", + "minimum": 1, + "maximum": 60000 + }, + "confirmation_max_attempts": { + "type": "integer", + "minimum": 1, + "maximum": 10000 + }, + "devnet_airdrop_max_lamports": { + "type": "integer", + "minimum": 0 + }, + "mainnet_send_enabled": { + "type": "boolean" + }, + "testnet_send_enabled": { + "type": "boolean" + }, + "devnet_send_enabled": { + "type": "boolean" + }, + "localnet_send_enabled": { + "type": "boolean" + } + } + } + } +} diff --git a/config/schemas/listeners.config.schema.json b/config/schemas/listeners.config.schema.json index 32d4ca6..45b37d8 100644 --- a/config/schemas/listeners.config.schema.json +++ b/config/schemas/listeners.config.schema.json @@ -5,11 +5,11 @@ "type": "object", "additionalProperties": false, "required": [ - "active_profile", + "default_profile", "profiles" ], "properties": { - "active_profile": { + "default_profile": { "$ref": "#/$defs/non_empty_string" }, "profiles": { diff --git a/config/schemas/logging.config.schema.json b/config/schemas/logging.config.schema.json index 2974c71..231ff34 100644 --- a/config/schemas/logging.config.schema.json +++ b/config/schemas/logging.config.schema.json @@ -5,11 +5,15 @@ "type": "object", "additionalProperties": false, "required": [ - "active_profile", + "logs_directory", + "default_profile", "profiles" ], "properties": { - "active_profile": { + "logs_directory": { + "$ref": "#/$defs/non_empty_string" + }, + "default_profile": { "$ref": "#/$defs/non_empty_string" }, "profiles": { diff --git a/config/schemas/resolved.app.config.schema.json b/config/schemas/resolved.app.config.schema.json index cdcae72..544b121 100644 --- a/config/schemas/resolved.app.config.schema.json +++ b/config/schemas/resolved.app.config.schema.json @@ -32,7 +32,6 @@ "name", "app", "database", - "data", "solana", "wallet", "execution", @@ -48,9 +47,6 @@ "database": { "$ref": "#/$defs/database" }, - "data": { - "$ref": "#/$defs/data" - }, "solana": { "$ref": "#/$defs/solana" }, @@ -70,8 +66,7 @@ "additionalProperties": false, "required": [ "name", - "environment", - "auto_reconnect_default" + "environment" ], "properties": { "name": { @@ -79,9 +74,6 @@ }, "environment": { "$ref": "#/$defs/non_empty_string" - }, - "auto_reconnect_default": { - "type": "boolean" } } }, @@ -173,22 +165,6 @@ } } }, - "data": { - "type": "object", - "additionalProperties": false, - "required": [ - "wallets_directory", - "logs_directory" - ], - "properties": { - "wallets_directory": { - "$ref": "#/$defs/non_empty_string" - }, - "logs_directory": { - "$ref": "#/$defs/non_empty_string" - } - } - }, "solana": { "type": "object", "additionalProperties": false, @@ -516,11 +492,7 @@ "cluster", "temporary_wallet_enabled", "temporary_wallet_alias", - "temporary_wallet_persist", - "localnet_send_enabled", - "devnet_send_enabled", - "testnet_send_enabled", - "mainnet_send_enabled" + "temporary_wallet_persist" ], "properties": { "wallet_dir": { @@ -540,18 +512,6 @@ }, "temporary_wallet_persist": { "type": "boolean" - }, - "localnet_send_enabled": { - "type": "boolean" - }, - "devnet_send_enabled": { - "type": "boolean" - }, - "testnet_send_enabled": { - "type": "boolean" - }, - "mainnet_send_enabled": { - "type": "boolean" } } }, @@ -559,6 +519,10 @@ "type": "object", "additionalProperties": false, "required": [ + "localnet_send_enabled", + "devnet_send_enabled", + "testnet_send_enabled", + "mainnet_send_enabled", "dry_run_default", "require_simulation", "require_operator_confirmation", @@ -630,6 +594,18 @@ "devnet_airdrop_max_lamports": { "type": "integer", "minimum": 0 + }, + "mainnet_send_enabled": { + "type": "boolean" + }, + "testnet_send_enabled": { + "type": "boolean" + }, + "devnet_send_enabled": { + "type": "boolean" + }, + "localnet_send_enabled": { + "type": "boolean" } } }, diff --git a/config/schemas/store.config.schema.json b/config/schemas/store.config.schema.json new file mode 100644 index 0000000..d439295 --- /dev/null +++ b/config/schemas/store.config.schema.json @@ -0,0 +1,133 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://khadhroony.local/schemas/store.config.schema.json", + "title": "Khadhroony Solana store configuration", + "type": "object", + "additionalProperties": false, + "required": [ + "default_profile", + "profiles" + ], + "properties": { + "default_profile": { + "$ref": "#/$defs/non_empty_string" + }, + "profiles": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/$defs/profile" + } + } + }, + "$defs": { + "non_empty_string": { + "type": "string", + "minLength": 1 + }, + "database": { + "type": "object", + "additionalProperties": false, + "required": [ + "enabled", + "backend", + "postgres", + "sqlite" + ], + "properties": { + "enabled": { + "type": "boolean" + }, + "backend": { + "type": "string", + "enum": [ + "postgres", + "sqlite" + ] + }, + "postgres": { + "$ref": "#/$defs/postgres" + }, + "sqlite": { + "$ref": "#/$defs/sqlite" + } + } + }, + "postgres": { + "type": "object", + "additionalProperties": false, + "required": [ + "url", + "max_connections", + "connect_timeout_ms", + "auto_initialize_schema" + ], + "properties": { + "url": { + "$ref": "#/$defs/non_empty_string" + }, + "max_connections": { + "type": "integer", + "minimum": 1 + }, + "connect_timeout_ms": { + "type": "integer", + "minimum": 1 + }, + "auto_initialize_schema": { + "type": "boolean" + } + } + }, + "sqlite": { + "type": "object", + "additionalProperties": false, + "required": [ + "path", + "create_if_missing", + "busy_timeout_ms", + "max_connections", + "auto_initialize_schema", + "use_wal" + ], + "properties": { + "path": { + "$ref": "#/$defs/non_empty_string" + }, + "create_if_missing": { + "type": "boolean" + }, + "busy_timeout_ms": { + "type": "integer", + "minimum": 1 + }, + "max_connections": { + "type": "integer", + "minimum": 1 + }, + "auto_initialize_schema": { + "type": "boolean" + }, + "use_wal": { + "type": "boolean" + } + } + }, + "profile": { + "type": "object", + "additionalProperties": false, + "required": [ + "name", + "database" + ], + "properties": { + "name": { + "$ref": "#/$defs/non_empty_string" + }, + "database": { + "$ref": "#/$defs/database" + } + } + } + } +} diff --git a/config/schemas/transport.config.schema.json b/config/schemas/transport.config.schema.json index d14726c..aa79125 100644 --- a/config/schemas/transport.config.schema.json +++ b/config/schemas/transport.config.schema.json @@ -5,13 +5,21 @@ "type": "object", "additionalProperties": false, "required": [ - "active_profile", + "default_profile", + "ws_endpoint_defaults", "profiles" ], "properties": { - "active_profile": { + "default_profile": { "$ref": "#/$defs/non_empty_string" }, + "ws_endpoint_defaults": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/$defs/ws_endpoint_defaults" + } + }, "profiles": { "type": "array", "minItems": 1, @@ -77,72 +85,6 @@ } } }, - "ws_endpoint": { - "type": "object", - "additionalProperties": false, - "required": [ - "name", - "enabled", - "provider", - "cluster", - "url", - "connect_timeout_ms", - "request_timeout_ms", - "unsubscribe_timeout_ms", - "write_channel_capacity", - "event_channel_capacity", - "auto_reconnect", - "roles" - ], - "properties": { - "name": { - "$ref": "#/$defs/non_empty_string" - }, - "enabled": { - "type": "boolean" - }, - "provider": { - "$ref": "#/$defs/non_empty_string" - }, - "cluster": { - "$ref": "#/$defs/non_empty_string" - }, - "url": { - "type": "string", - "pattern": "^wss?://" - }, - "connect_timeout_ms": { - "type": "integer", - "minimum": 1 - }, - "request_timeout_ms": { - "type": "integer", - "minimum": 1 - }, - "unsubscribe_timeout_ms": { - "type": "integer", - "minimum": 1 - }, - "write_channel_capacity": { - "type": "integer", - "minimum": 1 - }, - "event_channel_capacity": { - "type": "integer", - "minimum": 1 - }, - "auto_reconnect": { - "type": "boolean" - }, - "roles": { - "type": "array", - "minItems": 1, - "items": { - "$ref": "#/$defs/endpoint_role" - } - } - } - }, "endpoint_role": { "type": "object", "additionalProperties": false, @@ -195,6 +137,120 @@ } } }, + "ws_endpoint_defaults": { + "type": "object", + "additionalProperties": false, + "required": [ + "name", + "connect_timeout_ms", + "request_timeout_ms", + "unsubscribe_timeout_ms", + "write_channel_capacity", + "event_channel_capacity", + "auto_reconnect" + ], + "properties": { + "name": { + "$ref": "#/$defs/non_empty_string" + }, + "connect_timeout_ms": { + "type": "integer", + "minimum": 1 + }, + "request_timeout_ms": { + "type": "integer", + "minimum": 1 + }, + "unsubscribe_timeout_ms": { + "type": "integer", + "minimum": 1 + }, + "write_channel_capacity": { + "type": "integer", + "minimum": 1 + }, + "event_channel_capacity": { + "type": "integer", + "minimum": 1 + }, + "auto_reconnect": { + "type": "boolean" + } + } + }, + "ws_overrides": { + "type": "object", + "additionalProperties": false, + "properties": { + "connect_timeout_ms": { + "type": "integer", + "minimum": 1 + }, + "request_timeout_ms": { + "type": "integer", + "minimum": 1 + }, + "unsubscribe_timeout_ms": { + "type": "integer", + "minimum": 1 + }, + "write_channel_capacity": { + "type": "integer", + "minimum": 1 + }, + "event_channel_capacity": { + "type": "integer", + "minimum": 1 + }, + "auto_reconnect": { + "type": "boolean" + } + } + }, + "ws_endpoint_source": { + "type": "object", + "additionalProperties": false, + "required": [ + "name", + "defaults", + "enabled", + "provider", + "cluster", + "url", + "roles" + ], + "properties": { + "name": { + "$ref": "#/$defs/non_empty_string" + }, + "defaults": { + "$ref": "#/$defs/non_empty_string" + }, + "enabled": { + "type": "boolean" + }, + "provider": { + "$ref": "#/$defs/non_empty_string" + }, + "cluster": { + "$ref": "#/$defs/non_empty_string" + }, + "url": { + "type": "string", + "pattern": "^wss?://" + }, + "overrides": { + "$ref": "#/$defs/ws_overrides" + }, + "roles": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/$defs/endpoint_role" + } + } + } + }, "profile": { "type": "object", "additionalProperties": false, @@ -218,7 +274,7 @@ "type": "array", "minItems": 1, "items": { - "$ref": "#/$defs/ws_endpoint" + "$ref": "#/$defs/ws_endpoint_source" } } } diff --git a/config/schemas/wallet.config.schema.json b/config/schemas/wallet.config.schema.json new file mode 100644 index 0000000..8e450ac --- /dev/null +++ b/config/schemas/wallet.config.schema.json @@ -0,0 +1,68 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://khadhroony.local/schemas/wallet.config.schema.json", + "title": "Khadhroony Solana wallet configuration", + "type": "object", + "additionalProperties": false, + "required": [ + "wallets_directory", + "default_profile", + "profiles" + ], + "properties": { + "wallets_directory": { + "$ref": "#/$defs/non_empty_string" + }, + "default_profile": { + "$ref": "#/$defs/non_empty_string" + }, + "profiles": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/$defs/profile" + } + } + }, + "$defs": { + "non_empty_string": { + "type": "string", + "minLength": 1 + }, + "profile": { + "type": "object", + "additionalProperties": false, + "required": [ + "name", + "directory", + "cluster", + "temporary_wallet_enabled", + "temporary_wallet_alias", + "temporary_wallet_persist" + ], + "properties": { + "name": { + "$ref": "#/$defs/non_empty_string" + }, + "directory": { + "$ref": "#/$defs/non_empty_string" + }, + "cluster": { + "$ref": "#/$defs/non_empty_string" + }, + "temporary_wallet_enabled": { + "type": "boolean" + }, + "temporary_wallet_alias": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "pattern": "^[A-Za-z0-9][A-Za-z0-9_-]*$" + }, + "temporary_wallet_persist": { + "type": "boolean" + } + } + } + } +} diff --git a/config/store.config.json b/config/store.config.json new file mode 100644 index 0000000..7d76cc8 --- /dev/null +++ b/config/store.config.json @@ -0,0 +1,68 @@ +{ + "default_profile": "local_devnet", + "profiles": [ + { + "name": "local_devnet", + "database": { + "enabled": true, + "backend": "postgres", + "postgres": { + "url": "${KS_SECRET_POSTGRES_DEVNET_URL}", + "max_connections": 8, + "connect_timeout_ms": 5000, + "auto_initialize_schema": true + }, + "sqlite": { + "path": "data/local.sqlite", + "create_if_missing": true, + "busy_timeout_ms": 5000, + "max_connections": 1, + "auto_initialize_schema": true, + "use_wal": true + } + } + }, + { + "name": "mainnet_research", + "database": { + "enabled": true, + "backend": "postgres", + "postgres": { + "url": "${KS_SECRET_POSTGRES_MAINNET_URL}", + "max_connections": 16, + "connect_timeout_ms": 5000, + "auto_initialize_schema": true + }, + "sqlite": { + "path": "data/mainnet-import.sqlite", + "create_if_missing": true, + "busy_timeout_ms": 5000, + "max_connections": 1, + "auto_initialize_schema": true, + "use_wal": true + } + } + }, + { + "name": "mainnet", + "database": { + "enabled": true, + "backend": "postgres", + "postgres": { + "url": "${KS_SECRET_POSTGRES_MAINNET_URL}", + "max_connections": 16, + "connect_timeout_ms": 5000, + "auto_initialize_schema": true + }, + "sqlite": { + "path": "data/mainnet.sqlite", + "create_if_missing": true, + "busy_timeout_ms": 5000, + "max_connections": 1, + "auto_initialize_schema": true, + "use_wal": true + } + } + } + ] +} diff --git a/config/transport.config.json b/config/transport.config.json index 6ee40a9..88afdfa 100644 --- a/config/transport.config.json +++ b/config/transport.config.json @@ -1,5 +1,25 @@ { - "active_profile": "mainnet_research", + "default_profile": "local_devnet", + "ws_endpoint_defaults": [ + { + "name": "standard_rpc_ws", + "connect_timeout_ms": 5000, + "request_timeout_ms": 10000, + "unsubscribe_timeout_ms": 1500, + "write_channel_capacity": 256, + "event_channel_capacity": 1024, + "auto_reconnect": false + }, + { + "name": "high_capacity_rpc_ws", + "connect_timeout_ms": 5000, + "request_timeout_ms": 10000, + "unsubscribe_timeout_ms": 1500, + "write_channel_capacity": 512, + "event_channel_capacity": 4096, + "auto_reconnect": false + } + ], "profiles": [ { "name": "local_devnet", @@ -97,12 +117,7 @@ "provider": "solana-public", "cluster": "devnet", "url": "wss://api.devnet.solana.com", - "connect_timeout_ms": 5000, - "request_timeout_ms": 10000, - "unsubscribe_timeout_ms": 1500, - "write_channel_capacity": 256, - "event_channel_capacity": 1024, - "auto_reconnect": false, + "defaults": "standard_rpc_ws", "roles": [ { "role": "slot_notifications", @@ -406,12 +421,7 @@ "provider": "solana-public", "cluster": "mainnet-beta", "url": "wss://api.mainnet.solana.com", - "connect_timeout_ms": 5000, - "request_timeout_ms": 10000, - "unsubscribe_timeout_ms": 1500, - "write_channel_capacity": 256, - "event_channel_capacity": 1024, - "auto_reconnect": false, + "defaults": "standard_rpc_ws", "roles": [ { "role": "slot_notifications", @@ -448,12 +458,7 @@ "provider": "helius", "cluster": "mainnet-beta", "url": "wss://mainnet.helius-rpc.com/?api-key=${KS_SECRET_HELIUS_API_KEY}", - "connect_timeout_ms": 5000, - "request_timeout_ms": 10000, - "unsubscribe_timeout_ms": 1500, - "write_channel_capacity": 512, - "event_channel_capacity": 4096, - "auto_reconnect": false, + "defaults": "high_capacity_rpc_ws", "roles": [ { "role": "logs_subscribe", @@ -751,12 +756,7 @@ "provider": "solana-public", "cluster": "mainnet-beta", "url": "wss://api.mainnet.solana.com", - "connect_timeout_ms": 5000, - "request_timeout_ms": 10000, - "unsubscribe_timeout_ms": 1500, - "write_channel_capacity": 256, - "event_channel_capacity": 1024, - "auto_reconnect": false, + "defaults": "standard_rpc_ws", "roles": [ { "role": "slot_notifications", @@ -793,12 +793,7 @@ "provider": "helius", "cluster": "mainnet-beta", "url": "wss://mainnet.helius-rpc.com/?api-key=${KS_SECRET_HELIUS_API_KEY}", - "connect_timeout_ms": 5000, - "request_timeout_ms": 10000, - "unsubscribe_timeout_ms": 1500, - "write_channel_capacity": 512, - "event_channel_capacity": 4096, - "auto_reconnect": false, + "defaults": "high_capacity_rpc_ws", "roles": [ { "role": "logs_subscribe", diff --git a/config/wallet.config.json b/config/wallet.config.json new file mode 100644 index 0000000..35fa41a --- /dev/null +++ b/config/wallet.config.json @@ -0,0 +1,30 @@ +{ + "wallets_directory": "${KS_WALLETS_DIRECTORY:-wallets}", + "default_profile": "local_devnet", + "profiles": [ + { + "name": "local_devnet", + "directory": "temporary/local_devnet", + "cluster": "devnet", + "temporary_wallet_enabled": true, + "temporary_wallet_alias": "local-devnet-operator", + "temporary_wallet_persist": true + }, + { + "name": "mainnet_research", + "directory": "temporary/mainnet_research", + "cluster": "mainnet-beta", + "temporary_wallet_enabled": false, + "temporary_wallet_alias": "mainnet_research-disabled", + "temporary_wallet_persist": false + }, + { + "name": "mainnet", + "directory": "temporary/mainnet", + "cluster": "mainnet-beta", + "temporary_wallet_enabled": false, + "temporary_wallet_alias": "mainnet-disabled", + "temporary_wallet_persist": false + } + ] +} diff --git a/docs/decisions/KHADHROONY_SOLANA_NAMESPACE_POLICY.md b/docs/decisions/KHADHROONY_SOLANA_NAMESPACE_POLICY.md index 2e03150..c18e8b6 100644 --- a/docs/decisions/KHADHROONY_SOLANA_NAMESPACE_POLICY.md +++ b/docs/decisions/KHADHROONY_SOLANA_NAMESPACE_POLICY.md @@ -1,5 +1,5 @@ - + # Politique de namespace Khadhroony Solana @@ -57,33 +57,38 @@ Chaque domaine possède les trois mêmes classes : - `KS_PUBLIC_*` / `KB_PUBLIC_*` : valeur explicitement classée comme publiable ; le préfixe ne suffit pas à lui seul à autoriser une exposition, qui doit rester définie par un DTO ou une surface publique explicite ; - `KS_*` / `KB_*` hors sous-préfixes précédents : valeur interne ; elle n'est pas exposée en fonctionnement normal mais peut être incluse dans un diagnostic explicitement demandé si sa sémantique n'est pas sensible. -Une variable appartenant à un contrat `ks-*` qui utilise `KB_*`, ou l'inverse pour un contrat réellement spécifique `kb-*`, est non conforme. `0.5.1-pre.004` ne laisse actuellement aucun contrat d'environnement propre au desktop : les variables qu'il consomme appartiennent toutes aux composants ou scénarios Solana et utilisent donc `KS_*`. +Une variable appartenant à un contrat `ks-*` qui utilise `KB_*`, ou l'inverse pour un contrat réellement spécifique `kb-*`, est non conforme. `0.5.1-pre.004` n’avait encore identifié aucun contrat d’environnement propre au desktop. Depuis l’introduction des compositions par binaire, `KB_APP_DEMO_DESKTOP_CONFIG_PATH` est le premier contrat réellement possédé par `kb-app-demo-desktop`; les variables des composants et scénarios Solana qu’il consomme restent `KS_*`. La classification d'une valeur sensible doit survivre à la substitution. Une chaîne composée contenant une valeur issue de `KS_SECRET_*` ou `KB_SECRET_*` reste sensible dans son ensemble et ne doit pas redevenir une simple valeur publiable après résolution. Cette propagation et le camouflage effectif sont implémentés après le split config/logging, pas dans la prerelease de renommage. ## 5. Configuration source, runtime et publique -La restructuration `0.5.1` utilise des compositions propres aux binaires et des documents spécialisés partagés : +La restructuration `0.5.1` utilise des documents spécialisés Khadhroony Solana et, uniquement lorsque nécessaire, une composition propre au binaire : -- `config/kb-app-demo-desktop.default.config.json` compose les besoins du desktop ; -- `config/ks-pipeline-demo-scenarios.default.config.json` compose les besoins du CLI/scénarios ; -- `config/logging.config.json` appartient à `ks-logging` ; -- `config/transport.config.json` contient les profils HTTP/WebSocket partagés ; -- `config/listeners.config.json` contient les profils de subscriptions Solana partagés ; +- `config/kb-app-demo-desktop.default.config.json` compose les overrides du desktop ; +- `config/logging.config.json` appartient à `ks-logging` et porte notamment `logs_directory` hors profils ; +- `config/transport.config.json` contient les profils HTTP/WebSocket et les classes de defaults WebSocket ; +- `config/listeners.config.json` contient les profils de subscriptions Solana ; +- `config/store.config.json` contient les profils de stockage ; +- `config/wallet.config.json` contient la racine wallet globale et les profils wallet ; +- `config/execution.config.json` contient les politiques de simulation, soumission et limites ; - tous les schémas actifs résident sous `config/schemas/`. -Le fichier de composition sélectionne explicitement les profils spécialisés. Un nouveau worker ou binaire doit pouvoir fournir son propre `.default.config.json` et une configuration dédiée sans modifier `ks-config` pour enregistrer son existence. +Chaque document partagé possède un `default_profile`. Un consommateur qui accepte les defaults n'a pas besoin de composition dédiée. C'est notamment le cas de `ks-pipeline-demo-scenarios`; `KS_DEVNET_CONFIG_PATH` reste uniquement un override facultatif vers une composition explicite. + +Lorsqu'un binaire possède une composition, celle-ci peut remplacer indépendamment les profils spécialisés. `ks-config` doit prouver l'existence des fichiers et des profils référencés avant de produire le contrat runtime. `AppConfig/ProfileConfig` reste temporairement un contrat runtime résolu afin de préserver les consommateurs pendant la migration. Il n'est plus un document source chargé directement ; son schéma de compatibilité est `config/schemas/resolved.app.config.schema.json` et ses fixtures sont sous `test-fixtures/config/`. -La conception doit distinguer : +La conception distingue : -1. la représentation source, composée de documents indépendants pouvant contenir des références `${KS_*}` ou `${KB_*}` selon le propriétaire du contrat ; -2. la composition propre au binaire, qui choisit les documents et profils ; -3. la représentation runtime résolue, qui peut porter des secrets ; -4. la représentation publique/diagnostique, construite explicitement et incapable d'exposer une valeur secrète résolue. +1. les documents source indépendants, pouvant contenir des références `${KS_*}` ou `${KB_*}` selon le propriétaire ; +2. leurs valeurs globales et `default_profile` ; +3. l'éventuelle composition propre au binaire, qui fournit des overrides ; +4. la représentation runtime résolue, qui peut porter des secrets ; +5. la représentation publique/diagnostique, construite explicitement et incapable d'exposer une valeur secrète résolue. -Une configuration runtime complète ne doit jamais être sérialisée puis « nettoyée » après coup pour produire un payload public. +Une configuration runtime complète ne doit jamais être sérialisée puis « nettoyée » après coup pour produire un payload public. Les bindings TypeScript des crates généralistes suivent la même frontière : ils ne sont conservés que lorsqu'un contrat générique indépendant d'une application Tauri le justifie. ## 6. Identités runtime et persistées diff --git a/docs/guides/CONFIGURATION.md b/docs/guides/CONFIGURATION.md index af4326c..e9aad58 100644 --- a/docs/guides/CONFIGURATION.md +++ b/docs/guides/CONFIGURATION.md @@ -1,50 +1,45 @@ - + # Guide de configuration ## Objectif -La configuration est organisée autour de compositions propres aux binaires et de documents spécialisés réutilisables. Un binaire choisit les fichiers partagés et les profils dont il a besoin au lieu de recopier une configuration monolithique. +La configuration sépare les responsabilités Solana partagées des choix propres aux binaires. Les documents spécialisés sont autonomes grâce à leurs valeurs globales et à leur `default_profile`; une composition de binaire ne fournit que les overrides nécessaires. -## Fichiers actifs +## Documents actifs -Compositions : +Composition actuelle : -- `config/kb-app-demo-desktop.default.config.json` : composition par défaut du desktop ; -- `config/ks-pipeline-demo-scenarios.default.config.json` : composition par défaut du CLI/scénarios. +- `config/kb-app-demo-desktop.default.config.json` : composition par défaut du desktop. Documents partagés : - `config/logging.config.json` ; - `config/transport.config.json` ; -- `config/listeners.config.json`. +- `config/listeners.config.json` ; +- `config/store.config.json` ; +- `config/wallet.config.json` ; +- `config/execution.config.json`. -Exemples : - -- `config/example.kb-app-demo-desktop.default.config.json` ; -- `config/example.logging.config.json` ; -- `config/example.transport.config.json` ; -- `config/example.listeners.config.json`. - -Schémas : - -- `config/schemas/composition.config.schema.json` ; -- `config/schemas/logging.config.schema.json` ; -- `config/schemas/transport.config.schema.json` ; -- `config/schemas/listeners.config.schema.json` ; -- `config/schemas/resolved.app.config.schema.json` pour le contrat runtime reconstruit. +Chaque document possède un exemple `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. +## Defaults partagés + +Chaque document spécialisé définit un `default_profile`. Lorsqu'aucune composition n'est fournie, `ks-config` combine les defaults propres à transport, listeners, store, wallet et execution ; les noms de ces profils n'ont pas besoin d'être identiques. Une composition sélectionne des sources et profils, mais ne duplique pas des paramètres arbitraires : les overrides fins restent dans le schéma propriétaire et les globals explicitement configurables passent par leur variable d'environnement dédiée. + +Cette règle permet à une bibliothèque, un scénario ou un futur worker d'utiliser une configuration cohérente sans créer un fichier de composition uniquement pour répéter les choix par défaut. + ## Composition d'un binaire Une composition définit : -1. son `active_profile` ; +1. son `active_profile` applicatif ; 2. les chemins des documents partagés ; -3. pour chaque profil, le `logging_profile`, `transport_profile` et `listeners_profile` à sélectionner ; -4. temporairement, les sections qui n'ont pas encore leur document spécialisé. +3. les éventuels overrides de profils spécialisés ; +4. les paramètres propres au binaire qui ne relèvent pas d'un document partagé. Exemple conceptuel : @@ -53,14 +48,23 @@ kb-app-demo-desktop.default.config.json sources.logging -> config/logging.config.json sources.transport -> config/transport.config.json sources.listeners -> config/listeners.config.json + sources.store -> config/store.config.json + sources.wallet -> config/wallet.config.json + sources.execution -> config/execution.config.json + + profile local_devnet + aucun override -> defaults des documents partagés profile mainnet_research logging_profile -> mainnet_research transport_profile -> mainnet_research listeners_profile -> mainnet_research + store_profile -> mainnet_research + wallet_profile -> mainnet_research + execution_profile -> mainnet_research ``` -Les `active_profile` propres aux documents partagés restent utiles lorsqu'ils sont chargés seuls. Dans une composition, la référence explicite du binaire prime. +`ks-config` charge les fichiers référencés, valide leurs schémas/types et refuse une composition qui référence un profil inexistant. ## Desktop @@ -70,65 +74,100 @@ Le chemin de composition du desktop peut être remplacé par : KB_APP_DEMO_DESKTOP_CONFIG_PATH ``` -Le logging conserve l'override opérationnel : +Le logging conserve aussi l'override opérationnel : ```text KS_LOGGING_CONFIG_PATH ``` -Sans cet override, le desktop utilise le chemin logging déclaré par sa composition. - ## Scénarios Devnet -Les scénarios opt-in utilisent : +Par défaut, `ks-pipeline-demo-scenarios` utilise directement les defaults des documents partagés. Il n'existe plus de `config/ks-pipeline-demo-scenarios.default.config.json` obligatoire. + +Les overrides opérateur restent : ```text -KS_DEVNET_CONFIG_PATH -KS_DEVNET_PROFILE +KS_DEVNET_CONFIG_PATH # composition explicite facultative +KS_DEVNET_PROFILE # sélection d'un profil runtime compatible Devnet ``` -`KS_DEVNET_CONFIG_PATH` désigne désormais un fichier de composition. Sans override, les scénarios utilisent `config/ks-pipeline-demo-scenarios.default.config.json`. +## Valeurs hors profils -## Transport +Les paramètres globaux ne doivent pas être dupliqués dans chaque profil. -`transport.config.json` possède les endpoints HTTP et WebSocket ainsi que leurs rôles et limites. Le profil transport peut être consommé directement par `ks-onchain-transport` sans dépendre d'un profil applicatif complet. +### Logging -Cela prépare les futurs workers : un worker de capture pourra sélectionner un profil transport différent du desktop tout en partageant le même document. +```text +logs_directory = ${KS_LOGS_DIRECTORY:-logs} +``` -## Listeners +Les chemins de fichiers des targets sont relatifs à cette racine lorsqu'ils ne sont pas absolus. -`listeners.config.json` contient les déclarations de subscriptions par logs, programme et compte. Un profil listeners peut être associé à n'importe quel profil transport compatible par le fichier de composition du consommateur. +### Wallet -La séparation évite de recopier les listes de `program_id` et les filtres lorsque plusieurs binaires observent la même surface Solana. +```text +wallets_directory = ${KS_WALLETS_DIRECTORY:-wallets} +``` -## Logging +Chaque profil wallet ne contient plus qu'un chemin relatif et ses paramètres d'identité/persistance. -`logging.config.json` appartient à `ks-logging`. Une composition référence son fichier et son profil, mais les types et validations logging ne reviennent pas dans `ks-config`. +## Transport WebSocket -## Contrat runtime résolu +Les paramètres communs d'un type d'endpoint WebSocket sont définis dans `ws_endpoint_defaults`. Chaque endpoint choisit une classe de defaults et peut écraser individuellement : -Pendant la migration `0.5.1`, `ks-config` reconstruit encore un `AppConfig/ProfileConfig` contenant les sections nécessaires aux consommateurs existants. Ce contrat est une projection runtime, pas une configuration source. +- `connect_timeout_ms` ; +- `request_timeout_ms` ; +- `unsubscribe_timeout_ms` ; +- `write_channel_capacity` ; +- `event_channel_capacity` ; +- `auto_reconnect`. -Les tests de compatibilité utilisent les fixtures sous `test-fixtures/config/`. Aucun binaire ne doit charger ces fixtures en production. +Les classes actuelles couvrent le RPC WS standard et le RPC WS à capacité plus élevée. Une future surface Helius avancée devra avoir son propre contrat/default lorsqu'elle sera réellement implémentée. -## Étapes suivantes +## Store -Le prochain split doit extraire : +`store.config.json` porte les paramètres PostgreSQL/SQLite. Ce déplacement ne change aucune table ni migration SQL ; la normalisation SQL reste réservée à `0.5.3`. -- database vers le document store ; -- `logs_directory` vers logging ; -- `wallets_directory`, le stockage wallet et les paramètres de wallet vers un document wallet ; -- les permissions `*_send_enabled` du wallet vers la politique execution ; -- execution ; -- configuration spécifique au desktop. +## Wallet et politique d'exécution -Après cette étape seulement, les DTO publics, diagnostics et règles de camouflage seront construits sur les nouvelles frontières. +Les propriétés de stockage/identité du wallet appartiennent à `wallet.config.json`. + +Les autorisations : + +```text +localnet_send_enabled +devnet_send_enabled +testnet_send_enabled +mainnet_send_enabled +``` + +appartiennent à `execution.config.json`, avec les limites de dépense, frais, simulation et confirmation. Un wallet sait signer ; la politique d'exécution décide si une soumission est autorisée. + +## 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. + +Les fixtures de compatibilité sont sous `test-fixtures/config/`. Elles ne doivent pas être chargées en production. + +## 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é. + +## É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. ## Invariants - aucun secret réel dans les fichiers versionnés ; - tous les schémas actifs sous `config/schemas/` ; - aucun retour au fichier monolithique `app.config.json` ; -- un fichier de composition appartient à son binaire ; +- les valeurs globales restent hors profils ; +- une composition appartient à son binaire ; - un document spécialisé reste indépendant des binaires qui le consomment ; -- `ks-config` fournit les mécanismes partagés sans enregistrer une liste fermée de workers/applications. +- `ks-config` valide les références mais ne maintient pas une liste fermée de futurs workers/applications. diff --git a/docs/guides/LOGGING.md b/docs/guides/LOGGING.md index c702688..0cd2fd1 100644 --- a/docs/guides/LOGGING.md +++ b/docs/guides/LOGGING.md @@ -1,88 +1,49 @@ - + # Guide de logging et tracing ## Objectif -`ks-logging` possède désormais le contrat logging, son document de profils, son schéma JSON et l’initialisation runtime `tracing`. +`ks-logging` possède le contrat logging, son document de profils, son schéma JSON et l'initialisation runtime `tracing`. -## Flux de démarrage +## Valeur globale -1. charger `config/logging.config.json` avec `ks_logging::read_logging_json_file_with_environment` ; -2. sélectionner le profil avec `ks_logging::active_logging_profile` ; -3. appeler `ks_logging::init_logging` une seule fois ; -4. conserver `LoggingGuard` jusqu’à la fermeture du processus ; -5. émettre les événements avec des targets canoniques. - -```rust -let document = match ks_logging::read_logging_json_file_with_environment( - std::path::Path::new("config/logging.config.json"), - std::path::Path::new("."), -) { - std::result::Result::Ok(value) => value, - std::result::Result::Err(error) => return std::result::Result::Err(error), -}; -let config = match ks_logging::active_logging_profile(&document) { - std::result::Result::Ok(value) => value, - std::result::Result::Err(error) => return std::result::Result::Err(error), -}; -let guard = match ks_logging::init_logging(config) { - std::result::Result::Ok(value) => value, - std::result::Result::Err(error) => return std::result::Result::Err(error), -}; -``` - -Le document logging reste indépendant de la composition binaire. Une composition `.default.config.json` sélectionne explicitement le profil logging qu’elle veut utiliser ; le `active_profile` propre à `logging.config.json` reste disponible pour les consommateurs autonomes. - -## Contrat JSON - -Le schéma est `config/schemas/logging.config.schema.json`. Les exemples sont `config/logging.config.json` pour la configuration runtime de référence et `config/example.logging.config.json` pour un exemple minimal. - -Une route définit notamment : - -- sink console ou fichier ; -- niveau minimal ; -- format humain, compact, pretty ou JSON ; -- rotation ; -- targets exactes ou préfixes ; -- activation ANSI. - -Les routes fichier ne doivent jamais écrire de secrets ou de keypairs. - -## Nomenclature des targets - -Les targets suivent les conventions du workspace, par exemple : +La racine des logs n'est pas un profil : ```text -ks-pipeline.backfill -ks-pipeline.decode-replay -ks-onchain-transport.http -ks-lib-executor.spl.token-2022 -ks-lib-materializer.compliance.audit -ks-lib-materializer.token.accounts +logging.config.json.logs_directory = ${KS_LOGS_DIRECTORY:-logs} ``` -Pour un matérialisateur, la target de tracing reste distincte du `processorName` persisté. +Les targets fichier utilisent des chemins relatifs, par exemple `devnet/ks-pipeline/debug.log`. `ks-logging` les résout sous `logs_directory` au chargement. Une valeur absolue explicite reste absolue. -## Frontend desktop +Cette séparation évite de recopier `logs/` dans chaque profil et permet à l'opérateur de déplacer tous les logs par variable d'environnement ou `.env`. -Le desktop charge les documents app et logging séparément. Il ne convertit plus un type `ks_config::LoggingConfig` vers `ks_logging::LoggingConfig` : le type runtime est possédé directement par `ks-logging`. +## Sélection du profil -La surface publique de diagnostic/configuration reste volontairement inchangée jusqu’à `0.5.1-pre.006`, qui supprimera l’exposition des configurations résolues complètes. +`logging.config.json` définit `default_profile`. Un consommateur autonome l'utilise avec `default_logging_profile`. -## Diagnostic +Un binaire possédant une composition peut sélectionner un autre profil avec `logging_profile`; cette sélection ne modifie pas le document partagé. -- vérifier `KS_LOGGING_CONFIG_PATH` si le fichier par défaut n’est pas utilisé ; -- vérifier le profil logging actif indépendamment du profil app ; -- valider le document contre son schéma ; -- confirmer le niveau global et les filtres spécifiques ; -- vérifier les chemins et permissions des routes fichier ; -- ne pas réinitialiser le subscriber global pendant l’exécution. +## Flux de démarrage desktop -## Références +1. charger la composition `config/kb-app-demo-desktop.default.config.json` ; +2. résoudre le chemin `logging` référencé ; +3. charger `logging.config.json` ; +4. choisir l'override du profil ou son `default_profile` ; +5. résoudre les paths sous `logs_directory` ; +6. appeler `ks_logging::init_logging` ; +7. poursuivre l'initialisation du runtime. -- `ks-logging/README.md` ; -- `ks-logging/USAGE.md` ; -- `config/README.md` ; -- `docs/OPERATION_NAMING_CONVENTION.md`. +`KS_LOGGING_CONFIG_PATH` remplace le chemin du document et `KS_LOGS_DIRECTORY` remplace uniquement sa racine de sortie. + +## Sécurité + +Les logs ne doivent jamais exposer : + +- une valeur `KS_SECRET_*` ou `KB_SECRET_*` ; +- un DSN avec credentials ; +- une clé privée/keypair ; +- une configuration runtime complète résolue. + +Le masquage structurel et les DTO diagnostics sûrs sont finalisés dans la prochaine prerelease de `0.5.1`. 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 797ec80..c3111cc 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`. `0.5.1-pre.001` a fermé l'inventaire et les contrats de migration. `0.5.1-pre.002` a renommé physiquement les dix crates Solana généralistes vers `ks-*` / `ks_*`. `0.5.1-pre.003` a migré les identités runtime et persistables hiérarchiques de `ks-lib`. `0.5.1-pre.004` migre maintenant les contrats d’environnement vers les namespaces d’ownership `KS_*` / `KB_*`, sans toucher au namespace SQL ni encore scinder les fichiers de configuration. `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` 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. -La base `0.5.0-pre.004` puis `0.5.1-pre.001` ont été validées par `cargo fmt --all`, `cargo check --workspace`, `cargo clippy --all-targets`, `python3 scripts/audit_rust_workspace_rules.py` et `cargo test --workspace`. `pre.002` a ensuite été acceptée après validation opérateur du renommage physique. `pre.003` a été acceptée après validation opérateur. `pre.004` doit rejouer les contrôles de référence, car les noms d’environnement sont consommés par les tests optionnels, les scénarios, le store, la configuration et le desktop. +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. ## 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,58 +538,64 @@ 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 -Le split `pre.005` a d'abord extrait le logging. L'audit suivant a confirmé que le document applicatif restant mélangeait encore des responsabilités ayant des cycles de vie et des consommateurs distincts. La cible devient donc une composition propre à chaque binaire, complétée par des documents spécialisés partageables. +Le split est désormais fondé sur deux niveaux complémentaires : -La convention de composition est : +1. chaque document spécialisé est autonome, possède ses valeurs globales et un `default_profile` ; +2. une composition `.default.config.json` n'existe que lorsqu'un binaire doit remplacer des fichiers/profils ou porter des paramètres propres à l'application. -```text -config/.default.config.json -``` - -Les premières compositions sont : - -```text -config/kb-app-demo-desktop.default.config.json -config/ks-pipeline-demo-scenarios.default.config.json -``` - -`pre.006` extrait : +Les documents partagés à l'issue de `pre.007` sont : ```text config/logging.config.json config/transport.config.json config/listeners.config.json +config/store.config.json +config/wallet.config.json +config/execution.config.json ``` -avec exemples sous `config/` et schémas source sous `config/schemas/`. Chaque composition sélectionne explicitement les profils logging, transport et listeners qu'elle consomme. Les `active_profile` propres aux documents spécialisés restent des valeurs de repli pour les consommateurs autonomes, mais ne couplent pas les sélections d'un binaire. +Le desktop conserve : -`AppConfig/ProfileConfig` demeure provisoirement un **contrat runtime résolu**, reconstruit par `ks-config`, afin de préserver les consommateurs pendant la migration. Son schéma est `config/schemas/resolved.app.config.schema.json` et ses fixtures de test sont sous `test-fixtures/config/`; il ne correspond plus à un document source chargé au démarrage. +```text +config/kb-app-demo-desktop.default.config.json +``` -`pre.007` doit poursuivre la décomposition de `database/data`, wallet, execution et de la configuration réellement spécifique au desktop. À son terme, une composition doit principalement référencer des documents/profils spécialisés et une configuration dédiée au binaire, sans obliger `ks-config` à enregistrer la liste des futurs workers ou exécutables. +`ks-pipeline-demo-scenarios` n'a plus de composition dédiée par défaut. En absence de `KS_DEVNET_CONFIG_PATH`, il compose directement les `default_profile` indépendants des documents partagés. Un override explicite reste possible pour un besoin opérateur particulier. -Cette décomposition doit être terminée avant de figer les surfaces `public`/`diagnostic` et la propagation de sensibilité en `pre.008`. +Les valeurs non dépendantes d'un profil sont sorties des profils : + +- `logging.logs_directory = ${KS_LOGS_DIRECTORY:-logs}` ; +- `wallet.wallets_directory = ${KS_WALLETS_DIRECTORY:-wallets}`. + +Les chemins relatifs de logs et de wallets sont résolus sous ces racines. Les autorisations `*_send_enabled` appartiennent désormais à `execution.config.json`, pas à `wallet.config.json`. + +Le transport possède des classes WebSocket de defaults nommées. Chaque endpoint choisit une classe et peut fournir ses propres `overrides`; `auto_reconnect` appartient donc au transport et non à `AppSectionConfig`. Les classes actuelles couvrent uniquement les surfaces réellement présentes : RPC WS standard et RPC WS à capacité supérieure. Une future surface Helius avancée aura sa propre classe/contrat lorsqu'elle sera implémentée. + +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. ## 9. Source, runtime, public et diagnostic @@ -640,7 +646,11 @@ Avant chaque changement structurel correspondant, conserver ou ajouter des tests - l'interdiction des variables de configuration possédées par le workspace hors namespace d'ownership `KS_*` ou `KB_*` ; - la propagation de sensibilité depuis `KS_SECRET_*` vers les valeurs composées ; - l'impossibilité pour une sentinelle secrète d'apparaître dans `Debug`, logs, erreurs, payloads Tauri, sérialisation publique ou diagnostic ; -- la validation indépendante des schémas source de composition, logging, transport et listeners sous `config/schemas/` ; +- la validation indépendante des schémas source de composition, logging, transport, listeners, store, wallet et execution sous `config/schemas/` ; +- la résolution indépendante des `default_profile` sans supposer des noms identiques ; +- le refus des références de profils inexistants dans une composition ; +- le maintien de `logs_directory` et `wallets_directory` hors profils ; +- la résolution des classes de defaults WebSocket avant les overrides propres aux endpoints ; - la validation du schéma `resolved.app.config.schema.json` uniquement comme contrat runtime transitoire ; - l'indépendance des profils généralistes et logging ; - l'absence de duplication structurelle des types logging entre `ks-config` et `ks-logging` ; @@ -694,7 +704,7 @@ Les tests ne doivent pas figer comme comportement légitime la fuite actuelle de ### `0.5.1-pre.006` — composition binaire + transport + listeners - **implémenté** : remplacement du rôle de `app.config.json` par des compositions `.default.config.json` ; -- **implémenté** : création de `kb-app-demo-desktop.default.config.json` et d’une composition propre à `ks-pipeline-demo-scenarios` ; +- **implémenté** : création de `kb-app-demo-desktop.default.config.json` et première composition transitoire propre à `ks-pipeline-demo-scenarios`, ensuite supprimée en `pre.007` au profit des defaults partagés ; - **implémenté** : extraction des endpoints HTTP/WebSocket vers `transport.config.json` avec schéma et exemple dédiés ; - **implémenté** : extraction des listeners vers `listeners.config.json` avec schéma et exemple dédiés ; - **implémenté** : sélection explicite des profils logging/transport/listeners par chaque composition ; @@ -703,22 +713,26 @@ Les tests ne doivent pas figer comme comportement légitime la fuite actuelle de - **implémenté** : suppression des anciens fichiers source `app.config.json`, `example.app.config.json` et de leur schéma source historique ; - ne pas encore modifier les DTO publics ni la politique de camouflage. -### `0.5.1-pre.007` — store + wallet + execution + configuration dédiée des binaires +### `0.5.1-pre.007` — defaults partagés + store + wallet + execution -- extraire `database` vers un document store cohérent avec les frontières de `ks-store` ; -- supprimer `DataConfig` en réaffectant `logs_directory` au document logging et `wallets_directory` au document wallet selon leur ownership réel ; -- auditer la redondance entre `data.wallets_directory` et `wallet.wallet_dir`, puis définir un répertoire racine wallet global et des chemins/alias de profils sans duplication ; -- sortir les paramètres identité/stockage temporaire du wallet vers un document wallet et préparer le contrat `0.5.2` ; -- déplacer `localnet_send_enabled`, `devnet_send_enabled`, `testnet_send_enabled` et `mainnet_send_enabled` hors du wallet vers la politique d'exécution ; -- extraire les politiques d'exécution vers un document indépendant ; -- décider si `app.auto_reconnect_default`, actuellement conservé pour compatibilité, appartient au transport ou doit être supprimé avec migration explicite ; -- sortir `demo` de `ks-config` vers une configuration réellement possédée par `kb-app-demo-desktop` ; -- réduire les compositions à des références vers profils spécialisés et configuration dédiée du binaire ; -- vérifier qu'un nouveau worker peut ajouter son fichier de composition/dédié sans modifier `ks-config` pour enregistrer son existence. +- **implémenté** : extraction de `database` vers `store.config.json` ; +- **implémenté** : suppression de `DataConfig`, avec `logs_directory` global dans logging et `wallets_directory` global dans wallet ; +- **implémenté** : extraction du stockage/alias/persistance wallet vers `wallet.config.json` avec chemins relatifs à la racine globale ; +- **implémenté** : déplacement de `localnet_send_enabled`, `devnet_send_enabled`, `testnet_send_enabled` et `mainnet_send_enabled` vers `execution.config.json` ; +- **implémenté** : extraction complète des politiques d'exécution vers `execution.config.json` ; +- **implémenté** : déplacement de `auto_reconnect` vers des classes de defaults WebSocket nommées, avec overrides par endpoint ; +- **implémenté** : remplacement des `active_profile` des documents partagés par des `default_profile` autonomes ; +- **implémenté** : suppression de la composition par défaut de `ks-pipeline-demo-scenarios`; les scénarios utilisent les defaults partagés sauf override explicite `KS_DEVNET_CONFIG_PATH` ; +- **implémenté** : validation par `ks-config` de l'existence des profils référencés par une composition ; +- **audit TS-RS** : 17 exports `export_to` restent dans `ks-config`, 100 dans `ks-lib` et 89 dans `kb-app-demo-desktop`; le frontend desktop n'importe directement que ses bindings `kb_app_demo_desktop`, aucun binding `ks-config`/`ks-lib`. -### `0.5.1-pre.008` — surfaces publiques sûres et camouflage +Le split partagé est considéré terminé après cette prerelease. Les champs applicatifs transitoires de la composition desktop seront traités avec la frontière publique/application de `pre.008`, et non par la création d'un nouveau document généraliste. + +### `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 ; diff --git a/docs/rules/RULES_SPECIFIC_KHADHROONY.md b/docs/rules/RULES_SPECIFIC_KHADHROONY.md index 5ef514f..2d19fb0 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` @@ -237,21 +237,26 @@ Aucun renommage massif de modules n'est autorisé sans étape de contrôle dédi ## Règles de configuration -- La configuration commune Khadhroony Solana passe par `ks-config`, mais chaque binaire possède son fichier de composition et peut ajouter sa configuration dédiée sans rendre `ks-config` dépendant de ce binaire. -- Le nom par défaut d'une composition binaire suit `.default.config.json`. Le desktop utilise `config/kb-app-demo-desktop.default.config.json` ; le CLI des scénarios utilise `config/ks-pipeline-demo-scenarios.default.config.json`. -- Une composition référence les documents spécialisés et sélectionne explicitement les profils qu'elle consomme. Elle ne doit pas recopier les blocs transport, listeners ou logging. -- Les documents partagés actuels sont `config/logging.config.json`, `config/transport.config.json` et `config/listeners.config.json`. Ils conservent chacun des profils nommés et peuvent être consommés indépendamment d'une composition. -- Les schémas JSON actifs sont conservés exclusivement sous `config/schemas/`. `composition.config.schema.json`, `logging.config.schema.json`, `transport.config.schema.json` et `listeners.config.schema.json` décrivent les documents source ; `resolved.app.config.schema.json` décrit uniquement le contrat runtime transitoire reconstruit par `ks-config`. +- La configuration commune Khadhroony Solana passe par `ks-config`, mais chaque binaire peut posséder un fichier de composition et une configuration dédiée sans rendre `ks-config` dépendant de ce binaire. +- Le nom par défaut d'une composition binaire suit `.default.config.json`. Le desktop utilise `config/kb-app-demo-desktop.default.config.json`. Un consommateur qui accepte intégralement les defaults partagés ne doit pas créer une composition uniquement pour les répéter. +- Les documents partagés actifs sont `config/logging.config.json`, `config/transport.config.json`, `config/listeners.config.json`, `config/store.config.json`, `config/wallet.config.json` et `config/execution.config.json`. +- Chaque document partagé définit un `default_profile`. Une composition peut sélectionner un autre profil ; une référence explicite doit être validée par `ks-config` et doit désigner un profil existant. +- Une composition ne recopie pas arbitrairement les paramètres d’un document spécialisé. Les overrides de champ restent définis et validés par le contrat propriétaire (par exemple les overrides d’un endpoint WebSocket) ; les valeurs globales explicitement prévues pour l’environnement peuvent être remplacées via `KS_*`/`KB_*`. +- Les valeurs qui ne varient pas par profil restent au niveau racine de leur document spécialisé. `logs_directory` appartient au logging et `wallets_directory` au wallet ; ils ne doivent pas être dupliqués dans les profils. +- Les valeurs globales pouvant varier par installation doivent fournir un défaut versionné sûr et peuvent être remplacées par une variable d'environnement namespacée. +- 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` et `config/schema.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` sélectionne une composition pour les scénarios Devnet opt-in. `KS_LOGGING_CONFIG_PATH` reste un override explicite du document logging ; sans override, la composition fournit son chemin. +- 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. +- 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. - Les valeurs sensibles doivent utiliser des variables d'environnement ou un stockage chiffré 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 : une variable de scénario `ks-pipeline-demo-scenarios` reste `KS_*` même si le desktop la lit. +- 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 structures de configuration exposées à Tauri doivent dériver `TS`. +- 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é. ## Ordre de développement cible diff --git a/kb-app-demo-desktop/CHANGELOG.md b/kb-app-demo-desktop/CHANGELOG.md index b4befbe..74ed5f2 100644 --- a/kb-app-demo-desktop/CHANGELOG.md +++ b/kb-app-demo-desktop/CHANGELOG.md @@ -1,8 +1,13 @@ - + # CHANGELOG — kb-app-demo-desktop +## `0.5.1-pre.007` + +- consomme les nouveaux documents store/wallet/execution via la composition desktop ; +- lit les autorisations d’exécution depuis `ExecutionConfig` et conserve les payloads Tauri actuels jusqu’au chantier de non-divulgation de la prerelease suivante. + ## `0.5.1-pre.006` - remplace `config/app.config.json` par la composition dédiée `config/kb-app-demo-desktop.default.config.json`, sélectionnable via `KB_APP_DEMO_DESKTOP_CONFIG_PATH` ; diff --git a/kb-app-demo-desktop/src/app_state.rs b/kb-app-demo-desktop/src/app_state.rs index 6cc467b..84294a6 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: 14 +// version: 15 //! Shared Tauri application state and startup initialization. @@ -44,12 +44,8 @@ impl crate::AppState { std::result::Result::Ok(profile) => profile.clone(), std::result::Result::Err(error) => return std::result::Result::Err(error), }; - let logging_source_path = ks_config::resolve_composition_source_path( - &workspace_root, - composed.composition.sources.logging.as_str(), - ); let logging_config_path = - resolve_logging_config_path(&workspace_root, &logging_source_path); + resolve_logging_config_path(&workspace_root, &composed.logging_path); let app_config = composed.app_config; let active_profile = match ks_config::active_profile(&app_config) { std::result::Result::Ok(profile) => profile.clone(), @@ -62,19 +58,15 @@ impl crate::AppState { std::result::Result::Ok(config) => config, std::result::Result::Err(error) => return std::result::Result::Err(error), }; - for profile in &composed.composition.profiles { - match ks_logging::logging_profile(&logging_document, profile.logging_profile.as_str()) { - std::result::Result::Ok(_) => (), + let logging_profile_name = ks_config::resolve_profile_selection( + composition_profile.logging_profile.as_deref(), + logging_document.default_profile.as_str(), + ); + let logging_config = + match ks_logging::logging_profile(&logging_document, logging_profile_name) { + std::result::Result::Ok(profile) => profile, std::result::Result::Err(error) => return std::result::Result::Err(error), - } - } - let logging_config = match ks_logging::logging_profile( - &logging_document, - composition_profile.logging_profile.as_str(), - ) { - std::result::Result::Ok(profile) => profile.clone(), - std::result::Result::Err(error) => return std::result::Result::Err(error), - }; + }; let logging_guard = match ks_logging::init_logging(&logging_config) { std::result::Result::Ok(guard) => guard, std::result::Result::Err(error) => return std::result::Result::Err(error), diff --git a/kb-app-demo-desktop/src/demo_execution_metadata_metaplex_token_metadata.rs b/kb-app-demo-desktop/src/demo_execution_metadata_metaplex_token_metadata.rs index 3a0092e..19a410b 100644 --- a/kb-app-demo-desktop/src/demo_execution_metadata_metaplex_token_metadata.rs +++ b/kb-app-demo-desktop/src/demo_execution_metadata_metaplex_token_metadata.rs @@ -1,5 +1,5 @@ // file: kb-app-demo-desktop/src/demo_execution_metadata_metaplex_token_metadata.rs -// version: 7 +// version: 8 //! Desktop adapters for generic and qualified Metaplex Token Metadata execution workflows. @@ -1030,7 +1030,7 @@ pub(crate) async fn demo_execution_metadata_metaplex_execute_campaign( std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; - if !profile.wallet.devnet_send_enabled { + if !profile.execution.devnet_send_enabled { return std::result::Result::Err( "selected profile does not authorize Devnet submissions required by Metaplex fixtures" .to_string(), diff --git a/kb-app-demo-desktop/src/demo_execution_solana_core.rs b/kb-app-demo-desktop/src/demo_execution_solana_core.rs index 9988ad4..72ac20b 100644 --- a/kb-app-demo-desktop/src/demo_execution_solana_core.rs +++ b/kb-app-demo-desktop/src/demo_execution_solana_core.rs @@ -1,5 +1,5 @@ // file: kb-app-demo-desktop/src/demo_execution_solana_core.rs -// version: 16 +// version: 17 //! Tauri adapter for bounded Solana Core execution on Devnet. @@ -631,7 +631,7 @@ fn devnet_profile_options( wallet_alias: profile.wallet.temporary_wallet_alias.clone(), max_spend_lamports: profile.execution.devnet_max_spend_lamports, max_airdrop_lamports: profile.execution.devnet_airdrop_max_lamports, - send_enabled: profile.wallet.devnet_send_enabled, + send_enabled: profile.execution.devnet_send_enabled, require_operator_confirmation: profile.execution.require_operator_confirmation, }); } diff --git a/ks-config/CHANGELOG.md b/ks-config/CHANGELOG.md index 4d4c7b7..c7b930a 100644 --- a/ks-config/CHANGELOG.md +++ b/ks-config/CHANGELOG.md @@ -1,8 +1,16 @@ - + # CHANGELOG — ks-config +## `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` ; +- place `wallets_directory` hors profils wallet et déplace les autorisations `*_send_enabled` dans la politique execution ; +- remplace `app.auto_reconnect_default` par des classes de defaults WebSocket nommées dans le transport, avec overrides propres à chaque endpoint ; +- permet la composition directe des defaults partagés sans fichier applicatif et valide les références de profils d’une composition avant production du contrat runtime ; +- supprime la nécessité d’une composition par défaut propre à `ks-pipeline-demo-scenarios`. + ## `0.5.1-pre.006` - remplace le document source monolithique `app.config.json` par des compositions propres aux binaires, dont `config/kb-app-demo-desktop.default.config.json` et `config/ks-pipeline-demo-scenarios.default.config.json` ; diff --git a/ks-config/README.md b/ks-config/README.md index 2ddfac9..9a1f59f 100644 --- a/ks-config/README.md +++ b/ks-config/README.md @@ -1,49 +1,50 @@ - + # ks-config -`ks-config` fournit les contrats de configuration généralistes Khadhroony Solana, les chargeurs de documents spécialisés et la résolution des compositions utilisées par les binaires. +`ks-config` fournit les contrats de configuration généralistes Khadhroony Solana, les chargeurs des documents spécialisés et la résolution des compositions facultatives utilisées par les binaires. ## Responsabilités - charger l'environnement du workspace et résoudre les placeholders `${KS_*}` / `${KB_*}` ; -- valider les documents transport, listeners et composition contre leurs schémas sous `config/schemas/` ; -- fournir les types partagés d'endpoints, listeners et profils runtime ; -- résoudre les références d'une composition vers des profils transport/listeners ; +- valider les documents transport, listeners, store, wallet, execution et composition contre leurs schémas sous `config/schemas/` ; +- fournir les types partagés d'endpoints, listeners, stockage, wallet, exécution et profils runtime ; +- résoudre les defaults de chaque document ou les overrides d'une composition ; +- refuser une référence de fichier ou de profil inexistante ; - reconstruire temporairement `AppConfig/ProfileConfig` pour les consommateurs existants ; - rester indépendant de la liste des applications et workers futurs. -## Composition +## Defaults et composition -Le desktop possède `config/kb-app-demo-desktop.default.config.json`. Le CLI des scénarios possède `config/ks-pipeline-demo-scenarios.default.config.json`. +Chaque document partagé possède un `default_profile`. Un consommateur qui accepte les defaults n'a pas besoin de composition dédiée. -Une composition choisit : +Le desktop possède `config/kb-app-demo-desktop.default.config.json` parce qu'il sélectionne des combinaisons propres à l'application. `ks-pipeline-demo-scenarios` utilise directement les defaults partagés ; `KS_DEVNET_CONFIG_PATH` permet encore un override explicite lorsque nécessaire. -- les documents partagés à charger ; -- le profil logging ; -- le profil transport ; -- le profil listeners ; -- temporairement les sections encore non séparées. - -Un futur worker pourra donc ajouter son propre `.default.config.json` sans créer une branche spécifique dans `ks-config`. +Un futur worker pourra ajouter son propre `.default.config.json` uniquement s'il doit remplacer des fichiers/profils ou ajouter des paramètres propres au binaire. ## Documents spécialisés actuels - `config/transport.config.json` / `config/schemas/transport.config.schema.json` ; - `config/listeners.config.json` / `config/schemas/listeners.config.schema.json` ; +- `config/store.config.json` / `config/schemas/store.config.schema.json` ; +- `config/wallet.config.json` / `config/schemas/wallet.config.schema.json` ; +- `config/execution.config.json` / `config/schemas/execution.config.schema.json` ; - `config/logging.config.json` reste possédé par `ks-logging` ; - `config/schemas/resolved.app.config.schema.json` décrit uniquement le contrat runtime transitoire. -Les anciens `config/app.config.json` et `config/example.app.config.json` ne sont plus des fichiers runtime. +Les valeurs globales telles que `logs_directory` et `wallets_directory` ne sont pas des propriétés de profils. ## API publique principale - `read_composed_app_config_with_environment` ; +- `read_default_shared_app_config_with_environment` ; - `parse_composition_json`, `composition_profile`, `active_composition_profile` ; -- `read_transport_json_file_with_environment`, `transport_profile` ; -- `read_listeners_json_file_with_environment`, `listeners_profile` ; -- `compose_app_config` ; +- `read_transport_json_file_with_environment`, `transport_profile`, `default_transport_profile` ; +- `read_listeners_json_file_with_environment`, `listeners_profile`, `default_listeners_profile` ; +- `read_store_json_file_with_environment`, `store_profile`, `default_store_profile` ; +- `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é. @@ -51,9 +52,9 @@ Les anciens `config/app.config.json` et `config/example.app.config.json` ne sont `ks-logging` dépend de `ks-config` pour les helpers d'environnement, mais `ks-config` ne possède aucun type logging runtime. -`ks-onchain-transport` peut consommer directement `TransportProfileConfig`. Un worker n'a donc pas besoin d'un profil applicatif complet pour construire ses pools réseau. +`ks-onchain-transport` peut consommer directement `TransportProfileConfig`. -Les sections database/data, wallet, execution et demo restent transitoires dans la composition jusqu'au split suivant. +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. ## Documents diff --git a/ks-config/TODO.md b/ks-config/TODO.md index 366bcde..b188399 100644 --- a/ks-config/TODO.md +++ b/ks-config/TODO.md @@ -1,15 +1,15 @@ - + # TODO — ks-config ## Série `0.5.x` -- [ ] `0.5.1` - sortir `database`, wallet, execution et la configuration spécifique au desktop vers leurs documents/propriétaires dédiés ; réaffecter `logs_directory` à logging et `wallets_directory` à wallet, puis réduire la composition aux références nécessaires. - [ ] `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`. - [ ] 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. diff --git a/ks-config/USAGE.md b/ks-config/USAGE.md index 1afa6cb..99d8f26 100644 --- a/ks-config/USAGE.md +++ b/ks-config/USAGE.md @@ -1,15 +1,28 @@ - + # Utilisation de ks-config ## Rôle de la crate -`ks-config` fournit les types, validations et chargeurs des configurations Khadhroony Solana partagées. Un binaire choisit son propre fichier de composition et combine les profils spécialisés dont il a besoin. +`ks-config` fournit les types, validations et chargeurs des configurations Khadhroony Solana partagées. Un consommateur peut utiliser les defaults des documents spécialisés ou fournir une composition qui remplace seulement les sélections nécessaires. -`ks-config` ne doit pas connaître la liste des futurs workers ou applications. +`ks-config` ne connaît pas la liste des futurs workers ou applications. -## Chargement d'une composition +## Charger les defaults partagés + +```rust +let config = match ks_config::read_default_shared_app_config_with_environment( + std::path::Path::new("."), +) { + Ok(value) => value, + Err(error) => return Err(error), +}; +``` + +Le chargeur combine les `default_profile` indépendants de transport, listeners, store, wallet et execution. Il ne suppose pas que ces profils portent tous le même nom. + +## Charger une composition de binaire ```rust let composed = match ks_config::read_composed_app_config_with_environment( @@ -20,25 +33,22 @@ let composed = match ks_config::read_composed_app_config_with_environment( Err(error) => return Err(error), }; let config = composed.app_config; -let profile = match ks_config::active_profile(&config) { - Ok(value) => value, - Err(error) => return Err(error), -}; ``` Le chargeur : 1. charge l'environnement du workspace ; 2. valide la composition ; -3. résout les chemins `transport` et `listeners` déclarés par la composition ; -4. charge et valide les deux documents spécialisés ; -5. sélectionne les profils référencés ; -6. reconstruit le contrat runtime `AppConfig/ProfileConfig` ; -7. applique les invariants métier existants. +3. résout les six chemins spécialisés ; +4. charge et valide transport, listeners, store, wallet et execution ; +5. vérifie l'existence du profil logging référencé sans créer de dépendance `ks-config -> ks-logging` ; +6. applique les overrides ou les `default_profile` ; +7. reconstruit le contrat runtime `AppConfig/ProfileConfig` ; +8. applique les invariants croisés existants. -Le logging reste chargé par `ks-logging`, car son type runtime appartient à cette crate. La composition fournit néanmoins le chemin et le nom de profil à sélectionner. +Le document logging est ensuite chargé par `ks-logging`, qui possède son type runtime. -## Configuration transport indépendante +## Transport et defaults WebSocket ```rust let transport = match ks_config::read_transport_json_file_with_environment( @@ -48,31 +58,31 @@ let transport = match ks_config::read_transport_json_file_with_environment( Ok(value) => value, Err(error) => return Err(error), }; -let profile = match ks_config::transport_profile(&transport, "mainnet_research") { +let profile = match ks_config::default_transport_profile(&transport) { Ok(value) => value, Err(error) => return Err(error), }; ``` -Le document expose `TransportConfigDocument` et `TransportProfileConfig` avec les endpoints HTTP/WebSocket et leurs rôles. +Les endpoints WebSocket source sélectionnent une classe `ws_endpoint_defaults`; les `overrides` propres à l'endpoint sont appliqués avant de produire `TransportProfileConfig`. -## Configuration listeners indépendante +## Listeners -```rust -let listeners = match ks_config::read_listeners_json_file_with_environment( - std::path::Path::new("config/listeners.config.json"), - std::path::Path::new("."), -) { - Ok(value) => value, - Err(error) => return Err(error), -}; -let profile = match ks_config::listeners_profile(&listeners, "mainnet_research") { - Ok(value) => value, - Err(error) => return Err(error), -}; -``` +`default_listeners_profile` ou `listeners_profile` sélectionnent un ensemble de subscriptions. `resolved_listener_config` le convertit vers le contrat runtime transitoire `ListenerConfig`. -`resolved_listener_config` convertit ce profil spécialisé vers le contrat runtime `ListenerConfig` utilisé pendant la phase de transition. +## Store + +`store.config.json` possède les paramètres de backend et les profils PostgreSQL/SQLite. Les helpers principaux sont `read_store_json_file_with_environment`, `default_store_profile` et `store_profile`. + +## Wallet + +`wallet.config.json` porte `wallets_directory` hors profils. Chaque profil définit un chemin relatif, le cluster, l'alias et la politique temporaire/persistante. `resolved_wallet_profile` construit le chemin runtime final. + +Les autorisations de soumission ne font plus partie du wallet. + +## Exécution + +`execution.config.json` possède simulation, confirmation, limites et `*_send_enabled`. Les helpers sont `read_execution_json_file_with_environment`, `default_execution_profile` et `execution_profile`. ## Chargement de l'environnement @@ -85,34 +95,19 @@ let report = match ks_config::load_workspace_environment(std::path::Path::new(". Par défaut, `ks-config` sélectionne `.env`; `KS_ENV_FILE` permet d'en choisir un autre sans écraser les variables déjà présentes dans le processus. -## Résolution des placeholders +Les valeurs globales peuvent être remplacées, notamment : -```rust -let raw = r#"{"databaseUrl":"${KS_SECRET_POSTGRES_DEVNET_URL:-postgres://localhost/ks}"}"#; -let resolved = ks_config::resolve_environment_placeholders(raw); -assert!(resolved.contains("databaseUrl")); +```text +KS_LOGS_DIRECTORY +KS_WALLETS_DIRECTORY ``` -La chaîne résolue peut encore contenir des secrets. Elle reste strictement backend jusqu'à l'introduction de la représentation sensible dédiée. - ## Contrat runtime résolu -`AppConfig` et `ProfileConfig` restent provisoirement le contrat runtime commun afin de ne pas casser tous les consommateurs pendant les splits successifs. Ils ne correspondent plus à un fichier `config/app.config.json` chargé directement. +`AppConfig` et `ProfileConfig` restent provisoirement le contrat runtime commun afin de ne pas casser tous les consommateurs pendant les splits successifs. Ils ne correspondent plus à un fichier source `app.config.json`. -Le schéma de ce contrat résolu est `config/schemas/resolved.app.config.schema.json`. Les fixtures correspondantes sont sous `test-fixtures/config/` et ne sont pas des configurations runtime. +Le schéma de ce contrat est `config/schemas/resolved.app.config.schema.json`. Les fixtures correspondantes sont sous `test-fixtures/config/`. -## Types publics importants +## Frontière TypeScript -- `CompositionConfigDocument`, `CompositionProfileConfig`, `CompositionConfigSources` ; -- `TransportConfigDocument`, `TransportProfileConfig` ; -- `ListenersConfigDocument`, `ListenersProfileConfig` ; -- `AppConfig`, `ProfileConfig` comme contrat runtime de transition ; -- `HttpEndpointConfig`, `WsEndpointConfig`, `EndpointRoleConfig` ; -- `ListenerConfig` et ses variantes ; -- `DatabaseConfig`, `WalletConfig`, `ExecutionConfig` jusqu'à leur split dédié. - -Les types logging appartiennent à `ks-logging`. - -## Prochaine frontière - -Les sections store/data, wallet, execution et desktop seront sorties de la composition dans la prerelease suivante. Une fois cette décomposition terminée, les surfaces source/runtime/public/diagnostic et le camouflage des secrets pourront être définis sur des responsabilités stables. +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. diff --git a/ks-config/src/composition.rs b/ks-config/src/composition.rs index f89c599..4a6480e 100644 --- a/ks-config/src/composition.rs +++ b/ks-config/src/composition.rs @@ -1,10 +1,16 @@ // file: ks-config/src/composition.rs -// version: 1 +// version: 2 //! Binary composition configuration and resolution into the shared runtime profile contract. const COMPOSITION_JSON_SCHEMA: &str = include_str!("../../config/schemas/composition.config.schema.json"); +const DEFAULT_LOGGING_CONFIG_PATH: &str = "config/logging.config.json"; +const DEFAULT_TRANSPORT_CONFIG_PATH: &str = "config/transport.config.json"; +const DEFAULT_LISTENERS_CONFIG_PATH: &str = "config/listeners.config.json"; +const DEFAULT_STORE_CONFIG_PATH: &str = "config/store.config.json"; +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)] @@ -26,33 +32,58 @@ pub struct CompositionConfigSources { pub transport: std::string::String, /// Listener configuration document path relative to the workspace root unless absolute. pub listeners: std::string::String, + /// Store configuration document path relative to the workspace root unless absolute. + pub store: std::string::String, + /// Wallet configuration document path relative to the workspace root unless absolute. + pub wallet: std::string::String, + /// Execution configuration document path relative to the workspace root unless absolute. + pub execution: std::string::String, } -/// Named composition profile referencing shared configuration profiles. +/// Named composition profile with optional overrides of each shared document default. #[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize)] pub struct CompositionProfileConfig { /// Composition profile code. pub name: std::string::String, - /// Application metadata retained until the remaining configuration split is completed. + /// Application metadata retained until the application-specific split is completed. pub app: crate::AppSectionConfig, - /// Logging profile selected from the referenced logging document. - pub logging_profile: std::string::String, - /// Transport profile selected from the referenced transport document. - pub transport_profile: std::string::String, - /// Listener profile selected from the referenced listener document. - pub listeners_profile: std::string::String, - /// Database configuration retained until the store split prerelease. - pub database: crate::DatabaseConfig, - /// Local data directories retained until their owning configuration files are split. - pub data: crate::DataConfig, - /// Wallet configuration retained until the wallet split prerelease. - pub wallet: crate::WalletConfig, - /// Execution policy retained until the execution split prerelease. - pub execution: crate::ExecutionConfig, - /// Desktop demo flags retained until the binary-specific split prerelease. + /// Optional logging profile override. + pub logging_profile: std::option::Option, + /// Optional transport profile override. + pub transport_profile: std::option::Option, + /// Optional listener profile override. + pub listeners_profile: std::option::Option, + /// Optional store profile override. + pub store_profile: std::option::Option, + /// Optional wallet profile override. + 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)] +pub struct ComposedAppConfig { + /// Parsed binary composition document. + pub composition: CompositionConfigDocument, + /// Resolved runtime application configuration. + pub app_config: crate::AppConfig, + /// Resolved logging source path. + pub logging_path: std::path::PathBuf, + /// Resolved transport source path. + pub transport_path: std::path::PathBuf, + /// Resolved listener source path. + pub listeners_path: std::path::PathBuf, + /// Resolved store source path. + pub store_path: std::path::PathBuf, + /// Resolved wallet source path. + pub wallet_path: std::path::PathBuf, + /// Resolved execution source path. + pub execution_path: std::path::PathBuf, +} + /// Returns the embedded binary composition JSON Schema text. pub fn composition_json_schema_text() -> &'static str { return COMPOSITION_JSON_SCHEMA; @@ -60,9 +91,9 @@ pub fn composition_json_schema_text() -> &'static str { /// Parses the embedded binary composition JSON Schema into a JSON value. pub fn composition_json_schema_value() -> ks_core::Result { - let schema_result = serde_json::from_str::(COMPOSITION_JSON_SCHEMA); - return match schema_result { - std::result::Result::Ok(schema) => std::result::Result::Ok(schema), + let result = serde_json::from_str::(COMPOSITION_JSON_SCHEMA); + return match result { + std::result::Result::Ok(value) => std::result::Result::Ok(value), std::result::Result::Err(error) => std::result::Result::Err(ks_core::Error::new( "composition_config_schema_parse_failed", error.to_string(), @@ -73,11 +104,11 @@ pub fn composition_json_schema_value() -> ks_core::Result { /// Validates raw binary composition JSON against its embedded schema. pub fn validate_composition_json_schema(raw_json: &str) -> ks_core::Result<()> { let schema = match composition_json_schema_value() { - std::result::Result::Ok(schema) => schema, + std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; let instance = match serde_json::from_str::(raw_json) { - std::result::Result::Ok(instance) => instance, + std::result::Result::Ok(value) => value, std::result::Result::Err(error) => { return std::result::Result::Err(ks_core::Error::new( "composition_config_json_parse_failed", @@ -86,7 +117,7 @@ pub fn validate_composition_json_schema(raw_json: &str) -> ks_core::Result<()> { }, }; let validator = match jsonschema::validator_for(&schema) { - std::result::Result::Ok(validator) => validator, + std::result::Result::Ok(value) => value, std::result::Result::Err(error) => { return std::result::Result::Err(ks_core::Error::new( "composition_config_schema_compile_failed", @@ -110,7 +141,7 @@ pub fn parse_composition_json(raw_json: &str) -> ks_core::Result return std::result::Result::Err(error), } let document = match serde_json::from_str::(raw_json) { - std::result::Result::Ok(document) => document, + std::result::Result::Ok(value) => value, std::result::Result::Err(error) => { return std::result::Result::Err(ks_core::Error::new( "composition_config_json_decode_failed", @@ -129,7 +160,7 @@ pub fn read_composition_json_file( path: &std::path::Path, ) -> ks_core::Result { let raw_json = match std::fs::read_to_string(path) { - std::result::Result::Ok(content) => content, + std::result::Result::Ok(value) => value, std::result::Result::Err(error) => { return std::result::Result::Err(ks_core::Error::new( "composition_config_file_read_failed", @@ -150,7 +181,7 @@ pub fn read_composition_json_file_with_environment( std::result::Result::Err(error) => return std::result::Result::Err(error), } let raw_json = match std::fs::read_to_string(path) { - std::result::Result::Ok(content) => content, + std::result::Result::Ok(value) => value, std::result::Result::Err(error) => { return std::result::Result::Err(ks_core::Error::new( "composition_config_file_read_failed", @@ -158,8 +189,7 @@ pub fn read_composition_json_file_with_environment( )); }, }; - let resolved = crate::resolve_environment_placeholders(&raw_json); - return parse_composition_json(&resolved); + return parse_composition_json(&crate::resolve_environment_placeholders(&raw_json)); } /// Returns one named binary composition profile. @@ -197,36 +227,79 @@ pub fn resolve_composition_source_path( return workspace_root.join(path); } -/// Resolves all shared transport/listener selections into the existing runtime application contract. +/// Resolves one optional composition override against a shared document default profile. +pub fn resolve_profile_selection<'a>( + override_name: std::option::Option<&'a str>, + default_profile: &'a str, +) -> &'a str { + return match override_name { + std::option::Option::Some(value) if !value.trim().is_empty() => value, + _ => default_profile, + }; +} + +/// Resolves all shared source selections into the runtime application contract. pub fn compose_app_config( document: &CompositionConfigDocument, transport: &crate::TransportConfigDocument, listeners: &crate::ListenersConfigDocument, + store: &crate::StoreConfigDocument, + wallet: &crate::WalletConfigDocument, + execution: &crate::ExecutionConfigDocument, ) -> ks_core::Result { let mut profiles = std::vec::Vec::::new(); for profile in &document.profiles { - let transport_profile = - match crate::transport_profile(transport, profile.transport_profile.as_str()) { - std::result::Result::Ok(value) => value, - std::result::Result::Err(error) => return std::result::Result::Err(error), - }; - let listeners_profile = - match crate::listeners_profile(listeners, profile.listeners_profile.as_str()) { - std::result::Result::Ok(value) => value, - std::result::Result::Err(error) => return std::result::Result::Err(error), - }; + let transport_name = resolve_profile_selection( + profile.transport_profile.as_deref(), + transport.default_profile.as_str(), + ); + let listeners_name = resolve_profile_selection( + profile.listeners_profile.as_deref(), + listeners.default_profile.as_str(), + ); + let store_name = resolve_profile_selection( + profile.store_profile.as_deref(), + store.default_profile.as_str(), + ); + let wallet_name = resolve_profile_selection( + profile.wallet_profile.as_deref(), + wallet.default_profile.as_str(), + ); + let execution_name = resolve_profile_selection( + profile.execution_profile.as_deref(), + execution.default_profile.as_str(), + ); + let transport_profile = match crate::transport_profile(transport, transport_name) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let listeners_profile = match crate::listeners_profile(listeners, listeners_name) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let store_profile = match crate::store_profile(store, store_name) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let wallet_profile = match crate::resolved_wallet_profile(wallet, wallet_name) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let execution_profile = match crate::execution_profile(execution, execution_name) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; profiles.push(crate::ProfileConfig { name: profile.name.clone(), app: profile.app.clone(), - database: profile.database.clone(), - data: profile.data.clone(), + database: store_profile.database.clone(), solana: crate::SolanaConfig { - http_endpoints: transport_profile.http_endpoints.clone(), - ws_endpoints: transport_profile.ws_endpoints.clone(), + http_endpoints: transport_profile.http_endpoints, + ws_endpoints: transport_profile.ws_endpoints, listeners: crate::resolved_listener_config(listeners_profile), }, - wallet: profile.wallet.clone(), - execution: profile.execution.clone(), + wallet: wallet_profile, + execution: execution_profile.config.clone(), demo: profile.demo.clone(), }); } @@ -240,20 +313,28 @@ pub fn compose_app_config( }; } -/// Reads a binary composition and its referenced transport/listener documents into the resolved runtime contract. +/// Reads a binary composition and all referenced shared documents into the runtime contract. pub fn read_composed_app_config_with_environment( composition_path: &std::path::Path, workspace_root: &std::path::Path, -) -> ks_core::Result { +) -> ks_core::Result { let composition = match read_composition_json_file_with_environment(composition_path, workspace_root) { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; + let logging_path = + resolve_composition_source_path(workspace_root, composition.sources.logging.as_str()); let transport_path = resolve_composition_source_path(workspace_root, composition.sources.transport.as_str()); let listeners_path = resolve_composition_source_path(workspace_root, composition.sources.listeners.as_str()); + let store_path = + resolve_composition_source_path(workspace_root, composition.sources.store.as_str()); + let wallet_path = + resolve_composition_source_path(workspace_root, composition.sources.wallet.as_str()); + let execution_path = + resolve_composition_source_path(workspace_root, composition.sources.execution.as_str()); let transport = match crate::read_transport_json_file_with_environment(&transport_path, workspace_root) { std::result::Result::Ok(value) => value, @@ -264,34 +345,120 @@ pub fn read_composed_app_config_with_environment( std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; - let app_config = match compose_app_config(&composition, &transport, &listeners) { + let store = match crate::read_store_json_file_with_environment(&store_path, workspace_root) { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; - return std::result::Result::Ok(crate::ComposedAppConfig { + let wallet = match crate::read_wallet_json_file_with_environment(&wallet_path, workspace_root) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let execution = + match crate::read_execution_json_file_with_environment(&execution_path, workspace_root) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + match validate_logging_references_with_environment(&logging_path, workspace_root, &composition) + { + std::result::Result::Ok(()) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let app_config = + match compose_app_config(&composition, &transport, &listeners, &store, &wallet, &execution) + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return std::result::Result::Ok(ComposedAppConfig { composition, app_config, + logging_path, transport_path, listeners_path, + store_path, + wallet_path, + execution_path, }); } -/// Resolved application configuration and the source documents needed by a binary. -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct ComposedAppConfig { - /// Parsed binary composition document. - pub composition: CompositionConfigDocument, - /// Resolved runtime application configuration. - pub app_config: crate::AppConfig, - /// Resolved transport source path. - pub transport_path: std::path::PathBuf, - /// Resolved listener source path. - pub listeners_path: std::path::PathBuf, +/// Loads canonical shared documents and composes their independent default profiles without an application composition file. +pub fn read_default_shared_app_config_with_environment( + workspace_root: &std::path::Path, +) -> ks_core::Result { + let transport_path = workspace_root.join(DEFAULT_TRANSPORT_CONFIG_PATH); + let listeners_path = workspace_root.join(DEFAULT_LISTENERS_CONFIG_PATH); + let store_path = workspace_root.join(DEFAULT_STORE_CONFIG_PATH); + let wallet_path = workspace_root.join(DEFAULT_WALLET_CONFIG_PATH); + let execution_path = workspace_root.join(DEFAULT_EXECUTION_CONFIG_PATH); + let transport = + match crate::read_transport_json_file_with_environment(&transport_path, workspace_root) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let listeners = + match crate::read_listeners_json_file_with_environment(&listeners_path, workspace_root) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let store = match crate::read_store_json_file_with_environment(&store_path, workspace_root) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let wallet = match crate::read_wallet_json_file_with_environment(&wallet_path, workspace_root) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let execution = + match crate::read_execution_json_file_with_environment(&execution_path, workspace_root) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let defaults = [ + transport.default_profile.as_str(), + listeners.default_profile.as_str(), + store.default_profile.as_str(), + wallet.default_profile.as_str(), + execution.default_profile.as_str(), + ]; + let profile_name = if defaults.iter().all(|value| return *value == defaults[0]) { + defaults[0].to_string() + } else { + "shared_default".to_string() + }; + let composition = CompositionConfigDocument { + active_profile: profile_name.clone(), + sources: CompositionConfigSources { + logging: DEFAULT_LOGGING_CONFIG_PATH.to_string(), + transport: DEFAULT_TRANSPORT_CONFIG_PATH.to_string(), + listeners: DEFAULT_LISTENERS_CONFIG_PATH.to_string(), + store: DEFAULT_STORE_CONFIG_PATH.to_string(), + wallet: DEFAULT_WALLET_CONFIG_PATH.to_string(), + execution: DEFAULT_EXECUTION_CONFIG_PATH.to_string(), + }, + profiles: vec![CompositionProfileConfig { + name: profile_name.clone(), + app: crate::AppSectionConfig { + name: "khadhroony-solana".to_string(), + environment: profile_name, + }, + 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); } /// Validates a typed binary composition document before referenced documents are loaded. pub fn validate_composition_document(document: &CompositionConfigDocument) -> ks_core::Result<()> { - match crate::require_non_empty(&document.active_profile, "active_profile") { + match crate::require_non_empty(&document.active_profile, "composition.active_profile") { std::result::Result::Ok(()) => (), std::result::Result::Err(error) => return std::result::Result::Err(error), } @@ -299,6 +466,9 @@ pub fn validate_composition_document(document: &CompositionConfigDocument) -> ks &document.sources.logging, &document.sources.transport, &document.sources.listeners, + &document.sources.store, + &document.sources.wallet, + &document.sources.execution, ] { match crate::require_non_empty(source, "composition.sources") { std::result::Result::Ok(()) => (), @@ -317,12 +487,19 @@ pub fn validate_composition_document(document: &CompositionConfigDocument) -> ks std::result::Result::Ok(()) => (), std::result::Result::Err(error) => return std::result::Result::Err(error), } - for reference in - [&profile.logging_profile, &profile.transport_profile, &profile.listeners_profile] - { - match crate::require_non_empty(reference, "composition.profile.reference") { - std::result::Result::Ok(()) => (), - std::result::Result::Err(error) => return std::result::Result::Err(error), + for reference in [ + profile.logging_profile.as_ref(), + profile.transport_profile.as_ref(), + profile.listeners_profile.as_ref(), + profile.store_profile.as_ref(), + profile.wallet_profile.as_ref(), + profile.execution_profile.as_ref(), + ] { + if let std::option::Option::Some(value) = reference + && let std::result::Result::Err(error) = + crate::require_non_empty(value, "composition.profile.reference") + { + return std::result::Result::Err(error); } } if !names.insert(profile.name.clone()) { @@ -338,14 +515,85 @@ pub fn validate_composition_document(document: &CompositionConfigDocument) -> ks }; } +fn validate_logging_references_with_environment( + path: &std::path::Path, + workspace_root: &std::path::Path, + composition: &CompositionConfigDocument, +) -> ks_core::Result<()> { + match crate::load_workspace_environment(workspace_root) { + std::result::Result::Ok(_) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let raw_json = match std::fs::read_to_string(path) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(ks_core::Error::new( + "composition_logging_file_read_failed", + error.to_string(), + )); + }, + }; + let resolved = crate::resolve_environment_placeholders(&raw_json); + let value = match serde_json::from_str::(&resolved) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(ks_core::Error::new( + "composition_logging_json_parse_failed", + error.to_string(), + )); + }, + }; + let default_profile = match value.get("default_profile").and_then(serde_json::Value::as_str) { + std::option::Option::Some(value) if !value.trim().is_empty() => value, + _ => { + return std::result::Result::Err(ks_core::Error::new( + "composition_logging_default_profile_missing", + path.display().to_string(), + )); + }, + }; + let profiles = match value.get("profiles").and_then(serde_json::Value::as_array) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(ks_core::Error::new( + "composition_logging_profiles_missing", + path.display().to_string(), + )); + }, + }; + let mut names = std::collections::BTreeSet::::new(); + for profile in profiles { + if let std::option::Option::Some(name) = + profile.get("name").and_then(serde_json::Value::as_str) + { + names.insert(name.to_string()); + } + } + if !names.contains(default_profile) { + return std::result::Result::Err(ks_core::Error::new( + "composition_logging_default_profile_not_found", + default_profile.to_string(), + )); + } + for profile in &composition.profiles { + let selected = + resolve_profile_selection(profile.logging_profile.as_deref(), default_profile); + if !names.contains(selected) { + return std::result::Result::Err(ks_core::Error::new( + "composition_logging_profile_not_found", + selected.to_string(), + )); + } + } + return std::result::Result::Ok(()); +} + #[cfg(test)] 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"); - const DEFAULT_TRANSPORT: &str = include_str!("../../config/transport.config.json"); - const DEFAULT_LISTENERS: &str = include_str!("../../config/listeners.config.json"); #[test] fn default_and_example_compositions_validate() { @@ -354,20 +602,19 @@ mod tests { } #[test] - fn default_composition_resolves_shared_transport_and_listener_profiles() { + fn local_devnet_composition_uses_shared_defaults() { let composition = match super::parse_composition_json(DEFAULT_COMPOSITION) { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => panic!("default composition must parse: {error}"), }; - let transport = match crate::parse_transport_json(DEFAULT_TRANSPORT) { + let profile = match super::composition_profile(&composition, "local_devnet") { std::result::Result::Ok(value) => value, - std::result::Result::Err(error) => panic!("default transport must parse: {error}"), + std::result::Result::Err(error) => { + panic!("local Devnet composition must resolve: {error}") + }, }; - let listeners = match crate::parse_listeners_json(DEFAULT_LISTENERS) { - std::result::Result::Ok(value) => value, - std::result::Result::Err(error) => panic!("default listeners must parse: {error}"), - }; - let resolved = super::compose_app_config(&composition, &transport, &listeners); - assert!(resolved.is_ok()); + assert!(profile.logging_profile.is_none()); + assert!(profile.transport_profile.is_none()); + assert!(profile.store_profile.is_none()); } } diff --git a/ks-config/src/execution.rs b/ks-config/src/execution.rs new file mode 100644 index 0000000..d958462 --- /dev/null +++ b/ks-config/src/execution.rs @@ -0,0 +1,172 @@ +// file: ks-config/src/execution.rs +// version: 1 + +//! Independent execution policy configuration document loading and validation. + +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)] +pub struct ExecutionConfigDocument { + /// Default execution profile used when a composition does not override it. + pub default_profile: std::string::String, + /// Named execution policy profiles. + pub profiles: std::vec::Vec, +} + +/// Named execution policy profile. +#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize)] +pub struct ExecutionProfileConfig { + /// Profile code. + pub name: std::string::String, + /// Runtime execution safety configuration. + #[serde(flatten)] + pub config: crate::ExecutionConfig, +} + +/// Returns the embedded execution JSON Schema text. +pub fn execution_json_schema_text() -> &'static str { + return EXECUTION_JSON_SCHEMA; +} + +/// Parses and validates one execution configuration document. +pub fn parse_execution_json(raw_json: &str) -> ks_core::Result { + let schema = match serde_json::from_str::(EXECUTION_JSON_SCHEMA) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(ks_core::Error::new( + "execution_config_schema_parse_failed", + error.to_string(), + )); + }, + }; + let instance = match serde_json::from_str::(raw_json) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(ks_core::Error::new( + "execution_config_json_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( + "execution_config_schema_compile_failed", + error.to_string(), + )); + }, + }; + if let std::result::Result::Err(error) = validator.validate(&instance) { + return std::result::Result::Err(ks_core::Error::new( + "execution_config_schema_validation_failed", + error.to_string(), + )); + } + let document = match serde_json::from_str::(raw_json) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(ks_core::Error::new( + "execution_config_json_decode_failed", + error.to_string(), + )); + }, + }; + return match validate_execution_document(&document) { + std::result::Result::Ok(()) => std::result::Result::Ok(document), + std::result::Result::Err(error) => std::result::Result::Err(error), + }; +} + +/// Loads environment placeholders and parses one execution configuration file. +pub fn read_execution_json_file_with_environment( + path: &std::path::Path, + workspace_root: &std::path::Path, +) -> ks_core::Result { + match crate::load_workspace_environment(workspace_root) { + std::result::Result::Ok(_) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let raw_json = match std::fs::read_to_string(path) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(ks_core::Error::new( + "execution_config_file_read_failed", + error.to_string(), + )); + }, + }; + return parse_execution_json(&crate::resolve_environment_placeholders(&raw_json)); +} + +/// Returns one named execution profile. +pub fn execution_profile<'a>( + document: &'a ExecutionConfigDocument, + profile_name: &str, +) -> ks_core::Result<&'a ExecutionProfileConfig> { + for profile in &document.profiles { + if profile.name == profile_name { + return std::result::Result::Ok(profile); + } + } + return std::result::Result::Err(ks_core::Error::new( + "execution_profile_not_found", + profile_name.to_string(), + )); +} + +/// Returns the default execution profile. +pub fn default_execution_profile( + document: &ExecutionConfigDocument, +) -> ks_core::Result<&ExecutionProfileConfig> { + return execution_profile(document, document.default_profile.as_str()); +} + +/// Validates one typed execution configuration document. +pub fn validate_execution_document(document: &ExecutionConfigDocument) -> ks_core::Result<()> { + match crate::require_non_empty(&document.default_profile, "execution.default_profile") { + std::result::Result::Ok(()) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + if document.profiles.is_empty() { + return std::result::Result::Err(ks_core::Error::new( + "execution_config_profiles_empty", + "at least one execution profile is required", + )); + } + let mut names = std::collections::BTreeSet::::new(); + for profile in &document.profiles { + match crate::require_non_empty(&profile.name, "execution.profile.name") { + std::result::Result::Ok(()) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + if !names.insert(profile.name.clone()) { + return std::result::Result::Err(ks_core::Error::new( + "execution_profile_name_duplicate", + profile.name.clone(), + )); + } + match crate::validate_execution(&profile.config) { + std::result::Result::Ok(()) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + } + return match default_execution_profile(document) { + std::result::Result::Ok(_) => std::result::Result::Ok(()), + std::result::Result::Err(error) => std::result::Result::Err(error), + }; +} + +#[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"); + + #[test] + fn default_and_example_execution_configs_validate() { + assert!(super::parse_execution_json(DEFAULT_EXECUTION).is_ok()); + assert!(super::parse_execution_json(EXAMPLE_EXECUTION).is_ok()); + } +} diff --git a/ks-config/src/lib.rs b/ks-config/src/lib.rs index 4582f76..e1558cc 100644 --- a/ks-config/src/lib.rs +++ b/ks-config/src/lib.rs @@ -1,16 +1,19 @@ // file: ks-config/src/lib.rs -// version: 7 +// version: 8 -//! Khadhroony Solana application configuration contract and loading helpers. +//! Khadhroony Solana shared configuration contracts and composition helpers. #![warn(missing_docs)] #![deny(unreachable_pub)] #![forbid(unsafe_code)] mod composition; mod environment; +mod execution; mod listeners; mod settings; +mod store; mod transport; +mod wallet; /// Exposes the resolved application configuration plus its composition source. pub use self::composition::ComposedAppConfig; @@ -38,8 +41,12 @@ pub use self::composition::read_composed_app_config_with_environment; pub use self::composition::read_composition_json_file; /// Exposes binary composition loading with workspace environment resolution. pub use self::composition::read_composition_json_file_with_environment; +/// Exposes canonical shared configuration loading without an application composition file. +pub use self::composition::read_default_shared_app_config_with_environment; /// Exposes composition source path resolution. pub use self::composition::resolve_composition_source_path; +/// Exposes composition profile fallback resolution. +pub use self::composition::resolve_profile_selection; /// Exposes typed binary composition validation. pub use self::composition::validate_composition_document; /// Exposes binary composition JSON Schema validation. @@ -50,12 +57,28 @@ pub use self::environment::EnvironmentLoadReport; pub use self::environment::load_workspace_environment; /// Exposes environment placeholder resolution. pub use self::environment::resolve_environment_placeholders; +/// Exposes the independent execution configuration document. +pub use self::execution::ExecutionConfigDocument; +/// Exposes one named execution profile. +pub use self::execution::ExecutionProfileConfig; +/// Exposes the default execution profile resolver. +pub use self::execution::default_execution_profile; +/// Exposes the execution JSON Schema text. +pub use self::execution::execution_json_schema_text; +/// Exposes one named execution profile resolver. +pub use self::execution::execution_profile; +/// Exposes execution configuration parsing from JSON. +pub use self::execution::parse_execution_json; +/// Exposes execution configuration loading with workspace environment resolution. +pub use self::execution::read_execution_json_file_with_environment; +/// Exposes typed execution document validation. +pub use self::execution::validate_execution_document; /// Exposes the independent listener configuration document. pub use self::listeners::ListenersConfigDocument; /// Exposes one named listener profile. pub use self::listeners::ListenersProfileConfig; -/// Exposes the active listener profile resolver. -pub use self::listeners::active_listeners_profile; +/// Exposes the default listener profile resolver. +pub use self::listeners::default_listeners_profile; /// Exposes the listener JSON Schema text. pub use self::listeners::listeners_json_schema_text; /// Exposes the listener JSON Schema value parser. @@ -76,15 +99,13 @@ pub use self::listeners::validate_listeners_document; pub use self::listeners::validate_listeners_json_schema; /// Exposes the account listener configuration type. pub use self::settings::AccountListenerConfig; -/// Exposes the root application configuration type. +/// Exposes the root resolved runtime configuration type. pub use self::settings::AppConfig; -/// Exposes the application section configuration type. +/// Exposes the application metadata configuration type. pub use self::settings::AppSectionConfig; -/// Exposes the local data configuration type. -pub use self::settings::DataConfig; /// Exposes the database configuration type. pub use self::settings::DatabaseConfig; -/// Exposes the demo application configuration type. +/// Exposes the temporary demo application configuration type. pub use self::settings::DemoConfig; /// Exposes the endpoint role configuration type. pub use self::settings::EndpointRoleConfig; @@ -118,9 +139,9 @@ pub use self::settings::config_json_schema_text; pub use self::settings::config_json_schema_value; /// Exposes resolved runtime configuration parsing from a JSON string. pub use self::settings::parse_config_json; -/// Exposes resolved runtime configuration loading from a filesystem path. +/// Exposes resolved runtime snapshot loading from a filesystem path. pub use self::settings::read_config_json_file; -/// Exposes resolved runtime configuration loading with workspace environment resolution. +/// 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; @@ -130,16 +151,42 @@ pub use self::settings::serialize_config_json_pretty; pub use self::settings::validate_config; /// Exposes the JSON Schema validator for raw resolved runtime configuration JSON. pub use self::settings::validate_config_json_schema; +/// Exposes the independent store configuration document. +pub use self::store::StoreConfigDocument; +/// Exposes one named store profile. +pub use self::store::StoreProfileConfig; +/// Exposes the default store profile resolver. +pub use self::store::default_store_profile; +/// Exposes store configuration parsing from JSON. +pub use self::store::parse_store_json; +/// Exposes store configuration loading with workspace environment resolution. +pub use self::store::read_store_json_file_with_environment; +/// Exposes the store JSON Schema text. +pub use self::store::store_json_schema_text; +/// Exposes the store JSON Schema value parser. +pub use self::store::store_json_schema_value; +/// Exposes one named store profile resolver. +pub use self::store::store_profile; +/// Exposes typed store document validation. +pub use self::store::validate_store_document; +/// Exposes store JSON Schema validation. +pub use self::store::validate_store_json_schema; /// Exposes the independent transport configuration document. pub use self::transport::TransportConfigDocument; -/// Exposes one named transport profile. +/// Exposes one fully resolved transport profile. pub use self::transport::TransportProfileConfig; -/// Exposes the active transport profile resolver. -pub use self::transport::active_transport_profile; +/// Exposes one transport source profile. +pub use self::transport::TransportProfileSourceConfig; +/// Exposes WebSocket endpoint default values. +pub use self::transport::WsEndpointDefaultsConfig; +/// Exposes WebSocket endpoint-specific overrides. +pub use self::transport::WsEndpointOverridesConfig; +/// Exposes one source WebSocket endpoint before defaults are resolved. +pub use self::transport::WsEndpointSourceConfig; +/// Exposes the default transport profile resolver. +pub use self::transport::default_transport_profile; /// Exposes transport configuration parsing from JSON. pub use self::transport::parse_transport_json; -/// Exposes transport configuration loading from a filesystem path. -pub use self::transport::read_transport_json_file; /// Exposes transport configuration loading with workspace environment resolution. pub use self::transport::read_transport_json_file_with_environment; /// Exposes the transport JSON Schema text. @@ -152,12 +199,36 @@ pub use self::transport::transport_profile; pub use self::transport::validate_transport_document; /// Exposes transport JSON Schema validation. pub use self::transport::validate_transport_json_schema; +/// Exposes the independent wallet configuration document. +pub use self::wallet::WalletConfigDocument; +/// Exposes one named wallet source profile. +pub use self::wallet::WalletProfileConfig; +/// Exposes the default wallet source profile resolver. +pub use self::wallet::default_wallet_profile; +/// Exposes wallet configuration parsing from JSON. +pub use self::wallet::parse_wallet_json; +/// Exposes wallet configuration loading with workspace environment resolution. +pub use self::wallet::read_wallet_json_file_with_environment; +/// Exposes one resolved runtime wallet profile. +pub use self::wallet::resolved_wallet_profile; +/// Exposes typed wallet document validation. +pub use self::wallet::validate_wallet_document; +/// Exposes the wallet JSON Schema text. +pub use self::wallet::wallet_json_schema_text; +/// Exposes one named wallet source profile resolver. +pub use self::wallet::wallet_profile; -/// Internal non-empty string validation shared by configuration documents. +/// Keeps `require_non_empty` available to internal configuration modules. pub(crate) use self::settings::require_non_empty; -/// Internal typed HTTP endpoint validation shared by resolved and transport documents. +/// Keeps `validate_database` available to internal configuration modules. +pub(crate) use self::settings::validate_database; +/// Keeps `validate_execution` available to internal configuration modules. +pub(crate) use self::settings::validate_execution; +/// Keeps `validate_http_endpoint` available to internal configuration modules. pub(crate) use self::settings::validate_http_endpoint; -/// Internal typed listener validation shared by resolved and listener documents. +/// Keeps `validate_listeners` available to internal configuration modules. pub(crate) use self::settings::validate_listeners; -/// Internal typed WebSocket endpoint validation shared by resolved and transport documents. +/// Keeps `validate_wallet` available to internal configuration modules. +pub(crate) use self::settings::validate_wallet; +/// Keeps `validate_ws_endpoint` available to internal configuration modules. pub(crate) use self::settings::validate_ws_endpoint; diff --git a/ks-config/src/listeners.rs b/ks-config/src/listeners.rs index 27d5f5a..e56a977 100644 --- a/ks-config/src/listeners.rs +++ b/ks-config/src/listeners.rs @@ -1,5 +1,5 @@ // file: ks-config/src/listeners.rs -// version: 1 +// version: 2 //! Independent Solana listener configuration document loading and validation. @@ -9,8 +9,8 @@ const LISTENERS_JSON_SCHEMA: &str = /// Root listener document containing independently selectable named profiles. #[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize)] pub struct ListenersConfigDocument { - /// Active listener profile used by consumers that do not provide an explicit selection. - pub active_profile: std::string::String, + /// Default listener profile used by consumers that do not provide an explicit selection. + pub default_profile: std::string::String, /// Named listener profiles available in this document. pub profiles: std::vec::Vec, } @@ -157,11 +157,11 @@ pub fn listeners_profile<'a>( )); } -/// Returns the active listener profile declared by the document. -pub fn active_listeners_profile( +/// Returns the default listener profile declared by the document. +pub fn default_listeners_profile( document: &ListenersConfigDocument, ) -> ks_core::Result<&ListenersProfileConfig> { - return listeners_profile(document, document.active_profile.as_str()); + return listeners_profile(document, document.default_profile.as_str()); } /// Converts one named listener profile into the resolved runtime listener contract. @@ -177,7 +177,7 @@ pub fn resolved_listener_config(profile: &ListenersProfileConfig) -> crate::List /// Validates a typed listener configuration document. pub fn validate_listeners_document(document: &ListenersConfigDocument) -> ks_core::Result<()> { - match crate::require_non_empty(&document.active_profile, "active_profile") { + match crate::require_non_empty(&document.default_profile, "default_profile") { std::result::Result::Ok(()) => (), std::result::Result::Err(error) => return std::result::Result::Err(error), } @@ -205,7 +205,7 @@ pub fn validate_listeners_document(document: &ListenersConfigDocument) -> ks_cor std::result::Result::Err(error) => return std::result::Result::Err(error), } } - return match active_listeners_profile(document) { + return match default_listeners_profile(document) { std::result::Result::Ok(_) => std::result::Result::Ok(()), std::result::Result::Err(error) => std::result::Result::Err(error), }; diff --git a/ks-config/src/settings.rs b/ks-config/src/settings.rs index 76b76db..e9eefae 100644 --- a/ks-config/src/settings.rs +++ b/ks-config/src/settings.rs @@ -1,5 +1,5 @@ // file: ks-config/src/settings.rs -// version: 21 +// version: 22 //! Resolved runtime configuration models retained during the `0.5.1` source-document split. @@ -31,8 +31,6 @@ pub struct ProfileConfig { pub app: AppSectionConfig, /// Database configuration. pub database: DatabaseConfig, - /// Local data directories. - pub data: DataConfig, /// Solana endpoint and listener configuration. pub solana: SolanaConfig, /// Wallet configuration. @@ -54,8 +52,6 @@ pub struct AppSectionConfig { pub name: std::string::String, /// Environment name. pub environment: std::string::String, - /// Default auto reconnect flag used when a transport does not override it. - pub auto_reconnect_default: bool, } /// Database backend configuration. @@ -112,16 +108,6 @@ pub struct SqliteConfig { pub use_wal: bool, } -/// Local data directory configuration. -#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize, TS)] -#[ts(export, export_to = "../frontend/ts/bindings/ks_config/settings/DataConfig.ts")] -pub struct DataConfig { - /// Wallet directory path. - pub wallets_directory: std::string::String, - /// Logs directory path. - pub logs_directory: std::string::String, -} - /// 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")] @@ -317,14 +303,6 @@ pub struct WalletConfig { pub temporary_wallet_alias: std::string::String, /// Persists the managed temporary wallet between process restarts. pub temporary_wallet_persist: bool, - /// Enables local validator transaction sending. - pub localnet_send_enabled: bool, - /// Enables devnet transaction sending. - pub devnet_send_enabled: bool, - /// Enables testnet transaction sending. - pub testnet_send_enabled: bool, - /// Enables mainnet transaction sending. - pub mainnet_send_enabled: bool, } /// Execution safety configuration. @@ -334,6 +312,14 @@ pub struct WalletConfig { export_to = "../frontend/ts/bindings/ks_config/settings/ExecutionConfig.ts" )] pub struct ExecutionConfig { + /// Enables local validator transaction sending. + pub localnet_send_enabled: bool, + /// Enables devnet transaction sending. + pub devnet_send_enabled: bool, + /// Enables testnet transaction sending. + pub testnet_send_enabled: bool, + /// Enables mainnet transaction sending. + pub mainnet_send_enabled: bool, /// Enables dry-run for newly prepared execution policies. pub dry_run_default: bool, /// Requires simulation before send. @@ -598,10 +584,6 @@ fn validate_profile(profile: &ProfileConfig) -> ks_core::Result<()> { std::result::Result::Ok(()) => (), std::result::Result::Err(error) => return std::result::Result::Err(error), } - match validate_data(&profile.data) { - std::result::Result::Ok(()) => (), - std::result::Result::Err(error) => return std::result::Result::Err(error), - } match validate_solana(&profile.solana) { std::result::Result::Ok(()) => (), std::result::Result::Err(error) => return std::result::Result::Err(error), @@ -625,7 +607,7 @@ fn validate_app_section(config: &AppSectionConfig) -> ks_core::Result<()> { return require_non_empty(&config.environment, "app.environment"); } -fn validate_database(config: &DatabaseConfig) -> ks_core::Result<()> { +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( "database_backend_invalid", @@ -651,14 +633,6 @@ fn validate_database(config: &DatabaseConfig) -> ks_core::Result<()> { return require_non_empty(&config.sqlite.path, "database.sqlite.path"); } -fn validate_data(config: &DataConfig) -> ks_core::Result<()> { - match require_non_empty(&config.wallets_directory, "data.wallets_directory") { - std::result::Result::Ok(()) => (), - std::result::Result::Err(error) => return std::result::Result::Err(error), - } - return require_non_empty(&config.logs_directory, "data.logs_directory"); -} - fn validate_solana(config: &SolanaConfig) -> ks_core::Result<()> { if config.http_endpoints.is_empty() { return std::result::Result::Err(ks_core::Error::new( @@ -889,7 +863,7 @@ fn validate_account_listener(config: &AccountListenerConfig) -> ks_core::Result< return require_non_empty(&config.account_pubkey, "account_listener.account_pubkey"); } -fn validate_wallet(config: &WalletConfig) -> ks_core::Result<()> { +pub(crate) fn validate_wallet(config: &WalletConfig) -> ks_core::Result<()> { match require_non_empty(&config.wallet_dir, "wallet.wallet_dir") { std::result::Result::Ok(()) => (), std::result::Result::Err(error) => return std::result::Result::Err(error), @@ -935,7 +909,7 @@ fn validate_wallet_alias(value: &str) -> ks_core::Result<()> { return std::result::Result::Ok(()); } -fn validate_execution(config: &ExecutionConfig) -> ks_core::Result<()> { +pub(crate) fn validate_execution(config: &ExecutionConfig) -> ks_core::Result<()> { let configured_spend = config .localnet_max_spend_lamports .saturating_add(config.devnet_max_spend_lamports) @@ -985,11 +959,15 @@ fn validate_wallet_execution_pair( execution: &ExecutionConfig, ) -> ks_core::Result<()> { for (enabled, limit, cluster) in [ - (wallet.localnet_send_enabled, execution.localnet_max_spend_lamports, "localnet"), - (wallet.devnet_send_enabled, execution.devnet_max_spend_lamports, "devnet"), - (wallet.testnet_send_enabled, execution.testnet_max_spend_lamports, "testnet"), ( - wallet.mainnet_send_enabled, + execution.localnet_send_enabled, + execution.localnet_max_spend_lamports, + "localnet", + ), + (execution.devnet_send_enabled, execution.devnet_max_spend_lamports, "devnet"), + (execution.testnet_send_enabled, execution.testnet_max_spend_lamports, "testnet"), + ( + execution.mainnet_send_enabled, execution.mainnet_max_spend_lamports, "mainnet-beta", ), @@ -1001,7 +979,7 @@ fn validate_wallet_execution_pair( )); } } - if wallet.mainnet_send_enabled && !execution.require_operator_confirmation { + if execution.mainnet_send_enabled && !execution.require_operator_confirmation { return std::result::Result::Err(ks_core::Error::new( "execution_mainnet_send_without_confirmation", "mainnet sending requires operator confirmation", diff --git a/ks-config/src/store.rs b/ks-config/src/store.rs new file mode 100644 index 0000000..38f77c4 --- /dev/null +++ b/ks-config/src/store.rs @@ -0,0 +1,186 @@ +// file: ks-config/src/store.rs +// version: 1 + +//! 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)] +pub struct StoreConfigDocument { + /// Default store profile used when a composition does not override it. + pub default_profile: std::string::String, + /// Named store profiles. + pub profiles: std::vec::Vec, +} + +/// Named store profile. +#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize)] +pub struct StoreProfileConfig { + /// Profile code. + pub name: std::string::String, + /// Database backend configuration. + pub database: crate::DatabaseConfig, +} + +/// Returns the embedded store JSON Schema text. +pub fn store_json_schema_text() -> &'static str { + return STORE_JSON_SCHEMA; +} + +/// Parses the embedded store JSON Schema into a JSON value. +pub fn store_json_schema_value() -> ks_core::Result { + let result = serde_json::from_str::(STORE_JSON_SCHEMA); + return match result { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(error) => std::result::Result::Err(ks_core::Error::new( + "store_config_schema_parse_failed", + error.to_string(), + )), + }; +} + +/// Validates raw store JSON against the embedded schema. +pub fn validate_store_json_schema(raw_json: &str) -> ks_core::Result<()> { + let schema = match store_json_schema_value() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let instance = match serde_json::from_str::(raw_json) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(ks_core::Error::new( + "store_config_json_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( + "store_config_schema_compile_failed", + error.to_string(), + )); + }, + }; + 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( + "store_config_schema_validation_failed", + error.to_string(), + )), + }; +} + +/// Parses and validates one store configuration document. +pub fn parse_store_json(raw_json: &str) -> ks_core::Result { + match validate_store_json_schema(raw_json) { + std::result::Result::Ok(()) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let document = match serde_json::from_str::(raw_json) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(ks_core::Error::new( + "store_config_json_decode_failed", + error.to_string(), + )); + }, + }; + return match validate_store_document(&document) { + std::result::Result::Ok(()) => std::result::Result::Ok(document), + std::result::Result::Err(error) => std::result::Result::Err(error), + }; +} + +/// Loads environment placeholders and parses one store configuration file. +pub fn read_store_json_file_with_environment( + path: &std::path::Path, + workspace_root: &std::path::Path, +) -> ks_core::Result { + match crate::load_workspace_environment(workspace_root) { + std::result::Result::Ok(_) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let raw_json = match std::fs::read_to_string(path) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(ks_core::Error::new( + "store_config_file_read_failed", + error.to_string(), + )); + }, + }; + return parse_store_json(&crate::resolve_environment_placeholders(&raw_json)); +} + +/// Returns one named store profile. +pub fn store_profile<'a>( + document: &'a StoreConfigDocument, + profile_name: &str, +) -> ks_core::Result<&'a StoreProfileConfig> { + for profile in &document.profiles { + if profile.name == profile_name { + return std::result::Result::Ok(profile); + } + } + return std::result::Result::Err(ks_core::Error::new( + "store_profile_not_found", + profile_name.to_string(), + )); +} + +/// Returns the default store profile. +pub fn default_store_profile( + document: &StoreConfigDocument, +) -> ks_core::Result<&StoreProfileConfig> { + return store_profile(document, document.default_profile.as_str()); +} + +/// Validates one typed store configuration document. +pub fn validate_store_document(document: &StoreConfigDocument) -> ks_core::Result<()> { + match crate::require_non_empty(&document.default_profile, "store.default_profile") { + std::result::Result::Ok(()) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + if document.profiles.is_empty() { + return std::result::Result::Err(ks_core::Error::new( + "store_config_profiles_empty", + "at least one store profile is required", + )); + } + let mut names = std::collections::BTreeSet::::new(); + for profile in &document.profiles { + match crate::require_non_empty(&profile.name, "store.profile.name") { + std::result::Result::Ok(()) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + if !names.insert(profile.name.clone()) { + return std::result::Result::Err(ks_core::Error::new( + "store_profile_name_duplicate", + profile.name.clone(), + )); + } + match crate::validate_database(&profile.database) { + std::result::Result::Ok(()) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + } + return match default_store_profile(document) { + std::result::Result::Ok(_) => std::result::Result::Ok(()), + std::result::Result::Err(error) => std::result::Result::Err(error), + }; +} + +#[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"); + + #[test] + fn default_and_example_store_configs_validate() { + assert!(super::parse_store_json(DEFAULT_STORE).is_ok()); + assert!(super::parse_store_json(EXAMPLE_STORE).is_ok()); + } +} diff --git a/ks-config/src/transport.rs b/ks-config/src/transport.rs index 613a722..9fd963c 100644 --- a/ks-config/src/transport.rs +++ b/ks-config/src/transport.rs @@ -1,28 +1,99 @@ // file: ks-config/src/transport.rs -// version: 1 +// version: 2 //! Independent Solana transport configuration document loading and validation. const TRANSPORT_JSON_SCHEMA: &str = include_str!("../../config/schemas/transport.config.schema.json"); -/// Root transport document containing independently selectable named profiles. +/// Root transport document containing shared endpoint defaults and named profiles. #[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize)] pub struct TransportConfigDocument { - /// Active transport profile used by consumers that do not provide an explicit selection. - pub active_profile: std::string::String, + /// Default transport profile used when a composition does not override it. + pub default_profile: std::string::String, + /// Named WebSocket endpoint defaults resolved before endpoint-specific overrides. + pub ws_endpoint_defaults: std::vec::Vec, /// Named transport profiles available in this document. - pub profiles: std::vec::Vec, + pub profiles: std::vec::Vec, } -/// Named Solana HTTP and WebSocket endpoint profile. +/// Default values shared by one class of WebSocket endpoints. #[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize)] +pub struct WsEndpointDefaultsConfig { + /// Defaults code referenced by endpoints. + pub name: std::string::String, + /// Connection timeout in milliseconds. + pub connect_timeout_ms: u64, + /// Request timeout in milliseconds. + pub request_timeout_ms: u64, + /// Unsubscribe timeout in milliseconds. + pub unsubscribe_timeout_ms: u64, + /// Writer channel capacity. + pub write_channel_capacity: u32, + /// Event channel capacity. + pub event_channel_capacity: u32, + /// Default automatic reconnect policy. + pub auto_reconnect: bool, +} + +/// Endpoint-specific overrides applied on top of named WebSocket defaults. +#[derive(Clone, Debug, Default, serde::Deserialize, Eq, PartialEq, serde::Serialize)] +pub struct WsEndpointOverridesConfig { + /// Optional connection timeout override. + pub connect_timeout_ms: std::option::Option, + /// Optional request timeout override. + pub request_timeout_ms: std::option::Option, + /// Optional unsubscribe timeout override. + pub unsubscribe_timeout_ms: std::option::Option, + /// Optional writer channel capacity override. + pub write_channel_capacity: std::option::Option, + /// Optional event channel capacity override. + pub event_channel_capacity: std::option::Option, + /// Optional automatic reconnect override. + pub auto_reconnect: std::option::Option, +} + +/// Source shape of one WebSocket endpoint before defaults are resolved. +#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize)] +pub struct WsEndpointSourceConfig { + /// Endpoint name. + pub name: std::string::String, + /// Named defaults to resolve before endpoint-specific overrides. + pub defaults: std::string::String, + /// Enables this endpoint. + pub enabled: bool, + /// Provider code. + pub provider: std::string::String, + /// Cluster code. + pub cluster: std::string::String, + /// Full endpoint URL. + pub url: std::string::String, + /// Optional endpoint-specific overrides. + #[serde(default)] + pub overrides: WsEndpointOverridesConfig, + /// Role-specific limits for this URL. + pub roles: std::vec::Vec, +} + +/// Named Solana HTTP and WebSocket endpoint source profile. +#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize)] +pub struct TransportProfileSourceConfig { + /// Profile code. + pub name: std::string::String, + /// HTTP JSON-RPC endpoints. + pub http_endpoints: std::vec::Vec, + /// Standard Solana WebSocket endpoints before shared defaults are resolved. + pub ws_endpoints: std::vec::Vec, +} + +/// Fully resolved transport profile consumed by transport clients. +#[derive(Clone, Debug, Eq, PartialEq)] pub struct TransportProfileConfig { /// Profile code. pub name: std::string::String, /// HTTP JSON-RPC endpoints. pub http_endpoints: std::vec::Vec, - /// Standard Solana WebSocket endpoints. + /// Standard Solana WebSocket endpoints with defaults and overrides resolved. pub ws_endpoints: std::vec::Vec, } @@ -33,9 +104,9 @@ pub fn transport_json_schema_text() -> &'static str { /// Parses the embedded transport JSON Schema into a JSON value. pub fn transport_json_schema_value() -> ks_core::Result { - let schema_result = serde_json::from_str::(TRANSPORT_JSON_SCHEMA); - return match schema_result { - std::result::Result::Ok(schema) => std::result::Result::Ok(schema), + let result = serde_json::from_str::(TRANSPORT_JSON_SCHEMA); + return match result { + std::result::Result::Ok(value) => std::result::Result::Ok(value), std::result::Result::Err(error) => std::result::Result::Err(ks_core::Error::new( "transport_config_schema_parse_failed", error.to_string(), @@ -46,11 +117,11 @@ pub fn transport_json_schema_value() -> ks_core::Result { /// Validates raw transport JSON against the embedded transport schema. pub fn validate_transport_json_schema(raw_json: &str) -> ks_core::Result<()> { let schema = match transport_json_schema_value() { - std::result::Result::Ok(schema) => schema, + std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; let instance = match serde_json::from_str::(raw_json) { - std::result::Result::Ok(instance) => instance, + std::result::Result::Ok(value) => value, std::result::Result::Err(error) => { return std::result::Result::Err(ks_core::Error::new( "transport_config_json_parse_failed", @@ -59,7 +130,7 @@ pub fn validate_transport_json_schema(raw_json: &str) -> ks_core::Result<()> { }, }; let validator = match jsonschema::validator_for(&schema) { - std::result::Result::Ok(validator) => validator, + std::result::Result::Ok(value) => value, std::result::Result::Err(error) => { return std::result::Result::Err(ks_core::Error::new( "transport_config_schema_compile_failed", @@ -83,7 +154,7 @@ pub fn parse_transport_json(raw_json: &str) -> ks_core::Result return std::result::Result::Err(error), } let document = match serde_json::from_str::(raw_json) { - std::result::Result::Ok(document) => document, + std::result::Result::Ok(value) => value, std::result::Result::Err(error) => { return std::result::Result::Err(ks_core::Error::new( "transport_config_json_decode_failed", @@ -97,23 +168,7 @@ pub fn parse_transport_json(raw_json: &str) -> ks_core::Result ks_core::Result { - let raw_json = match std::fs::read_to_string(path) { - std::result::Result::Ok(content) => content, - std::result::Result::Err(error) => { - return std::result::Result::Err(ks_core::Error::new( - "transport_config_file_read_failed", - error.to_string(), - )); - }, - }; - return parse_transport_json(&raw_json); -} - -/// Loads workspace environment values, resolves placeholders and parses transport configuration. +/// Loads environment placeholders and parses one transport configuration file. pub fn read_transport_json_file_with_environment( path: &std::path::Path, workspace_root: &std::path::Path, @@ -123,7 +178,7 @@ pub fn read_transport_json_file_with_environment( std::result::Result::Err(error) => return std::result::Result::Err(error), } let raw_json = match std::fs::read_to_string(path) { - std::result::Result::Ok(content) => content, + std::result::Result::Ok(value) => value, std::result::Result::Err(error) => { return std::result::Result::Err(ks_core::Error::new( "transport_config_file_read_failed", @@ -131,15 +186,97 @@ pub fn read_transport_json_file_with_environment( )); }, }; - let resolved = crate::resolve_environment_placeholders(&raw_json); - return parse_transport_json(&resolved); + return parse_transport_json(&crate::resolve_environment_placeholders(&raw_json)); } -/// Returns one named transport profile. -pub fn transport_profile<'a>( +/// Resolves one named transport profile including WebSocket defaults and overrides. +pub fn transport_profile( + document: &TransportConfigDocument, + profile_name: &str, +) -> ks_core::Result { + let source = match source_transport_profile(document, profile_name) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let mut ws_endpoints = std::vec::Vec::::new(); + for endpoint in &source.ws_endpoints { + let defaults = match ws_endpoint_defaults(document, endpoint.defaults.as_str()) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let resolved = crate::WsEndpointConfig { + name: endpoint.name.clone(), + enabled: endpoint.enabled, + provider: endpoint.provider.clone(), + cluster: endpoint.cluster.clone(), + url: endpoint.url.clone(), + connect_timeout_ms: if let std::option::Option::Some(value) = + endpoint.overrides.connect_timeout_ms + { + value + } else { + defaults.connect_timeout_ms + }, + request_timeout_ms: if let std::option::Option::Some(value) = + endpoint.overrides.request_timeout_ms + { + value + } else { + defaults.request_timeout_ms + }, + unsubscribe_timeout_ms: if let std::option::Option::Some(value) = + endpoint.overrides.unsubscribe_timeout_ms + { + value + } else { + defaults.unsubscribe_timeout_ms + }, + write_channel_capacity: if let std::option::Option::Some(value) = + endpoint.overrides.write_channel_capacity + { + value + } else { + defaults.write_channel_capacity + }, + event_channel_capacity: if let std::option::Option::Some(value) = + endpoint.overrides.event_channel_capacity + { + value + } else { + defaults.event_channel_capacity + }, + auto_reconnect: if let std::option::Option::Some(value) = + endpoint.overrides.auto_reconnect + { + value + } else { + defaults.auto_reconnect + }, + roles: endpoint.roles.clone(), + }; + match crate::validate_ws_endpoint(&resolved) { + std::result::Result::Ok(()) => ws_endpoints.push(resolved), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + } + return std::result::Result::Ok(TransportProfileConfig { + name: source.name.clone(), + http_endpoints: source.http_endpoints.clone(), + ws_endpoints, + }); +} + +/// Returns the default resolved transport profile. +pub fn default_transport_profile( + document: &TransportConfigDocument, +) -> ks_core::Result { + return transport_profile(document, document.default_profile.as_str()); +} + +fn source_transport_profile<'a>( document: &'a TransportConfigDocument, profile_name: &str, -) -> ks_core::Result<&'a TransportProfileConfig> { +) -> ks_core::Result<&'a TransportProfileSourceConfig> { for profile in &document.profiles { if profile.name == profile_name { return std::result::Result::Ok(profile); @@ -151,19 +288,57 @@ pub fn transport_profile<'a>( )); } -/// Returns the active transport profile declared by the document. -pub fn active_transport_profile( - document: &TransportConfigDocument, -) -> ks_core::Result<&TransportProfileConfig> { - return transport_profile(document, document.active_profile.as_str()); +fn ws_endpoint_defaults<'a>( + document: &'a TransportConfigDocument, + defaults_name: &str, +) -> ks_core::Result<&'a WsEndpointDefaultsConfig> { + for defaults in &document.ws_endpoint_defaults { + if defaults.name == defaults_name { + return std::result::Result::Ok(defaults); + } + } + return std::result::Result::Err(ks_core::Error::new( + "transport_ws_defaults_not_found", + defaults_name.to_string(), + )); } /// Validates a typed transport configuration document. pub fn validate_transport_document(document: &TransportConfigDocument) -> ks_core::Result<()> { - match crate::require_non_empty(&document.active_profile, "active_profile") { + match crate::require_non_empty(&document.default_profile, "transport.default_profile") { std::result::Result::Ok(()) => (), std::result::Result::Err(error) => return std::result::Result::Err(error), } + if document.ws_endpoint_defaults.is_empty() { + return std::result::Result::Err(ks_core::Error::new( + "transport_ws_defaults_empty", + "at least one WebSocket defaults profile is required", + )); + } + let mut defaults_names = std::collections::BTreeSet::::new(); + for defaults in &document.ws_endpoint_defaults { + match crate::require_non_empty(&defaults.name, "transport.ws_endpoint_defaults.name") { + std::result::Result::Ok(()) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + if !defaults_names.insert(defaults.name.clone()) { + return std::result::Result::Err(ks_core::Error::new( + "transport_ws_defaults_duplicate", + defaults.name.clone(), + )); + } + if defaults.connect_timeout_ms == 0 + || defaults.request_timeout_ms == 0 + || defaults.unsubscribe_timeout_ms == 0 + || defaults.write_channel_capacity == 0 + || defaults.event_channel_capacity == 0 + { + return std::result::Result::Err(ks_core::Error::new( + "transport_ws_defaults_limit_zero", + defaults.name.clone(), + )); + } + } if document.profiles.is_empty() { return std::result::Result::Err(ks_core::Error::new( "transport_config_profiles_empty", @@ -195,13 +370,21 @@ pub fn validate_transport_document(document: &TransportConfigDocument) -> ks_cor } } for endpoint in &profile.ws_endpoints { - match crate::validate_ws_endpoint(endpoint) { + match crate::require_non_empty(&endpoint.defaults, "transport.ws_endpoint.defaults") { std::result::Result::Ok(()) => (), std::result::Result::Err(error) => return std::result::Result::Err(error), } + match ws_endpoint_defaults(document, endpoint.defaults.as_str()) { + std::result::Result::Ok(_) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + } + match transport_profile(document, profile.name.as_str()) { + std::result::Result::Ok(_) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), } } - return match active_transport_profile(document) { + return match default_transport_profile(document) { std::result::Result::Ok(_) => std::result::Result::Ok(()), std::result::Result::Err(error) => std::result::Result::Err(error), }; @@ -219,22 +402,19 @@ mod tests { } #[test] - fn schema_rejects_unsupported_transport_surface() { - let value_result = serde_json::from_str::(DEFAULT_TRANSPORT); - let mut value = match value_result { + fn websocket_defaults_are_resolved_before_endpoint_overrides() { + let document = match super::parse_transport_json(DEFAULT_TRANSPORT) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("default transport must parse: {error}"), + }; + let profile = match super::transport_profile(&document, "mainnet_research") { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => { - panic!("default transport config must parse: {error}") + panic!("mainnet research transport must resolve: {error}") }, }; - value["profiles"][0]["advanced_streams"] = serde_json::json!([]); - let raw_result = serde_json::to_string(&value); - let raw = match raw_result { - std::result::Result::Ok(raw) => raw, - std::result::Result::Err(error) => { - panic!("transport test value must serialize: {error}") - }, - }; - assert!(super::validate_transport_json_schema(&raw).is_err()); + assert_eq!(profile.ws_endpoints[0].event_channel_capacity, 1024); + assert_eq!(profile.ws_endpoints[1].event_channel_capacity, 4096); + assert!(!profile.ws_endpoints[0].auto_reconnect); } } diff --git a/ks-config/src/wallet.rs b/ks-config/src/wallet.rs new file mode 100644 index 0000000..9887e16 --- /dev/null +++ b/ks-config/src/wallet.rs @@ -0,0 +1,216 @@ +// file: ks-config/src/wallet.rs +// version: 1 + +//! 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)] +pub struct WalletConfigDocument { + /// Root directory for wallet storage, independent from profile selection. + pub wallets_directory: std::string::String, + /// Default wallet profile used when a composition does not override it. + pub default_profile: std::string::String, + /// Named wallet profiles. + pub profiles: std::vec::Vec, +} + +/// Named wallet profile whose directory is relative to the global wallet root unless absolute. +#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize)] +pub struct WalletProfileConfig { + /// Profile code. + pub name: std::string::String, + /// Wallet subdirectory or absolute directory. + pub directory: std::string::String, + /// Selected cluster name. + pub cluster: std::string::String, + /// Enables the managed temporary wallet for this profile. + pub temporary_wallet_enabled: bool, + /// Alias used for the managed temporary wallet. + pub temporary_wallet_alias: std::string::String, + /// Persists the managed temporary wallet between process restarts. + pub temporary_wallet_persist: bool, +} + +/// Returns the embedded wallet JSON Schema text. +pub fn wallet_json_schema_text() -> &'static str { + return WALLET_JSON_SCHEMA; +} + +/// Parses and validates one wallet configuration document. +pub fn parse_wallet_json(raw_json: &str) -> ks_core::Result { + let schema = match serde_json::from_str::(WALLET_JSON_SCHEMA) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(ks_core::Error::new( + "wallet_config_schema_parse_failed", + error.to_string(), + )); + }, + }; + let instance = match serde_json::from_str::(raw_json) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(ks_core::Error::new( + "wallet_config_json_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( + "wallet_config_schema_compile_failed", + error.to_string(), + )); + }, + }; + if let std::result::Result::Err(error) = validator.validate(&instance) { + return std::result::Result::Err(ks_core::Error::new( + "wallet_config_schema_validation_failed", + error.to_string(), + )); + } + let document = match serde_json::from_str::(raw_json) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(ks_core::Error::new( + "wallet_config_json_decode_failed", + error.to_string(), + )); + }, + }; + return match validate_wallet_document(&document) { + std::result::Result::Ok(()) => std::result::Result::Ok(document), + std::result::Result::Err(error) => std::result::Result::Err(error), + }; +} + +/// Loads environment placeholders and parses one wallet configuration file. +pub fn read_wallet_json_file_with_environment( + path: &std::path::Path, + workspace_root: &std::path::Path, +) -> ks_core::Result { + match crate::load_workspace_environment(workspace_root) { + std::result::Result::Ok(_) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let raw_json = match std::fs::read_to_string(path) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(ks_core::Error::new( + "wallet_config_file_read_failed", + error.to_string(), + )); + }, + }; + return parse_wallet_json(&crate::resolve_environment_placeholders(&raw_json)); +} + +/// Returns one named wallet source profile. +pub fn wallet_profile<'a>( + document: &'a WalletConfigDocument, + profile_name: &str, +) -> ks_core::Result<&'a WalletProfileConfig> { + for profile in &document.profiles { + if profile.name == profile_name { + return std::result::Result::Ok(profile); + } + } + return std::result::Result::Err(ks_core::Error::new( + "wallet_profile_not_found", + profile_name.to_string(), + )); +} + +/// Returns the default wallet source profile. +pub fn default_wallet_profile( + document: &WalletConfigDocument, +) -> ks_core::Result<&WalletProfileConfig> { + return wallet_profile(document, document.default_profile.as_str()); +} + +/// Resolves one source wallet profile into the runtime wallet contract. +pub fn resolved_wallet_profile( + document: &WalletConfigDocument, + profile_name: &str, +) -> ks_core::Result { + let profile = match wallet_profile(document, profile_name) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let configured = std::path::PathBuf::from(profile.directory.as_str()); + let wallet_dir = if configured.is_absolute() { + configured + } else { + std::path::PathBuf::from(document.wallets_directory.as_str()).join(configured) + }; + let resolved = crate::WalletConfig { + wallet_dir: wallet_dir.display().to_string(), + cluster: profile.cluster.clone(), + temporary_wallet_enabled: profile.temporary_wallet_enabled, + temporary_wallet_alias: profile.temporary_wallet_alias.clone(), + temporary_wallet_persist: profile.temporary_wallet_persist, + }; + return match crate::validate_wallet(&resolved) { + std::result::Result::Ok(()) => std::result::Result::Ok(resolved), + std::result::Result::Err(error) => std::result::Result::Err(error), + }; +} + +/// Validates one typed wallet configuration document. +pub fn validate_wallet_document(document: &WalletConfigDocument) -> ks_core::Result<()> { + match crate::require_non_empty(&document.wallets_directory, "wallet.wallets_directory") { + std::result::Result::Ok(()) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + match crate::require_non_empty(&document.default_profile, "wallet.default_profile") { + std::result::Result::Ok(()) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + if document.profiles.is_empty() { + return std::result::Result::Err(ks_core::Error::new( + "wallet_config_profiles_empty", + "at least one wallet profile is required", + )); + } + let mut names = std::collections::BTreeSet::::new(); + for profile in &document.profiles { + match crate::require_non_empty(&profile.name, "wallet.profile.name") { + std::result::Result::Ok(()) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + match crate::require_non_empty(&profile.directory, "wallet.profile.directory") { + std::result::Result::Ok(()) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + if !names.insert(profile.name.clone()) { + return std::result::Result::Err(ks_core::Error::new( + "wallet_profile_name_duplicate", + profile.name.clone(), + )); + } + match resolved_wallet_profile(document, profile.name.as_str()) { + std::result::Result::Ok(_) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + } + return match default_wallet_profile(document) { + std::result::Result::Ok(_) => std::result::Result::Ok(()), + std::result::Result::Err(error) => std::result::Result::Err(error), + }; +} + +#[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"); + + #[test] + fn default_and_example_wallet_configs_validate() { + assert!(super::parse_wallet_json(DEFAULT_WALLET).is_ok()); + assert!(super::parse_wallet_json(EXAMPLE_WALLET).is_ok()); + } +} diff --git a/ks-config/tests/external_composition_api.rs b/ks-config/tests/external_composition_api.rs index d6f1be4..d3b34c7 100644 --- a/ks-config/tests/external_composition_api.rs +++ b/ks-config/tests/external_composition_api.rs @@ -1,5 +1,5 @@ // file: ks-config/tests/external_composition_api.rs -// version: 1 +// version: 2 //! External API coverage for binary composition and shared configuration documents. @@ -23,7 +23,31 @@ fn external_consumer_resolves_composition_through_crate_root_exports() { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => panic!("listeners must parse: {error}"), }; - let resolved = match ks_config::compose_app_config(&composition, &transport, &listeners) { + 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 execution = match ks_config::parse_execution_json(include_str!( + "../../config/example.execution.config.json" + )) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("execution must parse: {error}"), + }; + let resolved = match ks_config::compose_app_config( + &composition, + &transport, + &listeners, + &store, + &wallet, + &execution, + ) { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => panic!("composition must resolve: {error}"), }; diff --git a/ks-logging/CHANGELOG.md b/ks-logging/CHANGELOG.md index 4fc55ac..8883b9e 100644 --- a/ks-logging/CHANGELOG.md +++ b/ks-logging/CHANGELOG.md @@ -1,8 +1,15 @@ - + # CHANGELOG — ks-logging +## `0.5.1-pre.007` + +- place `logs_directory` hors profils dans le document logging et le rend configurable via `${KS_LOGS_DIRECTORY:-logs}` ; +- remplace le sélecteur autonome `active_profile` par `default_profile` et résout les chemins relatifs des targets fichier sous la racine globale ; +- corrige le test de résolution des profils logging afin qu’il itère par référence et ne déplace pas `document.profiles` avant de réutiliser le document complet. +- corrige le test de routage canonique afin qu’il fixe explicitement la valeur de repli `logs` de `KS_LOGS_DIRECTORY` avant de vérifier les chemins résolus, sans modifier le runtime. + ## `0.5.1-pre.006` - ajoute la sélection explicite d'un profil logging nommé afin qu'une composition binaire puisse choisir un profil indépendamment du `active_profile` propre au document logging ; diff --git a/ks-logging/README.md b/ks-logging/README.md index 0d8a1fa..188ec1b 100644 --- a/ks-logging/README.md +++ b/ks-logging/README.md @@ -1,5 +1,5 @@ - + # ks-logging @@ -7,39 +7,21 @@ ## Responsabilités -- charger `logging.config.json` et résoudre ses placeholders d’environnement ; +- charger `logging.config.json` et résoudre ses placeholders d'environnement ; - valider `config/schemas/logging.config.schema.json` ; -- sélectionner un profil logging indépendamment du profil applicatif ; -- posséder `LoggingConfig`, `LogTargetConfig` et `LogTargetFilterConfig` ; -- sélectionner les routes activées ; -- appliquer niveaux, targets exactes et préfixes wildcard ; -- créer les writers non bloquants et gérer les formats/rotations ; -- conserver les `WorkerGuard` pendant toute la durée du processus. +- posséder `logs_directory` comme valeur globale indépendante des profils ; +- sélectionner un `default_profile` ou un profil explicitement choisi par une composition ; +- résoudre les chemins relatifs des targets fichier sous `logs_directory` ; +- initialiser les routes `tracing` sans exposer de secret. -## Relations +`logs_directory` utilise actuellement `${KS_LOGS_DIRECTORY:-logs}`. Une installation peut donc déplacer la racine des logs par environnement ou `.env` sans recopier le chemin dans chaque profil logging. -`ks-logging` dépend de `ks-config` uniquement pour le chargement du fichier `.env` et la résolution générique `${KS_*}` / `${KB_*}`. `ks-config` ne dépend pas de `ks-logging` et ne duplique aucun type logging. +## Frontière -Le desktop lit le chemin `logging` et le `logging_profile` depuis `config/kb-app-demo-desktop.default.config.json`, charge le document logging puis transmet le profil explicitement sélectionné à `init_logging`. - -## API publique - -- `read_logging_json_file_with_environment` charge le document logging ; -- `parse_logging_json` et `validate_logging_json_schema` valident le format ; -- `active_logging_profile` sélectionne le profil autonome actif et `logging_profile` résout un profil explicitement nommé par une composition ; -- `LoggingConfigDocument` et `LoggingProfileConfig` décrivent le document ; -- `LoggingConfig`, `LogTargetConfig` et `LogTargetFilterConfig` décrivent le runtime ; -- `init_logging` initialise le subscriber global ; -- `LoggingGuard` maintient les writers ; -- `tracing_target` retourne le target canonique de la crate. - -## Statut - -Le split config/logging est fonctionnel. Les profils logging sont indépendants des profils applicatifs et les routes existantes restent conservées dans `config/logging.config.json`. +Les types logging restent possédés par `ks-logging`, pas par `ks-config`. Une composition de binaire ne recopie aucune route : elle référence le document logging partagé et peut remplacer seulement le profil sélectionné. ## Documents - [Utilisation](USAGE.md) -- [Travaux restants](TODO.md) - [Historique](CHANGELOG.md) -- [Configuration locale](../config/README.md) +- [Guide logging](../docs/guides/LOGGING.md) diff --git a/ks-logging/USAGE.md b/ks-logging/USAGE.md index 175acc8..4694a2b 100644 --- a/ks-logging/USAGE.md +++ b/ks-logging/USAGE.md @@ -1,5 +1,5 @@ - + # Utilisation de ks-logging @@ -13,114 +13,48 @@ let document = match ks_logging::read_logging_json_file_with_environment( Ok(value) => value, Err(error) => return Err(error), }; -let config = match ks_logging::active_logging_profile(&document) { - Ok(value) => value, - Err(error) => return Err(error), -}; -let guard = match ks_logging::init_logging(config) { +let config = match ks_logging::default_logging_profile(&document) { Ok(value) => value, Err(error) => return Err(error), }; ``` -Le desktop utilise normalement le chemin déclaré dans sa composition `config/kb-app-demo-desktop.default.config.json`. `KS_LOGGING_CONFIG_PATH` peut encore remplacer explicitement ce chemin sans modifier le fichier de composition. - -## Format du document +Le document possède : ```json { - "active_profile": "local_devnet", - "profiles": [ - { - "name": "local_devnet", - "default_level": "info", - "targets": [], - "target_filters": [] - } - ] + "logs_directory": "${KS_LOGS_DIRECTORY:-logs}", + "default_profile": "local_devnet", + "profiles": [] } ``` -Le schéma formel est `config/schemas/logging.config.schema.json`. `config/example.logging.config.json` fournit un exemple minimal conforme avec des routes valides. +`logs_directory` n'appartient à aucun profil. Les paths relatifs des targets fichier sont résolus sous cette racine. Un path absolu reste inchangé. -`targets` doit contenir au moins une route et au moins une route doit être activée. +## Override par composition -## Sélection indépendante - -Le `active_profile` logging reste le défaut autonome du document. Lorsqu’un binaire utilise une composition, il sélectionne explicitement son `logging_profile` avec `ks_logging::logging_profile`; cette sélection prime pour ce binaire sans modifier le document partagé. - -## Construction directe - -Les tests ou consommateurs spécialisés peuvent toujours construire directement `LoggingConfig` puis appeler `init_logging` : +Une application peut sélectionner un autre profil avec : ```rust -let config = ks_logging::LoggingConfig { - default_level: "info".to_string(), - targets: vec![ks_logging::LogTargetConfig { - name: "console".to_string(), - enabled: true, - sink: "console".to_string(), - level: "info".to_string(), - path: String::new(), - rotation: "none".to_string(), - format: "compact".to_string(), - ansi: true, - targets: vec!["ks-*".to_string()], - }], - target_filters: Vec::new(), -}; -let guard = match ks_logging::init_logging(&config) { +let config = match ks_logging::logging_profile(&document, "mainnet_research") { Ok(value) => value, Err(error) => return Err(error), }; ``` -## Route fichier JSON +Le `default_profile` reste le fallback autonome du document ; la sélection explicite d'une composition prime uniquement pour le consommateur concerné. + +Le chemin du document peut être remplacé opérationnellement par `KS_LOGGING_CONFIG_PATH`. La racine des fichiers peut être remplacée indépendamment par `KS_LOGS_DIRECTORY`. + +## Initialisation + +Une fois le profil résolu : ```rust -let file_route = ks_logging::LogTargetConfig { - name: "operations-json".to_string(), - enabled: true, - sink: "file".to_string(), - level: "debug".to_string(), - path: "logs/operations.jsonl".to_string(), - rotation: "daily".to_string(), - format: "json".to_string(), - ansi: false, - targets: vec!["ks-pipeline*".to_string()], -}; -``` - -Les rotations supportées sont `none`, `never`, `daily` et `hourly`. Les formats supportés sont `human`, `compact`, `pretty` et `json`. - -## Inspection des routes - -```rust -for route_name in guard.route_names() { - println!("active logging route: {route_name}"); +match ks_logging::init_logging(&config) { + Ok(()) => (), + Err(error) => return Err(error), } - -assert_eq!(guard.route_count(), guard.route_names().len()); ``` -## Erreurs - -Les erreurs utilisent `ks_core::Error`. Les cas principaux sont : - -- document ou schéma invalide ; -- profil actif absent ou dupliqué ; -- aucune route activée ; -- sink, format, rotation ou niveau inconnu ; -- chemin fichier invalide ; -- échec d’initialisation du subscriber global. - -## Tests instructifs - -- les tests de `document` vérifient les deux fichiers logging versionnés et les profils indépendants ; -- `default_logging_config_routes_global_and_operational_crate_files` vérifie les routes canoniques ; -- les tests de `tracing_runtime` vérifient l’installation, les filtres et le volume de routes. - -## Limites - -- initialisation globale unique par processus ; -- la politique de redaction des valeurs secrètes avant logging est renforcée dans `0.5.1-pre.006`. +L'initialisation reste globale au processus. Les binaires doivent donc résoudre leur configuration avant le premier appel à `init_logging`. diff --git a/ks-logging/src/document.rs b/ks-logging/src/document.rs index 9428e60..cec6970 100644 --- a/ks-logging/src/document.rs +++ b/ks-logging/src/document.rs @@ -1,5 +1,5 @@ // file: ks-logging/src/document.rs -// version: 2 +// version: 5 //! Independent logging configuration document loading and validation. @@ -8,8 +8,10 @@ const LOGGING_JSON_SCHEMA: &str = include_str!("../../config/schemas/logging.con /// Root logging document containing independently selectable named profiles. #[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize)] pub struct LoggingConfigDocument { - /// Active logging profile name. - pub active_profile: std::string::String, + /// Root directory for file logs, independent from profile selection. + pub logs_directory: std::string::String, + /// Default logging profile used when a composition does not override it. + pub default_profile: std::string::String, /// Named logging profiles available in this document. pub profiles: std::vec::Vec, } @@ -132,14 +134,44 @@ pub fn read_logging_json_file_with_environment( return parse_logging_json(&resolved); } -/// Returns one named runtime logging profile. -pub fn logging_profile<'a>( +/// Returns one named runtime logging profile with file paths resolved below the global log root. +pub fn logging_profile( + document: &LoggingConfigDocument, + profile_name: &str, +) -> ks_core::Result { + let source = match source_logging_profile(document, profile_name) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let mut config = source.config.clone(); + let root = std::path::PathBuf::from(document.logs_directory.as_str()); + for target in &mut config.targets { + if target.sink != "file" || target.path.trim().is_empty() { + continue; + } + let configured = std::path::PathBuf::from(target.path.as_str()); + if configured.is_absolute() { + continue; + } + target.path = root.join(configured).display().to_string(); + } + return std::result::Result::Ok(config); +} + +/// Returns the default runtime logging profile selected by the logging document. +pub fn default_logging_profile( + document: &LoggingConfigDocument, +) -> ks_core::Result { + return logging_profile(document, document.default_profile.as_str()); +} + +fn source_logging_profile<'a>( document: &'a LoggingConfigDocument, profile_name: &str, -) -> ks_core::Result<&'a crate::LoggingConfig> { +) -> ks_core::Result<&'a LoggingProfileConfig> { for profile in &document.profiles { if profile.name == profile_name { - return std::result::Result::Ok(&profile.config); + return std::result::Result::Ok(profile); } } return std::result::Result::Err(ks_core::Error::new( @@ -148,16 +180,13 @@ pub fn logging_profile<'a>( )); } -/// Returns the active runtime logging profile selected by the logging document. -pub fn active_logging_profile( - document: &LoggingConfigDocument, -) -> ks_core::Result<&crate::LoggingConfig> { - return logging_profile(document, document.active_profile.as_str()); -} - /// Validates a typed logging configuration document. pub fn validate_logging_document(document: &LoggingConfigDocument) -> ks_core::Result<()> { - match require_non_empty(&document.active_profile, "active_profile") { + match require_non_empty(&document.logs_directory, "logging.logs_directory") { + std::result::Result::Ok(()) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + match require_non_empty(&document.default_profile, "logging.default_profile") { std::result::Result::Ok(()) => (), std::result::Result::Err(error) => return std::result::Result::Err(error), } @@ -168,7 +197,6 @@ pub fn validate_logging_document(document: &LoggingConfigDocument) -> ks_core::R )); } let mut names = std::collections::BTreeSet::::new(); - let mut active_count = 0_u32; for profile in &document.profiles { match require_non_empty(&profile.name, "logging.profile.name") { std::result::Result::Ok(()) => (), @@ -180,24 +208,15 @@ pub fn validate_logging_document(document: &LoggingConfigDocument) -> ks_core::R profile.name.clone(), )); } - if profile.name == document.active_profile { - active_count += 1; - } match validate_logging_config(&profile.config) { std::result::Result::Ok(()) => (), std::result::Result::Err(error) => return std::result::Result::Err(error), } } - if active_count != 1 { - return std::result::Result::Err(ks_core::Error::new( - "active_logging_profile_count_invalid", - format!( - "active logging profile '{}' must match exactly one profile", - document.active_profile - ), - )); - } - return std::result::Result::Ok(()); + return match default_logging_profile(document) { + std::result::Result::Ok(_) => std::result::Result::Ok(()), + std::result::Result::Err(error) => std::result::Result::Err(error), + }; } fn validate_logging_config(config: &crate::LoggingConfig) -> ks_core::Result<()> { @@ -416,28 +435,35 @@ mod tests { } #[test] - fn logging_profiles_are_independent_and_active_profile_resolves() { + fn logging_profiles_are_independent_and_default_profile_resolves() { let document_result = super::parse_logging_json(DEFAULT_LOGGING_CONFIG); let document = match document_result { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => panic!("default logging config must parse: {error}"), }; - assert_eq!(document.active_profile, "mainnet_research"); + assert_eq!(document.default_profile, "local_devnet"); assert_eq!(document.profiles.len(), 3); - assert!(super::active_logging_profile(&document).is_ok()); + assert!(super::default_logging_profile(&document).is_ok()); assert!(super::logging_profile(&document, "local_devnet").is_ok()); } #[test] fn default_logging_config_routes_global_and_operational_crate_files() { - let document_result = super::parse_logging_json(DEFAULT_LOGGING_CONFIG); + let mut value = parse_default_value(); + value["logs_directory"] = serde_json::Value::String("logs".to_string()); + let raw_json = value_to_json(&value); + let document_result = super::parse_logging_json(&raw_json); let document = match document_result { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => panic!("default logging config must parse: {error}"), }; let operational_crates = tracing_crate_names(); assert!(!operational_crates.is_empty()); - for profile in document.profiles { + for profile in &document.profiles { + let resolved = match super::logging_profile(&document, profile.name.as_str()) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("logging profile must resolve: {error}"), + }; let directory = match profile.name.as_str() { "local_devnet" => "devnet", "mainnet_research" => "mainnet_research", @@ -450,7 +476,7 @@ mod tests { ("error.jsonl", "error", "json"), ] { let expected_path = format!("logs/{directory}/{suffix}"); - assert!(profile.config.targets.iter().any(|target| { + assert!(resolved.targets.iter().any(|target| { return target.enabled && target.path == expected_path && target.level == level @@ -458,7 +484,7 @@ mod tests { && target.targets == std::vec!["*".to_string()]; })); } - assert!(profile.config.targets.iter().any(|target| { + assert!(resolved.targets.iter().any(|target| { return target.enabled && target.path == format!("logs/{directory}/app.log") && target.level == "debug" @@ -477,7 +503,7 @@ mod tests { crate_name.as_str() }; let expected_path = format!("logs/{directory}/{route_directory}/{suffix}"); - let route_exists = profile.config.targets.iter().any(|target| { + let route_exists = resolved.targets.iter().any(|target| { return target.enabled && target.path == expected_path && target.level == level @@ -500,16 +526,19 @@ mod tests { assert!(!DEFAULT_LOGGING_CONFIG.contains("\"ks_wallet\"")); assert!(DEFAULT_LOGGING_CONFIG.contains("\"ks-wallet\"")); assert!(DEFAULT_LOGGING_CONFIG.contains("\"ks-pipeline\"")); - assert!(DEFAULT_LOGGING_CONFIG.contains("logs/devnet/ks-pipeline/debug.log")); - assert!(DEFAULT_LOGGING_CONFIG.contains("logs/mainnet_research/ks-pipeline/info.log")); - assert!(DEFAULT_LOGGING_CONFIG.contains("logs/mainnet/ks-pipeline/error.jsonl")); + assert!( + DEFAULT_LOGGING_CONFIG.contains("\"logs_directory\": \"${KS_LOGS_DIRECTORY:-logs}\"") + ); + assert!(DEFAULT_LOGGING_CONFIG.contains("devnet/ks-pipeline/debug.log")); + assert!(DEFAULT_LOGGING_CONFIG.contains("mainnet_research/ks-pipeline/info.log")); + assert!(DEFAULT_LOGGING_CONFIG.contains("mainnet/ks-pipeline/error.jsonl")); assert!(!DEFAULT_LOGGING_CONFIG.contains("ks_pipeline")); } #[test] - fn logging_parser_rejects_missing_active_profile() { + fn logging_parser_rejects_missing_default_profile() { let mut value = parse_default_value(); - value["active_profile"] = serde_json::Value::String("missing_profile".to_string()); + value["default_profile"] = serde_json::Value::String("missing_profile".to_string()); let result = super::parse_logging_json(&value_to_json(&value)); assert!(result.is_err()); } diff --git a/ks-logging/src/lib.rs b/ks-logging/src/lib.rs index bb48574..a9a9709 100644 --- a/ks-logging/src/lib.rs +++ b/ks-logging/src/lib.rs @@ -1,5 +1,5 @@ // file: ks-logging/src/lib.rs -// version: 9 +// version: 10 //! Logging and tracing initialization for applications and workers. #![warn(missing_docs)] @@ -26,8 +26,8 @@ pub use self::config::LoggingConfig; pub use self::document::LoggingConfigDocument; /// Exposes one named logging profile. pub use self::document::LoggingProfileConfig; -/// Exposes active logging profile resolution. -pub use self::document::active_logging_profile; +/// Exposes default logging profile resolution. +pub use self::document::default_logging_profile; /// Exposes the embedded logging JSON Schema text. pub use self::document::logging_json_schema_text; /// Exposes the embedded logging JSON Schema parser. diff --git a/ks-onchain-transport/CHANGELOG.md b/ks-onchain-transport/CHANGELOG.md index fc1926f..021753f 100644 --- a/ks-onchain-transport/CHANGELOG.md +++ b/ks-onchain-transport/CHANGELOG.md @@ -1,8 +1,12 @@ - + # CHANGELOG — ks-onchain-transport +## `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é. + ## `0.5.1-pre.006` - permet de construire directement les pools HTTP et WebSocket depuis `ks_config::TransportProfileConfig` ; diff --git a/ks-onchain-transport/src/execution_rpc.rs b/ks-onchain-transport/src/execution_rpc.rs index f9ba253..1f7984b 100644 --- a/ks-onchain-transport/src/execution_rpc.rs +++ b/ks-onchain-transport/src/execution_rpc.rs @@ -1,5 +1,5 @@ // file: ks-onchain-transport/src/execution_rpc.rs -// version: 11 +// version: 12 //! Typed Solana JSON-RPC adapters used by execution orchestration. @@ -2403,6 +2403,10 @@ mod tests { dry_run_default: true, require_simulation: true, require_operator_confirmation: true, + localnet_send_enabled: false, + devnet_send_enabled: true, + testnet_send_enabled: false, + mainnet_send_enabled: false, localnet_max_spend_lamports: 1_000_000, devnet_max_spend_lamports: 1_000_000, testnet_max_spend_lamports: 0, diff --git a/ks-pipeline-demo-scenarios/CHANGELOG.md b/ks-pipeline-demo-scenarios/CHANGELOG.md index b14035c..325efaf 100644 --- a/ks-pipeline-demo-scenarios/CHANGELOG.md +++ b/ks-pipeline-demo-scenarios/CHANGELOG.md @@ -1,8 +1,14 @@ - + # CHANGELOG — ks-pipeline-demo-scenarios +## `0.5.1-pre.007` + +- supprime la composition par défaut dédiée aux scénarios et charge directement les defaults partagés de `ks-config` ; +- conserve `KS_DEVNET_CONFIG_PATH` comme override facultatif vers une composition explicite ; +- lit désormais les autorisations de soumission Devnet depuis `ExecutionConfig` plutôt que depuis le contrat wallet. + ## `0.5.1-pre.006` - remplace la dépendance runtime au document applicatif monolithique par `config/ks-pipeline-demo-scenarios.default.config.json` pour les scénarios Devnet opt-in ; diff --git a/ks-pipeline-demo-scenarios/USAGE.md b/ks-pipeline-demo-scenarios/USAGE.md index 76e2f66..bb8516d 100644 --- a/ks-pipeline-demo-scenarios/USAGE.md +++ b/ks-pipeline-demo-scenarios/USAGE.md @@ -1,5 +1,5 @@ - + # Utilisation de ks-pipeline-demo-scenarios @@ -113,7 +113,7 @@ KS_SECRET_POSTGRES_TEST_URL='postgresql://…' \ cargo test -p ks-pipeline-demo-scenarios optional_devnet_token_2022_metadata_campaign_from_env -- --nocapture ``` -`KS_DEVNET_PROFILE` peut sélectionner explicitement le profil Devnet et `KS_DEVNET_CONFIG_PATH` peut remplacer la composition `config/ks-pipeline-demo-scenarios.default.config.json`. La composition résout ensuite les profils transport/listeners et le profil runtime doit utiliser un wallet persistant, autoriser les soumissions Devnet et respecter les plafonds de dépense et de frais. +Par défaut, les scénarios utilisent directement les `default_profile` des documents partagés via `ks_config::read_default_shared_app_config_with_environment`. `KS_DEVNET_PROFILE` peut sélectionner explicitement un profil Devnet compatible. `KS_DEVNET_CONFIG_PATH` reste un override facultatif vers une composition explicite ; aucun fichier de composition propre aux scénarios n’est requis. Le profil runtime doit utiliser un wallet persistant, autoriser les soumissions Devnet via la politique execution et respecter les plafonds de dépense et de frais. La sortie `TOKEN_2022_METADATA_FIXTURE` conserve le mint et la signature de préparation. Chaque ligne `TOKEN_2022_METADATA_STEP` conserve l’opération, la signature, le slot, la postcondition, le nombre de matérialisations et, pour `Emit`, la taille du `returnData`. La campagne de référence de `pre.012` a été exécutée avec succès et ses preuves sont conservées dans `SPL_TOKEN_2022_METADATA_DEVNET_VALIDATION_MATRIX.json` et dans le rapport `docs/validation/V0_4_8_METADATA_VALIDATION_REPORT.md` ; le rapport détaillé `pre.012` est archivé sous `olddocs/archivekbot3/docs/validation/`. Une nouvelle exécution produit de nouvelles transactions et ne remplace ces preuves qu’après validation explicite. diff --git a/ks-pipeline-demo-scenarios/src/environment.rs b/ks-pipeline-demo-scenarios/src/environment.rs index ca8727f..c5a9e26 100644 --- a/ks-pipeline-demo-scenarios/src/environment.rs +++ b/ks-pipeline-demo-scenarios/src/environment.rs @@ -1,5 +1,5 @@ // file: ks-pipeline-demo-scenarios/src/environment.rs -// version: 6 +// version: 7 //! Environment initialization for opt-in demonstration scenarios. @@ -10,6 +10,36 @@ pub fn initialize_demo_scenario_environment( return ks_config::load_workspace_environment(workspace_root); } +/// Loads the shared default Solana configuration or one explicit scenario composition override. +/// +/// Shared documents are the canonical default when `KS_DEVNET_CONFIG_PATH` is absent. +/// An explicit path may select a dedicated composition without making one mandatory for scenarios. +pub fn load_demo_scenario_config( + workspace_root: &std::path::Path, +) -> ks_core::Result { + match initialize_demo_scenario_environment(workspace_root) { + std::result::Result::Ok(_) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let configured_path = std::env::var("KS_DEVNET_CONFIG_PATH") + .ok() + .map(|value| return value.trim().to_string()) + .filter(|value| return !value.is_empty()); + if let std::option::Option::Some(value) = configured_path { + let path = std::path::PathBuf::from(value); + let resolved_path = if path.is_absolute() { path } else { workspace_root.join(path) }; + let composed = match ks_config::read_composed_app_config_with_environment( + resolved_path.as_path(), + workspace_root, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return std::result::Result::Ok(composed.app_config); + } + return ks_config::read_default_shared_app_config_with_environment(workspace_root); +} + /// Resolves one compatible Devnet profile for demo scenarios. /// /// An explicit name has priority, followed by `KS_DEVNET_PROFILE`, then the diff --git a/ks-pipeline-demo-scenarios/src/lib.rs b/ks-pipeline-demo-scenarios/src/lib.rs index b651a16..8504a25 100644 --- a/ks-pipeline-demo-scenarios/src/lib.rs +++ b/ks-pipeline-demo-scenarios/src/lib.rs @@ -1,5 +1,5 @@ // file: ks-pipeline-demo-scenarios/src/lib.rs -// version: 28 +// version: 29 #![forbid(unsafe_code)] #![deny(unreachable_pub)] @@ -17,6 +17,8 @@ mod spl; pub use self::environment::DevnetProfileStoreReadiness; /// Loads the workspace environment for opt-in demo and Devnet scenarios. pub use self::environment::initialize_demo_scenario_environment; +/// Loads the shared default configuration or an explicit scenario composition override. +pub use self::environment::load_demo_scenario_config; /// Verifies or initializes the PostgreSQL schema selected by one Devnet profile. pub use self::environment::prepare_demo_devnet_profile_store; /// Resolves one configured Devnet profile for UI, CLI and test scenarios. diff --git a/ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/collection_verify_campaign.rs b/ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/collection_verify_campaign.rs index 178477b..dedd2d0 100644 --- a/ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/collection_verify_campaign.rs +++ b/ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/collection_verify_campaign.rs @@ -1,5 +1,5 @@ // file: ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/collection_verify_campaign.rs -// version: 5 +// version: 6 //! Confirmed Devnet collection parent/member `Verify -> Unverify` campaign. @@ -643,21 +643,8 @@ mod tests { std::option::Option::Some(value) => value, std::option::Option::None => panic!("workspace root cannot be resolved"), }; - let config_path = match std::env::var("KS_DEVNET_CONFIG_PATH") { - std::result::Result::Ok(value) => { - let path = std::path::PathBuf::from(value); - if path.is_absolute() { path } else { workspace_root.join(path) } - }, - std::result::Result::Err(_) => { - workspace_root.join("config/ks-pipeline-demo-scenarios.default.config.json") - }, - }; - let composed = ks_config::read_composed_app_config_with_environment( - config_path.as_path(), - workspace_root, - ) - .unwrap_or_else(|error| panic!("Devnet configuration loading failed: {error}")); - let config = composed.app_config; + let config = crate::load_demo_scenario_config(workspace_root) + .unwrap_or_else(|error| panic!("Devnet configuration loading failed: {error}")); let profile_name = std::env::var("KS_DEVNET_PROFILE").ok(); let mut profile = crate::resolve_demo_devnet_profile(&config, profile_name.as_deref()) .unwrap_or_else(|error| panic!("Devnet profile resolution failed: {error}")); diff --git a/ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/create_mint_campaign.rs b/ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/create_mint_campaign.rs index 519de6c..c72998f 100644 --- a/ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/create_mint_campaign.rs +++ b/ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/create_mint_campaign.rs @@ -1,5 +1,5 @@ // file: ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/create_mint_campaign.rs -// version: 13 +// version: 14 //! Confirmed Devnet `Create -> Mint` campaign for one Metaplex asset family. @@ -59,7 +59,7 @@ where + Sync, O: crate::SolanaExecutionObserver, { - if !profile.wallet.devnet_send_enabled { + if !profile.execution.devnet_send_enabled { return std::result::Result::Err(ks_core::Error::config( "Metaplex Create -> Mint Devnet campaign requires devnet_send_enabled=true", )); @@ -808,21 +808,8 @@ mod tests { std::option::Option::Some(value) => value, std::option::Option::None => panic!("workspace root cannot be resolved"), }; - let config_path = match std::env::var("KS_DEVNET_CONFIG_PATH") { - std::result::Result::Ok(value) => { - let path = std::path::PathBuf::from(value); - if path.is_absolute() { path } else { workspace_root.join(path) } - }, - std::result::Result::Err(_) => { - workspace_root.join("config/ks-pipeline-demo-scenarios.default.config.json") - }, - }; - let composed = ks_config::read_composed_app_config_with_environment( - config_path.as_path(), - workspace_root, - ) - .unwrap_or_else(|error| panic!("Devnet configuration loading failed: {error}")); - let config = composed.app_config; + let config = crate::load_demo_scenario_config(workspace_root) + .unwrap_or_else(|error| panic!("Devnet configuration loading failed: {error}")); let profile_name = std::env::var("KS_DEVNET_PROFILE").ok(); let mut profile = crate::resolve_demo_devnet_profile(&config, profile_name.as_deref()) .unwrap_or_else(|error| panic!("Devnet profile resolution failed: {error}")); diff --git a/ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/devnet_execution.rs b/ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/devnet_execution.rs index 328f99b..09f0810 100644 --- a/ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/devnet_execution.rs +++ b/ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/devnet_execution.rs @@ -1,5 +1,5 @@ // file: ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/devnet_execution.rs -// version: 21 +// version: 22 //! Real Devnet simulation and submission for current Metaplex Token Metadata operations. @@ -1185,7 +1185,7 @@ fn validate_profile( "Metaplex Devnet orchestration requires simulation", )); } - if request.submit && !profile.wallet.devnet_send_enabled { + if request.submit && !profile.execution.devnet_send_enabled { return std::result::Result::Err(ks_core::Error::config( "Devnet transaction submission is disabled by the wallet profile", )); diff --git a/ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/escrow_campaign.rs b/ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/escrow_campaign.rs index 54a1831..70e64d8 100644 --- a/ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/escrow_campaign.rs +++ b/ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/escrow_campaign.rs @@ -1,5 +1,5 @@ // file: ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/escrow_campaign.rs -// version: 5 +// version: 6 //! Confirmed Devnet Token Owned Escrow campaign for current Metaplex escrow operations. @@ -71,7 +71,7 @@ where + Sync, O: crate::SolanaExecutionObserver, { - if !profile.wallet.devnet_send_enabled { + if !profile.execution.devnet_send_enabled { return std::result::Result::Err(ks_core::Error::config( "Metaplex escrow Devnet campaign requires devnet_send_enabled=true", )); @@ -968,21 +968,8 @@ mod tests { std::option::Option::Some(value) => value, std::option::Option::None => panic!("workspace root cannot be resolved"), }; - let config_path = match std::env::var("KS_DEVNET_CONFIG_PATH") { - std::result::Result::Ok(value) => { - let path = std::path::PathBuf::from(value); - if path.is_absolute() { path } else { workspace_root.join(path) } - }, - std::result::Result::Err(_) => { - workspace_root.join("config/ks-pipeline-demo-scenarios.default.config.json") - }, - }; - let composed = ks_config::read_composed_app_config_with_environment( - config_path.as_path(), - workspace_root, - ) - .unwrap_or_else(|error| panic!("Devnet configuration loading failed: {error}")); - let config = composed.app_config; + let config = crate::load_demo_scenario_config(workspace_root) + .unwrap_or_else(|error| panic!("Devnet configuration loading failed: {error}")); let profile_name = std::env::var("KS_DEVNET_PROFILE").ok(); let mut profile = crate::resolve_demo_devnet_profile(&config, profile_name.as_deref()) .unwrap_or_else(|error| panic!("Devnet profile resolution failed: {error}")); diff --git a/ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/fixture.rs b/ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/fixture.rs index 5ddf91a..9f221b6 100644 --- a/ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/fixture.rs +++ b/ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/fixture.rs @@ -1,5 +1,5 @@ // file: ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/fixture.rs -// version: 20 +// version: 21 //! Native Metaplex Token Metadata fixture preparation for Devnet demos. @@ -120,7 +120,7 @@ pub async fn prepare_metaplex_create_fixture( "Metaplex fixture endpoint roles must not be empty", )); } - if !profile.wallet.devnet_send_enabled { + if !profile.execution.devnet_send_enabled { return std::result::Result::Err(ks_core::Error::config( "Metaplex fixture preparation requires devnet_send_enabled=true", )); diff --git a/ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/maintenance_campaign.rs b/ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/maintenance_campaign.rs index 9590501..cfd2a66 100644 --- a/ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/maintenance_campaign.rs +++ b/ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/maintenance_campaign.rs @@ -1,5 +1,5 @@ // file: ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/maintenance_campaign.rs -// version: 7 +// version: 8 //! Final bounded Devnet qualification campaign for current Metaplex maintenance operations. @@ -572,21 +572,8 @@ mod tests { std::option::Option::Some(value) => value, std::option::Option::None => panic!("workspace root cannot be resolved"), }; - let config_path = match std::env::var("KS_DEVNET_CONFIG_PATH") { - std::result::Result::Ok(value) => { - let path = std::path::PathBuf::from(value); - if path.is_absolute() { path } else { workspace_root.join(path) } - }, - std::result::Result::Err(_) => { - workspace_root.join("config/ks-pipeline-demo-scenarios.default.config.json") - }, - }; - let composed = ks_config::read_composed_app_config_with_environment( - config_path.as_path(), - workspace_root, - ) - .unwrap_or_else(|error| panic!("Devnet configuration loading failed: {error}")); - let config = composed.app_config; + let config = crate::load_demo_scenario_config(workspace_root) + .unwrap_or_else(|error| panic!("Devnet configuration loading failed: {error}")); let profile_name = std::env::var("KS_DEVNET_PROFILE").ok(); let mut profile = crate::resolve_demo_devnet_profile(&config, profile_name.as_deref()) .unwrap_or_else(|error| panic!("Devnet profile resolution failed: {error}")); diff --git a/ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/pnft_lifecycle_campaign.rs b/ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/pnft_lifecycle_campaign.rs index c0cc234..46908de 100644 --- a/ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/pnft_lifecycle_campaign.rs +++ b/ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/pnft_lifecycle_campaign.rs @@ -1,5 +1,5 @@ // file: ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/pnft_lifecycle_campaign.rs -// version: 7 +// version: 8 //! Confirmed Devnet pNFT delegate, lock, unlock, revoke and transfer campaign. @@ -1066,21 +1066,8 @@ mod tests { std::option::Option::Some(value) => value, std::option::Option::None => panic!("workspace root cannot be resolved"), }; - let config_path = match std::env::var("KS_DEVNET_CONFIG_PATH") { - std::result::Result::Ok(value) => { - let path = std::path::PathBuf::from(value); - if path.is_absolute() { path } else { workspace_root.join(path) } - }, - std::result::Result::Err(_) => { - workspace_root.join("config/ks-pipeline-demo-scenarios.default.config.json") - }, - }; - let composed = ks_config::read_composed_app_config_with_environment( - config_path.as_path(), - workspace_root, - ) - .unwrap_or_else(|error| panic!("Devnet configuration loading failed: {error}")); - let config = composed.app_config; + let config = crate::load_demo_scenario_config(workspace_root) + .unwrap_or_else(|error| panic!("Devnet configuration loading failed: {error}")); let profile_name = std::env::var("KS_DEVNET_PROFILE").ok(); let mut profile = crate::resolve_demo_devnet_profile(&config, profile_name.as_deref()) .unwrap_or_else(|error| panic!("Devnet profile resolution failed: {error}")); diff --git a/ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/print_burn_campaign.rs b/ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/print_burn_campaign.rs index 6169b44..491cf22 100644 --- a/ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/print_burn_campaign.rs +++ b/ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/print_burn_campaign.rs @@ -1,5 +1,5 @@ // file: ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/print_burn_campaign.rs -// version: 8 +// version: 9 //! Devnet printable master NFT `Print -> Burn` campaign. @@ -1175,21 +1175,8 @@ mod tests { std::option::Option::Some(value) => value, std::option::Option::None => panic!("workspace root cannot be resolved"), }; - let config_path = match std::env::var("KS_DEVNET_CONFIG_PATH") { - std::result::Result::Ok(value) => { - let path = std::path::PathBuf::from(value); - if path.is_absolute() { path } else { workspace_root.join(path) } - }, - std::result::Result::Err(_) => { - workspace_root.join("config/ks-pipeline-demo-scenarios.default.config.json") - }, - }; - let composed = ks_config::read_composed_app_config_with_environment( - config_path.as_path(), - workspace_root, - ) - .unwrap_or_else(|error| panic!("Devnet configuration loading failed: {error}")); - let config = composed.app_config; + let config = crate::load_demo_scenario_config(workspace_root) + .unwrap_or_else(|error| panic!("Devnet configuration loading failed: {error}")); let profile_name = std::env::var("KS_DEVNET_PROFILE").ok(); let mut profile = crate::resolve_demo_devnet_profile(&config, profile_name.as_deref()) .unwrap_or_else(|error| panic!("Devnet profile resolution failed: {error}")); diff --git a/ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/use_campaign.rs b/ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/use_campaign.rs index 431a551..f2311ce 100644 --- a/ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/use_campaign.rs +++ b/ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/use_campaign.rs @@ -1,5 +1,5 @@ // file: ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/use_campaign.rs -// version: 5 +// version: 6 //! Bounded Devnet availability probe for the current Metaplex `Use` surface. @@ -420,21 +420,8 @@ mod tests { std::option::Option::Some(value) => value, std::option::Option::None => panic!("workspace root cannot be resolved"), }; - let config_path = match std::env::var("KS_DEVNET_CONFIG_PATH") { - std::result::Result::Ok(value) => { - let path = std::path::PathBuf::from(value); - if path.is_absolute() { path } else { workspace_root.join(path) } - }, - std::result::Result::Err(_) => { - workspace_root.join("config/ks-pipeline-demo-scenarios.default.config.json") - }, - }; - let composed = ks_config::read_composed_app_config_with_environment( - config_path.as_path(), - workspace_root, - ) - .unwrap_or_else(|error| panic!("Devnet configuration loading failed: {error}")); - let config = composed.app_config; + let config = crate::load_demo_scenario_config(workspace_root) + .unwrap_or_else(|error| panic!("Devnet configuration loading failed: {error}")); let profile_name = std::env::var("KS_DEVNET_PROFILE").ok(); let mut profile = crate::resolve_demo_devnet_profile(&config, profile_name.as_deref()) .unwrap_or_else(|error| panic!("Devnet profile resolution failed: {error}")); diff --git a/ks-pipeline-demo-scenarios/src/metadata/solana_program/devnet_execution.rs b/ks-pipeline-demo-scenarios/src/metadata/solana_program/devnet_execution.rs index 8a83fa6..54d84ca 100644 --- a/ks-pipeline-demo-scenarios/src/metadata/solana_program/devnet_execution.rs +++ b/ks-pipeline-demo-scenarios/src/metadata/solana_program/devnet_execution.rs @@ -1,5 +1,5 @@ // file: ks-pipeline-demo-scenarios/src/metadata/solana_program/devnet_execution.rs -// version: 3 +// version: 4 //! Real Devnet simulation and controlled submission for Solana Program Metadata. @@ -635,7 +635,7 @@ fn validate_profile( "Solana Program Metadata orchestration requires simulation", )); } - if request.submit && !profile.wallet.devnet_send_enabled { + if request.submit && !profile.execution.devnet_send_enabled { return std::result::Result::Err(ks_core::Error::config( "Devnet transaction submission is disabled by the wallet profile", )); diff --git a/ks-pipeline-demo-scenarios/src/metadata/solana_program/fixture.rs b/ks-pipeline-demo-scenarios/src/metadata/solana_program/fixture.rs index 25b6733..e160cfa 100644 --- a/ks-pipeline-demo-scenarios/src/metadata/solana_program/fixture.rs +++ b/ks-pipeline-demo-scenarios/src/metadata/solana_program/fixture.rs @@ -1,5 +1,5 @@ // file: ks-pipeline-demo-scenarios/src/metadata/solana_program/fixture.rs -// version: 7 +// version: 8 //! Native Solana Program Metadata fixture preparation for Devnet journeys. @@ -118,7 +118,7 @@ pub async fn prepare_solana_program_metadata_fixture( )); } if profile.wallet.cluster != "devnet" - || !profile.wallet.devnet_send_enabled + || !profile.execution.devnet_send_enabled || !profile.wallet.temporary_wallet_enabled || !profile.wallet.temporary_wallet_persist { diff --git a/ks-pipeline-demo-scenarios/src/solana.rs b/ks-pipeline-demo-scenarios/src/solana.rs index 30712b4..f00700f 100644 --- a/ks-pipeline-demo-scenarios/src/solana.rs +++ b/ks-pipeline-demo-scenarios/src/solana.rs @@ -1,5 +1,5 @@ // file: ks-pipeline-demo-scenarios/src/solana.rs -// version: 15 +// version: 16 //! Devnet Solana execution orchestration with canonical post-validation. @@ -967,7 +967,7 @@ fn validate_devnet_profile( request.airdrop_lamports, profile.execution.devnet_airdrop_max_lamports ))); } - if request.submit && !profile.wallet.devnet_send_enabled { + if request.submit && !profile.execution.devnet_send_enabled { return std::result::Result::Err(ks_core::Error::config( "Devnet transaction submission is disabled by the wallet profile", )); diff --git a/ks-pipeline-demo-scenarios/src/spl/associated_token_account.rs b/ks-pipeline-demo-scenarios/src/spl/associated_token_account.rs index 8a4d00d..86dbf22 100644 --- a/ks-pipeline-demo-scenarios/src/spl/associated_token_account.rs +++ b/ks-pipeline-demo-scenarios/src/spl/associated_token_account.rs @@ -1,5 +1,5 @@ // file: ks-pipeline-demo-scenarios/src/spl/associated_token_account.rs -// version: 14 +// version: 15 //! Devnet ATA execution with stateful and canonical post-validation. @@ -686,7 +686,7 @@ fn validate_profile( "SPL Associated Token Account Devnet orchestration requires simulation", )); } - if request.submit && !profile.wallet.devnet_send_enabled { + if request.submit && !profile.execution.devnet_send_enabled { return std::result::Result::Err(ks_core::Error::config( "Devnet transaction submission is disabled by the wallet profile", )); diff --git a/ks-pipeline-demo-scenarios/src/spl/memo.rs b/ks-pipeline-demo-scenarios/src/spl/memo.rs index 502f022..cd2bdd7 100644 --- a/ks-pipeline-demo-scenarios/src/spl/memo.rs +++ b/ks-pipeline-demo-scenarios/src/spl/memo.rs @@ -1,5 +1,5 @@ // file: ks-pipeline-demo-scenarios/src/spl/memo.rs -// version: 10 +// version: 11 //! Devnet SPL Memo v4 execution with canonical post-validation. @@ -544,7 +544,7 @@ fn validate_profile( "Memo Devnet orchestration requires simulation", )); } - if request.submit && !profile.wallet.devnet_send_enabled { + if request.submit && !profile.execution.devnet_send_enabled { return std::result::Result::Err(ks_core::Error::config( "Devnet transaction submission is disabled by the wallet profile", )); diff --git a/ks-pipeline-demo-scenarios/src/spl/token/execution.rs b/ks-pipeline-demo-scenarios/src/spl/token/execution.rs index d848656..8f31373 100644 --- a/ks-pipeline-demo-scenarios/src/spl/token/execution.rs +++ b/ks-pipeline-demo-scenarios/src/spl/token/execution.rs @@ -1,5 +1,5 @@ // file: ks-pipeline-demo-scenarios/src/spl/token/execution.rs -// version: 14 +// version: 15 //! Devnet classic SPL Token execution with stateful and canonical post-validation. @@ -642,7 +642,7 @@ fn validate_profile( "SPL Token Devnet orchestration requires simulation", )); } - if request.submit && !profile.wallet.devnet_send_enabled { + if request.submit && !profile.execution.devnet_send_enabled { return std::result::Result::Err(ks_core::Error::config( "Devnet transaction submission is disabled by the wallet profile", )); diff --git a/ks-pipeline-demo-scenarios/src/spl/token/lifecycle.rs b/ks-pipeline-demo-scenarios/src/spl/token/lifecycle.rs index 24e3de6..3a130ff 100644 --- a/ks-pipeline-demo-scenarios/src/spl/token/lifecycle.rs +++ b/ks-pipeline-demo-scenarios/src/spl/token/lifecycle.rs @@ -1,5 +1,5 @@ // file: ks-pipeline-demo-scenarios/src/spl/token/lifecycle.rs -// version: 13 +// version: 14 //! Controlled Devnet lifecycle for freshly prepared classic SPL Token accounts. @@ -295,7 +295,7 @@ where return std::result::Result::Err(error); } if profile.wallet.cluster != "devnet" - || !profile.wallet.devnet_send_enabled + || !profile.execution.devnet_send_enabled || !profile.execution.require_simulation { return std::result::Result::Err(ks_core::Error::config( diff --git a/ks-pipeline-demo-scenarios/src/spl/token_2022/devnet_execution.rs b/ks-pipeline-demo-scenarios/src/spl/token_2022/devnet_execution.rs index 63f69cb..dd12db0 100644 --- a/ks-pipeline-demo-scenarios/src/spl/token_2022/devnet_execution.rs +++ b/ks-pipeline-demo-scenarios/src/spl/token_2022/devnet_execution.rs @@ -1,5 +1,5 @@ // file: ks-pipeline-demo-scenarios/src/spl/token_2022/devnet_execution.rs -// version: 8 +// version: 9 //! Devnet Token-2022 execution with stateful and canonical post-validation. @@ -882,7 +882,7 @@ fn validate_profile( "Token-2022 Devnet orchestration requires simulation", )); } - if request.submit && !profile.wallet.devnet_send_enabled { + if request.submit && !profile.execution.devnet_send_enabled { return std::result::Result::Err(ks_core::Error::config( "Devnet transaction submission is disabled by the wallet profile", )); diff --git a/ks-pipeline-demo-scenarios/src/spl/token_2022/metadata/campaign.rs b/ks-pipeline-demo-scenarios/src/spl/token_2022/metadata/campaign.rs index f758c9f..4995711 100644 --- a/ks-pipeline-demo-scenarios/src/spl/token_2022/metadata/campaign.rs +++ b/ks-pipeline-demo-scenarios/src/spl/token_2022/metadata/campaign.rs @@ -1,5 +1,5 @@ // file: ks-pipeline-demo-scenarios/src/spl/token_2022/metadata/campaign.rs -// version: 8 +// version: 9 //! Ordered confirmed Devnet campaign for the five Token-2022 Token Metadata instructions. @@ -497,21 +497,8 @@ mod tests { std::option::Option::Some(value) => value, std::option::Option::None => panic!("workspace root cannot be resolved"), }; - let config_path = match std::env::var("KS_DEVNET_CONFIG_PATH") { - std::result::Result::Ok(value) => { - let path = std::path::PathBuf::from(value); - if path.is_absolute() { path } else { workspace_root.join(path) } - }, - std::result::Result::Err(_) => { - workspace_root.join("config/ks-pipeline-demo-scenarios.default.config.json") - }, - }; - let composed = ks_config::read_composed_app_config_with_environment( - config_path.as_path(), - workspace_root, - ) - .unwrap_or_else(|error| panic!("Devnet configuration loading failed: {error}")); - let config = composed.app_config; + let config = crate::load_demo_scenario_config(workspace_root) + .unwrap_or_else(|error| panic!("Devnet configuration loading failed: {error}")); let profile_name = std::env::var("KS_DEVNET_PROFILE").ok(); let mut profile = crate::resolve_demo_devnet_profile(&config, profile_name.as_deref()) .unwrap_or_else(|error| panic!("Devnet profile resolution failed: {error}")); diff --git a/ks-pipeline-demo-scenarios/src/spl/token_2022/metadata/fixture.rs b/ks-pipeline-demo-scenarios/src/spl/token_2022/metadata/fixture.rs index d698bf0..7ce2ed0 100644 --- a/ks-pipeline-demo-scenarios/src/spl/token_2022/metadata/fixture.rs +++ b/ks-pipeline-demo-scenarios/src/spl/token_2022/metadata/fixture.rs @@ -1,5 +1,5 @@ // file: ks-pipeline-demo-scenarios/src/spl/token_2022/metadata/fixture.rs -// version: 4 +// version: 5 //! Native Token-2022 mint preparation for the embedded Token Metadata Devnet campaign. @@ -91,7 +91,7 @@ pub async fn prepare_token_2022_metadata_fixture( "Token-2022 metadata fixture requires a persistent Devnet wallet profile", )); } - if !profile.wallet.devnet_send_enabled || !options.operator_confirmed { + if !profile.execution.devnet_send_enabled || !options.operator_confirmed { return std::result::Result::Err(ks_core::Error::config( "Token-2022 metadata fixture requires enabled Devnet submission and explicit operator confirmation", )); diff --git a/scripts/audit_khadhroony_workspace_rules.py b/scripts/audit_khadhroony_workspace_rules.py index 52cf9e7..1a4e13b 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: 22 +# version: 23 """Audit mechanically verifiable rules specific to khadhroony-bot3.""" @@ -909,14 +909,19 @@ def audit_environment_namespaces(root: pathlib.Path) -> list[Violation]: placeholder = re.compile(r"\$\{([A-Z][A-Z0-9_]*)") for relative in [ "config/kb-app-demo-desktop.default.config.json", - "config/ks-pipeline-demo-scenarios.default.config.json", "config/logging.config.json", "config/transport.config.json", "config/listeners.config.json", + "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 = root / relative if not config.is_file(): @@ -954,83 +959,66 @@ def audit_environment_namespaces(root: pathlib.Path) -> list[Violation]: def audit_configuration_split(root: pathlib.Path) -> list[Violation]: - """Require binary composition plus independent shared configuration documents.""" + """Require one binary composition plus independent shared configuration documents.""" violations: list[Violation] = [] required_files = [ "config/kb-app-demo-desktop.default.config.json", - "config/ks-pipeline-demo-scenarios.default.config.json", "config/logging.config.json", "config/transport.config.json", "config/listeners.config.json", + "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/schemas/composition.config.schema.json", "config/schemas/logging.config.schema.json", "config/schemas/transport.config.schema.json", "config/schemas/listeners.config.schema.json", + "config/schemas/store.config.schema.json", + "config/schemas/wallet.config.schema.json", + "config/schemas/execution.config.schema.json", "config/schemas/resolved.app.config.schema.json", ] for relative in required_files: if (root / relative).is_file(): continue - violations.append( - Violation( - "KH_CFG001", - relative, - 1, - "required split configuration file is missing", - ) - ) - composition_paths = [ - "config/kb-app-demo-desktop.default.config.json", - "config/ks-pipeline-demo-scenarios.default.config.json", - ] - for relative in composition_paths: - path = root / relative - if not path.is_file(): - continue + violations.append(Violation("KH_CFG001", relative, 1, "required split configuration file is missing")) + composition_path = root / "config/kb-app-demo-desktop.default.config.json" + if composition_path.is_file(): try: - composition = json.loads(path.read_text(encoding="utf-8")) + composition = json.loads(composition_path.read_text(encoding="utf-8")) except json.JSONDecodeError: composition = None - if not isinstance(composition, dict): - continue - sources = composition.get("sources") - if not isinstance(sources, dict) or set(sources) != {"logging", "transport", "listeners"}: - violations.append( - Violation( - "KH_CFG002", - relative, - 1, - "binary composition must reference logging, transport and listeners sources", - ) - ) - for index, profile in enumerate(composition.get("profiles", []), 1): - if not isinstance(profile, dict): - continue - if "solana" in profile or "logging" in profile: - violations.append( - Violation( - "KH_CFG003", - relative, - index, - "composition profiles must reference transport/listener/logging profiles instead of embedding them", - ) - ) - for key in ["logging_profile", "transport_profile", "listeners_profile"]: - if not isinstance(profile.get(key), str) or not profile.get(key, "").strip(): - violations.append( - Violation( - "KH_CFG004", - relative, - index, - f"composition profile must define non-empty `{key}`", - ) - ) - for relative in ["config/logging.config.json", "config/transport.config.json", "config/listeners.config.json"]: + if isinstance(composition, dict): + sources = composition.get("sources") + expected_sources = {"logging", "transport", "listeners", "store", "wallet", "execution"} + if not isinstance(sources, dict) or set(sources) != expected_sources: + violations.append(Violation("KH_CFG002", composition_path.relative_to(root).as_posix(), 1, "binary composition must reference all six shared configuration sources")) + for index, profile in enumerate(composition.get("profiles", []), 1): + if not isinstance(profile, dict): + continue + forbidden = {"solana", "logging", "database", "data", "wallet", "execution"} + if forbidden.intersection(profile): + violations.append(Violation("KH_CFG003", composition_path.relative_to(root).as_posix(), index, "composition profiles must reference shared profiles instead of embedding shared configuration")) + for key in ["logging_profile", "transport_profile", "listeners_profile", "store_profile", "wallet_profile", "execution_profile"]: + if key in profile and (not isinstance(profile.get(key), str) or not profile.get(key, "").strip()): + violations.append(Violation("KH_CFG004", composition_path.relative_to(root).as_posix(), index, f"optional composition override `{key}` must be non-empty when present")) + shared = [ + "config/logging.config.json", + "config/transport.config.json", + "config/listeners.config.json", + "config/store.config.json", + "config/wallet.config.json", + "config/execution.config.json", + ] + for relative in shared: path = root / relative if not path.is_file(): continue @@ -1040,28 +1028,47 @@ def audit_configuration_split(root: pathlib.Path) -> list[Violation]: document = None if not isinstance(document, dict): continue - if not isinstance(document.get("active_profile"), str): - violations.append(Violation("KH_CFG005", relative, 1, "shared configuration document must define active_profile")) + if not isinstance(document.get("default_profile"), str) or not document.get("default_profile", "").strip(): + violations.append(Violation("KH_CFG005", relative, 1, "shared configuration document must define a non-empty default_profile")) if not isinstance(document.get("profiles"), list) or not document.get("profiles"): violations.append(Violation("KH_CFG006", relative, 1, "shared configuration document must define named profiles")) + logging = root / "config/logging.config.json" + if logging.is_file(): + document = json.loads(logging.read_text(encoding="utf-8")) + if not isinstance(document.get("logs_directory"), str) or not document.get("logs_directory", "").strip(): + violations.append(Violation("KH_CFG008", "config/logging.config.json", 1, "logging root directory must be global and non-empty")) + wallet = root / "config/wallet.config.json" + if wallet.is_file(): + document = json.loads(wallet.read_text(encoding="utf-8")) + if not isinstance(document.get("wallets_directory"), str) or not document.get("wallets_directory", "").strip(): + violations.append(Violation("KH_CFG009", "config/wallet.config.json", 1, "wallet root directory must be global and non-empty")) + for index, profile in enumerate(document.get("profiles", []), 1): + if isinstance(profile, dict) and any(key.endswith("_send_enabled") for key in profile): + violations.append(Violation("KH_CFG010", "config/wallet.config.json", index, "send authorization belongs to execution configuration, not wallet profiles")) + transport = root / "config/transport.config.json" + if transport.is_file(): + document = json.loads(transport.read_text(encoding="utf-8")) + defaults = document.get("ws_endpoint_defaults") + if not isinstance(defaults, list) or not defaults: + violations.append(Violation("KH_CFG011", "config/transport.config.json", 1, "transport configuration must define named WebSocket endpoint defaults")) + for pindex, profile in enumerate(document.get("profiles", []), 1): + if not isinstance(profile, dict): + continue + for endpoint in profile.get("ws_endpoints", []): + if isinstance(endpoint, dict) and (not isinstance(endpoint.get("defaults"), str) or not endpoint.get("defaults", "").strip()): + violations.append(Violation("KH_CFG012", "config/transport.config.json", pindex, "each WebSocket endpoint must select one named defaults class")) legacy = [ "config/app.config.json", "config/example.app.config.json", "config/schemas/app.config.schema.json", "config/example.config.json", "config/schema.config.json", + "config/ks-pipeline-demo-scenarios.default.config.json", ] for relative in legacy: if not (root / relative).exists(): continue - violations.append( - Violation( - "KH_CFG007", - relative, - 1, - "legacy monolithic application configuration file must be removed", - ) - ) + violations.append(Violation("KH_CFG007", relative, 1, "legacy or unnecessary configuration composition file must be removed")) return violations diff --git a/test-fixtures/config/example.resolved.app.config.json b/test-fixtures/config/example.resolved.app.config.json index 0d20136..f2ccfbe 100644 --- a/test-fixtures/config/example.resolved.app.config.json +++ b/test-fixtures/config/example.resolved.app.config.json @@ -5,8 +5,7 @@ "name": "local_devnet", "app": { "name": "khadhroony-bot3", - "environment": "development", - "auto_reconnect_default": false + "environment": "development" }, "database": { "enabled": true, @@ -26,10 +25,6 @@ "use_wal": true } }, - "data": { - "wallets_directory": "wallets", - "logs_directory": "logs" - }, "solana": { "http_endpoints": [ { @@ -209,11 +204,7 @@ "cluster": "devnet", "temporary_wallet_enabled": true, "temporary_wallet_alias": "local-devnet-operator", - "temporary_wallet_persist": true, - "localnet_send_enabled": false, - "devnet_send_enabled": true, - "testnet_send_enabled": false, - "mainnet_send_enabled": false + "temporary_wallet_persist": true }, "execution": { "dry_run_default": true, @@ -229,7 +220,11 @@ "send_max_retries": 3, "confirmation_poll_interval_ms": 500, "confirmation_max_attempts": 120, - "devnet_airdrop_max_lamports": 2000000000 + "devnet_airdrop_max_lamports": 2000000000, + "localnet_send_enabled": false, + "devnet_send_enabled": true, + "testnet_send_enabled": false, + "mainnet_send_enabled": false }, "demo": { "live_demo_enabled": true, diff --git a/test-fixtures/config/resolved.app.config.json b/test-fixtures/config/resolved.app.config.json index cfd290c..99d813c 100644 --- a/test-fixtures/config/resolved.app.config.json +++ b/test-fixtures/config/resolved.app.config.json @@ -5,8 +5,7 @@ "name": "local_devnet", "app": { "name": "khadhroony-bot3", - "environment": "development", - "auto_reconnect_default": false + "environment": "development" }, "database": { "enabled": true, @@ -26,10 +25,6 @@ "use_wal": true } }, - "data": { - "wallets_directory": "wallets", - "logs_directory": "logs" - }, "solana": { "http_endpoints": [ { @@ -209,11 +204,7 @@ "cluster": "devnet", "temporary_wallet_enabled": true, "temporary_wallet_alias": "local-devnet-operator", - "temporary_wallet_persist": true, - "localnet_send_enabled": false, - "devnet_send_enabled": true, - "testnet_send_enabled": false, - "mainnet_send_enabled": false + "temporary_wallet_persist": true }, "execution": { "dry_run_default": true, @@ -229,7 +220,11 @@ "send_max_retries": 3, "confirmation_poll_interval_ms": 500, "confirmation_max_attempts": 120, - "devnet_airdrop_max_lamports": 2000000000 + "devnet_airdrop_max_lamports": 2000000000, + "localnet_send_enabled": false, + "devnet_send_enabled": true, + "testnet_send_enabled": false, + "mainnet_send_enabled": false }, "demo": { "live_demo_enabled": true, @@ -240,8 +235,7 @@ "name": "mainnet_research", "app": { "name": "khadhroony-bot3", - "environment": "research", - "auto_reconnect_default": false + "environment": "research" }, "database": { "enabled": true, @@ -261,10 +255,6 @@ "use_wal": true } }, - "data": { - "wallets_directory": "wallets", - "logs_directory": "logs" - }, "solana": { "http_endpoints": [ { @@ -644,11 +634,7 @@ "cluster": "mainnet-beta", "temporary_wallet_enabled": false, "temporary_wallet_alias": "mainnet_research-disabled", - "temporary_wallet_persist": false, - "localnet_send_enabled": false, - "devnet_send_enabled": false, - "testnet_send_enabled": false, - "mainnet_send_enabled": false + "temporary_wallet_persist": false }, "execution": { "dry_run_default": true, @@ -664,7 +650,11 @@ "send_max_retries": 3, "confirmation_poll_interval_ms": 500, "confirmation_max_attempts": 120, - "devnet_airdrop_max_lamports": 0 + "devnet_airdrop_max_lamports": 0, + "localnet_send_enabled": false, + "devnet_send_enabled": false, + "testnet_send_enabled": false, + "mainnet_send_enabled": false }, "demo": { "live_demo_enabled": true, @@ -675,8 +665,7 @@ "name": "mainnet", "app": { "name": "khadhroony-bot3", - "environment": "mainnet", - "auto_reconnect_default": false + "environment": "mainnet" }, "database": { "enabled": true, @@ -696,10 +685,6 @@ "use_wal": true } }, - "data": { - "wallets_directory": "wallets", - "logs_directory": "logs" - }, "solana": { "http_endpoints": [ { @@ -1079,11 +1064,7 @@ "cluster": "mainnet-beta", "temporary_wallet_enabled": false, "temporary_wallet_alias": "mainnet-disabled", - "temporary_wallet_persist": false, - "localnet_send_enabled": false, - "devnet_send_enabled": false, - "testnet_send_enabled": false, - "mainnet_send_enabled": false + "temporary_wallet_persist": false }, "execution": { "dry_run_default": true, @@ -1099,7 +1080,11 @@ "send_max_retries": 3, "confirmation_poll_interval_ms": 500, "confirmation_max_attempts": 120, - "devnet_airdrop_max_lamports": 0 + "devnet_airdrop_max_lamports": 0, + "localnet_send_enabled": false, + "devnet_send_enabled": false, + "testnet_send_enabled": false, + "mainnet_send_enabled": false }, "demo": { "live_demo_enabled": true,