0.5.1-pre.007
This commit is contained in:
@@ -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.
|
# 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_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.
|
# 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.
|
# Optional Devnet profile selected by CLI and opt-in scenario tests.
|
||||||
# When absent, the first compatible Devnet profile is selected.
|
# When absent, the first compatible Devnet profile is selected.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# file: Cargo.toml
|
# file: Cargo.toml
|
||||||
# version: 50
|
# version: 51
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
resolver = "3"
|
resolver = "3"
|
||||||
@@ -18,7 +18,7 @@ members = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.5.1-pre.6"
|
version = "0.5.1-pre.7"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://git.sasedev.com/Sasedev/khadhroony-bot3"
|
repository = "https://git.sasedev.com/Sasedev/khadhroony-bot3"
|
||||||
|
|||||||
103
config/README.md
103
config/README.md
@@ -1,110 +1,123 @@
|
|||||||
<!-- file: config/README.md -->
|
<!-- file: config/README.md -->
|
||||||
<!-- version: 21 -->
|
<!-- version: 22 -->
|
||||||
|
|
||||||
# Configuration locale
|
# 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 `<binary>.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 `<binary>.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
|
```text
|
||||||
config/kb-app-demo-desktop.default.config.json
|
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
|
`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.
|
||||||
config/ks-pipeline-demo-scenarios.default.config.json
|
|
||||||
```
|
|
||||||
|
|
||||||
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`.
|
## Fichiers actifs
|
||||||
|
|
||||||
## Documents partagés actuels
|
|
||||||
|
|
||||||
```text
|
```text
|
||||||
config/
|
config/
|
||||||
├── kb-app-demo-desktop.default.config.json
|
├── kb-app-demo-desktop.default.config.json
|
||||||
├── ks-pipeline-demo-scenarios.default.config.json
|
|
||||||
├── logging.config.json
|
├── logging.config.json
|
||||||
├── transport.config.json
|
├── transport.config.json
|
||||||
├── listeners.config.json
|
├── listeners.config.json
|
||||||
|
├── store.config.json
|
||||||
|
├── wallet.config.json
|
||||||
|
├── execution.config.json
|
||||||
├── example.kb-app-demo-desktop.default.config.json
|
├── example.kb-app-demo-desktop.default.config.json
|
||||||
├── example.logging.config.json
|
├── example.logging.config.json
|
||||||
├── example.transport.config.json
|
├── example.transport.config.json
|
||||||
├── example.listeners.config.json
|
├── example.listeners.config.json
|
||||||
|
├── example.store.config.json
|
||||||
|
├── example.wallet.config.json
|
||||||
|
├── example.execution.config.json
|
||||||
└── schemas/
|
└── schemas/
|
||||||
├── composition.config.schema.json
|
├── composition.config.schema.json
|
||||||
├── logging.config.schema.json
|
├── logging.config.schema.json
|
||||||
├── transport.config.schema.json
|
├── transport.config.schema.json
|
||||||
├── listeners.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
|
||||||
```
|
```
|
||||||
|
|
||||||
`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
|
```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
|
logging_profile = mainnet_research
|
||||||
transport_profile = mainnet_research
|
transport_profile = mainnet_research
|
||||||
listeners_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 ;
|
## Transport WebSocket
|
||||||
- `programSubscribe` ;
|
|
||||||
- `accountSubscribe` ;
|
|
||||||
- commitment et rôles d'endpoint associés.
|
|
||||||
|
|
||||||
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` ;
|
## Store
|
||||||
- `wallet` ;
|
|
||||||
- `execution` ;
|
|
||||||
- `demo`.
|
|
||||||
|
|
||||||
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
|
## 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 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
|
## Exemples et schémas
|
||||||
|
|
||||||
Les exemples sous `config/` sont des références conformes et ne sont pas chargés automatiquement. Tous les schémas actifs résident exclusivement sous `config/schemas/`.
|
Les exemples sous `config/` 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.
|
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.
|
||||||
|
|
||||||
## 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.
|
|
||||||
|
|||||||
26
config/example.execution.config.json
Normal file
26
config/example.execution.config.json
Normal file
@@ -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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -3,67 +3,17 @@
|
|||||||
"sources": {
|
"sources": {
|
||||||
"logging": "config/logging.config.json",
|
"logging": "config/logging.config.json",
|
||||||
"transport": "config/transport.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": [
|
"profiles": [
|
||||||
{
|
{
|
||||||
"name": "local_devnet",
|
"name": "local_devnet",
|
||||||
"app": {
|
"app": {
|
||||||
"name": "khadhroony-bot3",
|
"name": "khadhroony-bot3",
|
||||||
"environment": "development",
|
"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": {
|
"demo": {
|
||||||
"live_demo_enabled": true,
|
"live_demo_enabled": true,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"active_profile": "local_devnet",
|
"default_profile": "local_devnet",
|
||||||
"profiles": [
|
"profiles": [
|
||||||
{
|
{
|
||||||
"name": "local_devnet",
|
"name": "local_devnet",
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"active_profile": "local_devnet",
|
"logs_directory": "${KS_LOGS_DIRECTORY:-logs}",
|
||||||
|
"default_profile": "local_devnet",
|
||||||
"profiles": [
|
"profiles": [
|
||||||
{
|
{
|
||||||
"name": "local_devnet",
|
"name": "local_devnet",
|
||||||
@@ -23,7 +24,7 @@
|
|||||||
"enabled": true,
|
"enabled": true,
|
||||||
"sink": "file",
|
"sink": "file",
|
||||||
"level": "debug",
|
"level": "debug",
|
||||||
"path": "logs/devnet/debug.log",
|
"path": "devnet/debug.log",
|
||||||
"rotation": "daily",
|
"rotation": "daily",
|
||||||
"format": "human",
|
"format": "human",
|
||||||
"ansi": false,
|
"ansi": false,
|
||||||
|
|||||||
26
config/example.store.config.json
Normal file
26
config/example.store.config.json
Normal file
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -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": [
|
"profiles": [
|
||||||
{
|
{
|
||||||
"name": "local_devnet",
|
"name": "local_devnet",
|
||||||
@@ -97,12 +117,7 @@
|
|||||||
"provider": "solana-public",
|
"provider": "solana-public",
|
||||||
"cluster": "devnet",
|
"cluster": "devnet",
|
||||||
"url": "wss://api.devnet.solana.com",
|
"url": "wss://api.devnet.solana.com",
|
||||||
"connect_timeout_ms": 5000,
|
"defaults": "standard_rpc_ws",
|
||||||
"request_timeout_ms": 10000,
|
|
||||||
"unsubscribe_timeout_ms": 1500,
|
|
||||||
"write_channel_capacity": 256,
|
|
||||||
"event_channel_capacity": 1024,
|
|
||||||
"auto_reconnect": false,
|
|
||||||
"roles": [
|
"roles": [
|
||||||
{
|
{
|
||||||
"role": "slot_notifications",
|
"role": "slot_notifications",
|
||||||
|
|||||||
14
config/example.wallet.config.json
Normal file
14
config/example.wallet.config.json
Normal file
@@ -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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
68
config/execution.config.json
Normal file
68
config/execution.config.json
Normal file
@@ -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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -3,67 +3,17 @@
|
|||||||
"sources": {
|
"sources": {
|
||||||
"logging": "config/logging.config.json",
|
"logging": "config/logging.config.json",
|
||||||
"transport": "config/transport.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": [
|
"profiles": [
|
||||||
{
|
{
|
||||||
"name": "local_devnet",
|
"name": "local_devnet",
|
||||||
"app": {
|
"app": {
|
||||||
"name": "khadhroony-bot3",
|
"name": "khadhroony-bot3",
|
||||||
"environment": "development",
|
"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": {
|
"demo": {
|
||||||
"live_demo_enabled": true,
|
"live_demo_enabled": true,
|
||||||
@@ -74,129 +24,35 @@
|
|||||||
"name": "mainnet_research",
|
"name": "mainnet_research",
|
||||||
"app": {
|
"app": {
|
||||||
"name": "khadhroony-bot3",
|
"name": "khadhroony-bot3",
|
||||||
"environment": "research",
|
"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": {
|
"demo": {
|
||||||
"live_demo_enabled": true,
|
"live_demo_enabled": true,
|
||||||
"trading_demo_enabled": false
|
"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",
|
"name": "mainnet",
|
||||||
"app": {
|
"app": {
|
||||||
"name": "khadhroony-bot3",
|
"name": "khadhroony-bot3",
|
||||||
"environment": "mainnet",
|
"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": {
|
"demo": {
|
||||||
"live_demo_enabled": true,
|
"live_demo_enabled": true,
|
||||||
"trading_demo_enabled": false
|
"trading_demo_enabled": false
|
||||||
}
|
},
|
||||||
|
"logging_profile": "mainnet",
|
||||||
|
"transport_profile": "mainnet",
|
||||||
|
"listeners_profile": "mainnet",
|
||||||
|
"store_profile": "mainnet",
|
||||||
|
"wallet_profile": "mainnet",
|
||||||
|
"execution_profile": "mainnet"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"active_profile": "mainnet_research",
|
"default_profile": "local_devnet",
|
||||||
"profiles": [
|
"profiles": [
|
||||||
{
|
{
|
||||||
"name": "local_devnet",
|
"name": "local_devnet",
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -34,8 +34,7 @@
|
|||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"required": [
|
"required": [
|
||||||
"name",
|
"name",
|
||||||
"environment",
|
"environment"
|
||||||
"auto_reconnect_default"
|
|
||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"name": {
|
"name": {
|
||||||
@@ -43,238 +42,6 @@
|
|||||||
},
|
},
|
||||||
"environment": {
|
"environment": {
|
||||||
"$ref": "#/$defs/non_empty_string"
|
"$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": [
|
"required": [
|
||||||
"logging",
|
"logging",
|
||||||
"transport",
|
"transport",
|
||||||
"listeners"
|
"listeners",
|
||||||
|
"store",
|
||||||
|
"wallet",
|
||||||
|
"execution"
|
||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"logging": {
|
"logging": {
|
||||||
@@ -311,6 +81,15 @@
|
|||||||
},
|
},
|
||||||
"listeners": {
|
"listeners": {
|
||||||
"$ref": "#/$defs/non_empty_string"
|
"$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": [
|
"required": [
|
||||||
"name",
|
"name",
|
||||||
"app",
|
"app",
|
||||||
"logging_profile",
|
|
||||||
"transport_profile",
|
|
||||||
"listeners_profile",
|
|
||||||
"database",
|
|
||||||
"data",
|
|
||||||
"wallet",
|
|
||||||
"execution",
|
|
||||||
"demo"
|
"demo"
|
||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
@@ -336,6 +108,9 @@
|
|||||||
"app": {
|
"app": {
|
||||||
"$ref": "#/$defs/app_section"
|
"$ref": "#/$defs/app_section"
|
||||||
},
|
},
|
||||||
|
"demo": {
|
||||||
|
"$ref": "#/$defs/demo"
|
||||||
|
},
|
||||||
"logging_profile": {
|
"logging_profile": {
|
||||||
"$ref": "#/$defs/non_empty_string"
|
"$ref": "#/$defs/non_empty_string"
|
||||||
},
|
},
|
||||||
@@ -345,20 +120,14 @@
|
|||||||
"listeners_profile": {
|
"listeners_profile": {
|
||||||
"$ref": "#/$defs/non_empty_string"
|
"$ref": "#/$defs/non_empty_string"
|
||||||
},
|
},
|
||||||
"database": {
|
"store_profile": {
|
||||||
"$ref": "#/$defs/database"
|
"$ref": "#/$defs/non_empty_string"
|
||||||
},
|
},
|
||||||
"data": {
|
"wallet_profile": {
|
||||||
"$ref": "#/$defs/data"
|
"$ref": "#/$defs/non_empty_string"
|
||||||
},
|
},
|
||||||
"wallet": {
|
"execution_profile": {
|
||||||
"$ref": "#/$defs/wallet"
|
"$ref": "#/$defs/non_empty_string"
|
||||||
},
|
|
||||||
"execution": {
|
|
||||||
"$ref": "#/$defs/execution"
|
|
||||||
},
|
|
||||||
"demo": {
|
|
||||||
"$ref": "#/$defs/demo"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
127
config/schemas/execution.config.schema.json
Normal file
127
config/schemas/execution.config.schema.json
Normal file
@@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,11 +5,11 @@
|
|||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"required": [
|
"required": [
|
||||||
"active_profile",
|
"default_profile",
|
||||||
"profiles"
|
"profiles"
|
||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"active_profile": {
|
"default_profile": {
|
||||||
"$ref": "#/$defs/non_empty_string"
|
"$ref": "#/$defs/non_empty_string"
|
||||||
},
|
},
|
||||||
"profiles": {
|
"profiles": {
|
||||||
|
|||||||
@@ -5,11 +5,15 @@
|
|||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"required": [
|
"required": [
|
||||||
"active_profile",
|
"logs_directory",
|
||||||
|
"default_profile",
|
||||||
"profiles"
|
"profiles"
|
||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"active_profile": {
|
"logs_directory": {
|
||||||
|
"$ref": "#/$defs/non_empty_string"
|
||||||
|
},
|
||||||
|
"default_profile": {
|
||||||
"$ref": "#/$defs/non_empty_string"
|
"$ref": "#/$defs/non_empty_string"
|
||||||
},
|
},
|
||||||
"profiles": {
|
"profiles": {
|
||||||
|
|||||||
@@ -32,7 +32,6 @@
|
|||||||
"name",
|
"name",
|
||||||
"app",
|
"app",
|
||||||
"database",
|
"database",
|
||||||
"data",
|
|
||||||
"solana",
|
"solana",
|
||||||
"wallet",
|
"wallet",
|
||||||
"execution",
|
"execution",
|
||||||
@@ -48,9 +47,6 @@
|
|||||||
"database": {
|
"database": {
|
||||||
"$ref": "#/$defs/database"
|
"$ref": "#/$defs/database"
|
||||||
},
|
},
|
||||||
"data": {
|
|
||||||
"$ref": "#/$defs/data"
|
|
||||||
},
|
|
||||||
"solana": {
|
"solana": {
|
||||||
"$ref": "#/$defs/solana"
|
"$ref": "#/$defs/solana"
|
||||||
},
|
},
|
||||||
@@ -70,8 +66,7 @@
|
|||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"required": [
|
"required": [
|
||||||
"name",
|
"name",
|
||||||
"environment",
|
"environment"
|
||||||
"auto_reconnect_default"
|
|
||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"name": {
|
"name": {
|
||||||
@@ -79,9 +74,6 @@
|
|||||||
},
|
},
|
||||||
"environment": {
|
"environment": {
|
||||||
"$ref": "#/$defs/non_empty_string"
|
"$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": {
|
"solana": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
@@ -516,11 +492,7 @@
|
|||||||
"cluster",
|
"cluster",
|
||||||
"temporary_wallet_enabled",
|
"temporary_wallet_enabled",
|
||||||
"temporary_wallet_alias",
|
"temporary_wallet_alias",
|
||||||
"temporary_wallet_persist",
|
"temporary_wallet_persist"
|
||||||
"localnet_send_enabled",
|
|
||||||
"devnet_send_enabled",
|
|
||||||
"testnet_send_enabled",
|
|
||||||
"mainnet_send_enabled"
|
|
||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"wallet_dir": {
|
"wallet_dir": {
|
||||||
@@ -540,18 +512,6 @@
|
|||||||
},
|
},
|
||||||
"temporary_wallet_persist": {
|
"temporary_wallet_persist": {
|
||||||
"type": "boolean"
|
"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",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"required": [
|
"required": [
|
||||||
|
"localnet_send_enabled",
|
||||||
|
"devnet_send_enabled",
|
||||||
|
"testnet_send_enabled",
|
||||||
|
"mainnet_send_enabled",
|
||||||
"dry_run_default",
|
"dry_run_default",
|
||||||
"require_simulation",
|
"require_simulation",
|
||||||
"require_operator_confirmation",
|
"require_operator_confirmation",
|
||||||
@@ -630,6 +594,18 @@
|
|||||||
"devnet_airdrop_max_lamports": {
|
"devnet_airdrop_max_lamports": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"minimum": 0
|
"minimum": 0
|
||||||
|
},
|
||||||
|
"mainnet_send_enabled": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"testnet_send_enabled": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"devnet_send_enabled": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"localnet_send_enabled": {
|
||||||
|
"type": "boolean"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
133
config/schemas/store.config.schema.json
Normal file
133
config/schemas/store.config.schema.json
Normal file
@@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,13 +5,21 @@
|
|||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"required": [
|
"required": [
|
||||||
"active_profile",
|
"default_profile",
|
||||||
|
"ws_endpoint_defaults",
|
||||||
"profiles"
|
"profiles"
|
||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"active_profile": {
|
"default_profile": {
|
||||||
"$ref": "#/$defs/non_empty_string"
|
"$ref": "#/$defs/non_empty_string"
|
||||||
},
|
},
|
||||||
|
"ws_endpoint_defaults": {
|
||||||
|
"type": "array",
|
||||||
|
"minItems": 1,
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/$defs/ws_endpoint_defaults"
|
||||||
|
}
|
||||||
|
},
|
||||||
"profiles": {
|
"profiles": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"minItems": 1,
|
"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": {
|
"endpoint_role": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"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": {
|
"profile": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
@@ -218,7 +274,7 @@
|
|||||||
"type": "array",
|
"type": "array",
|
||||||
"minItems": 1,
|
"minItems": 1,
|
||||||
"items": {
|
"items": {
|
||||||
"$ref": "#/$defs/ws_endpoint"
|
"$ref": "#/$defs/ws_endpoint_source"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
68
config/schemas/wallet.config.schema.json
Normal file
68
config/schemas/wallet.config.schema.json
Normal file
@@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
68
config/store.config.json
Normal file
68
config/store.config.json
Normal file
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -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": [
|
"profiles": [
|
||||||
{
|
{
|
||||||
"name": "local_devnet",
|
"name": "local_devnet",
|
||||||
@@ -97,12 +117,7 @@
|
|||||||
"provider": "solana-public",
|
"provider": "solana-public",
|
||||||
"cluster": "devnet",
|
"cluster": "devnet",
|
||||||
"url": "wss://api.devnet.solana.com",
|
"url": "wss://api.devnet.solana.com",
|
||||||
"connect_timeout_ms": 5000,
|
"defaults": "standard_rpc_ws",
|
||||||
"request_timeout_ms": 10000,
|
|
||||||
"unsubscribe_timeout_ms": 1500,
|
|
||||||
"write_channel_capacity": 256,
|
|
||||||
"event_channel_capacity": 1024,
|
|
||||||
"auto_reconnect": false,
|
|
||||||
"roles": [
|
"roles": [
|
||||||
{
|
{
|
||||||
"role": "slot_notifications",
|
"role": "slot_notifications",
|
||||||
@@ -406,12 +421,7 @@
|
|||||||
"provider": "solana-public",
|
"provider": "solana-public",
|
||||||
"cluster": "mainnet-beta",
|
"cluster": "mainnet-beta",
|
||||||
"url": "wss://api.mainnet.solana.com",
|
"url": "wss://api.mainnet.solana.com",
|
||||||
"connect_timeout_ms": 5000,
|
"defaults": "standard_rpc_ws",
|
||||||
"request_timeout_ms": 10000,
|
|
||||||
"unsubscribe_timeout_ms": 1500,
|
|
||||||
"write_channel_capacity": 256,
|
|
||||||
"event_channel_capacity": 1024,
|
|
||||||
"auto_reconnect": false,
|
|
||||||
"roles": [
|
"roles": [
|
||||||
{
|
{
|
||||||
"role": "slot_notifications",
|
"role": "slot_notifications",
|
||||||
@@ -448,12 +458,7 @@
|
|||||||
"provider": "helius",
|
"provider": "helius",
|
||||||
"cluster": "mainnet-beta",
|
"cluster": "mainnet-beta",
|
||||||
"url": "wss://mainnet.helius-rpc.com/?api-key=${KS_SECRET_HELIUS_API_KEY}",
|
"url": "wss://mainnet.helius-rpc.com/?api-key=${KS_SECRET_HELIUS_API_KEY}",
|
||||||
"connect_timeout_ms": 5000,
|
"defaults": "high_capacity_rpc_ws",
|
||||||
"request_timeout_ms": 10000,
|
|
||||||
"unsubscribe_timeout_ms": 1500,
|
|
||||||
"write_channel_capacity": 512,
|
|
||||||
"event_channel_capacity": 4096,
|
|
||||||
"auto_reconnect": false,
|
|
||||||
"roles": [
|
"roles": [
|
||||||
{
|
{
|
||||||
"role": "logs_subscribe",
|
"role": "logs_subscribe",
|
||||||
@@ -751,12 +756,7 @@
|
|||||||
"provider": "solana-public",
|
"provider": "solana-public",
|
||||||
"cluster": "mainnet-beta",
|
"cluster": "mainnet-beta",
|
||||||
"url": "wss://api.mainnet.solana.com",
|
"url": "wss://api.mainnet.solana.com",
|
||||||
"connect_timeout_ms": 5000,
|
"defaults": "standard_rpc_ws",
|
||||||
"request_timeout_ms": 10000,
|
|
||||||
"unsubscribe_timeout_ms": 1500,
|
|
||||||
"write_channel_capacity": 256,
|
|
||||||
"event_channel_capacity": 1024,
|
|
||||||
"auto_reconnect": false,
|
|
||||||
"roles": [
|
"roles": [
|
||||||
{
|
{
|
||||||
"role": "slot_notifications",
|
"role": "slot_notifications",
|
||||||
@@ -793,12 +793,7 @@
|
|||||||
"provider": "helius",
|
"provider": "helius",
|
||||||
"cluster": "mainnet-beta",
|
"cluster": "mainnet-beta",
|
||||||
"url": "wss://mainnet.helius-rpc.com/?api-key=${KS_SECRET_HELIUS_API_KEY}",
|
"url": "wss://mainnet.helius-rpc.com/?api-key=${KS_SECRET_HELIUS_API_KEY}",
|
||||||
"connect_timeout_ms": 5000,
|
"defaults": "high_capacity_rpc_ws",
|
||||||
"request_timeout_ms": 10000,
|
|
||||||
"unsubscribe_timeout_ms": 1500,
|
|
||||||
"write_channel_capacity": 512,
|
|
||||||
"event_channel_capacity": 4096,
|
|
||||||
"auto_reconnect": false,
|
|
||||||
"roles": [
|
"roles": [
|
||||||
{
|
{
|
||||||
"role": "logs_subscribe",
|
"role": "logs_subscribe",
|
||||||
|
|||||||
30
config/wallet.config.json
Normal file
30
config/wallet.config.json
Normal file
@@ -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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: docs/decisions/KHADHROONY_SOLANA_NAMESPACE_POLICY.md -->
|
<!-- file: docs/decisions/KHADHROONY_SOLANA_NAMESPACE_POLICY.md -->
|
||||||
<!-- version: 7 -->
|
<!-- version: 8 -->
|
||||||
|
|
||||||
# Politique de namespace Khadhroony Solana
|
# 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_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.
|
- `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.
|
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
|
## 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/kb-app-demo-desktop.default.config.json` compose les overrides du desktop ;
|
||||||
- `config/ks-pipeline-demo-scenarios.default.config.json` compose les besoins du CLI/scénarios ;
|
- `config/logging.config.json` appartient à `ks-logging` et porte notamment `logs_directory` hors profils ;
|
||||||
- `config/logging.config.json` appartient à `ks-logging` ;
|
- `config/transport.config.json` contient les profils HTTP/WebSocket et les classes de defaults WebSocket ;
|
||||||
- `config/transport.config.json` contient les profils HTTP/WebSocket partagés ;
|
- `config/listeners.config.json` contient les profils de subscriptions Solana ;
|
||||||
- `config/listeners.config.json` contient les profils de subscriptions Solana partagés ;
|
- `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/`.
|
- 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 `<binary>.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/`.
|
`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 ;
|
1. les documents source indépendants, pouvant contenir des références `${KS_*}` ou `${KB_*}` selon le propriétaire ;
|
||||||
2. la composition propre au binaire, qui choisit les documents et profils ;
|
2. leurs valeurs globales et `default_profile` ;
|
||||||
3. la représentation runtime résolue, qui peut porter des secrets ;
|
3. l'éventuelle composition propre au binaire, qui fournit des overrides ;
|
||||||
4. la représentation publique/diagnostique, construite explicitement et incapable d'exposer une valeur secrète résolue.
|
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
|
## 6. Identités runtime et persistées
|
||||||
|
|
||||||
|
|||||||
@@ -1,50 +1,45 @@
|
|||||||
<!-- file: docs/guides/CONFIGURATION.md -->
|
<!-- file: docs/guides/CONFIGURATION.md -->
|
||||||
<!-- version: 6 -->
|
<!-- version: 7 -->
|
||||||
|
|
||||||
# Guide de configuration
|
# Guide de configuration
|
||||||
|
|
||||||
## Objectif
|
## 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/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.
|
|
||||||
|
|
||||||
Documents partagés :
|
Documents partagés :
|
||||||
|
|
||||||
- `config/logging.config.json` ;
|
- `config/logging.config.json` ;
|
||||||
- `config/transport.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 :
|
Chaque document possède un exemple `example.*.config.json`. Les schémas correspondants résident sous `config/schemas/`.
|
||||||
|
|
||||||
- `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.
|
|
||||||
|
|
||||||
`.env`, ou le fichier sélectionné par `KS_ENV_FILE`, fournit les valeurs non versionnées.
|
`.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
|
## Composition d'un binaire
|
||||||
|
|
||||||
Une composition définit :
|
Une composition définit :
|
||||||
|
|
||||||
1. son `active_profile` ;
|
1. son `active_profile` applicatif ;
|
||||||
2. les chemins des documents partagés ;
|
2. les chemins des documents partagés ;
|
||||||
3. pour chaque profil, le `logging_profile`, `transport_profile` et `listeners_profile` à sélectionner ;
|
3. les éventuels overrides de profils spécialisés ;
|
||||||
4. temporairement, les sections qui n'ont pas encore leur document spécialisé.
|
4. les paramètres propres au binaire qui ne relèvent pas d'un document partagé.
|
||||||
|
|
||||||
Exemple conceptuel :
|
Exemple conceptuel :
|
||||||
|
|
||||||
@@ -53,14 +48,23 @@ kb-app-demo-desktop.default.config.json
|
|||||||
sources.logging -> config/logging.config.json
|
sources.logging -> config/logging.config.json
|
||||||
sources.transport -> config/transport.config.json
|
sources.transport -> config/transport.config.json
|
||||||
sources.listeners -> config/listeners.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
|
profile mainnet_research
|
||||||
logging_profile -> mainnet_research
|
logging_profile -> mainnet_research
|
||||||
transport_profile -> mainnet_research
|
transport_profile -> mainnet_research
|
||||||
listeners_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
|
## Desktop
|
||||||
|
|
||||||
@@ -70,65 +74,100 @@ Le chemin de composition du desktop peut être remplacé par :
|
|||||||
KB_APP_DEMO_DESKTOP_CONFIG_PATH
|
KB_APP_DEMO_DESKTOP_CONFIG_PATH
|
||||||
```
|
```
|
||||||
|
|
||||||
Le logging conserve l'override opérationnel :
|
Le logging conserve aussi l'override opérationnel :
|
||||||
|
|
||||||
```text
|
```text
|
||||||
KS_LOGGING_CONFIG_PATH
|
KS_LOGGING_CONFIG_PATH
|
||||||
```
|
```
|
||||||
|
|
||||||
Sans cet override, le desktop utilise le chemin logging déclaré par sa composition.
|
|
||||||
|
|
||||||
## Scénarios Devnet
|
## 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
|
```text
|
||||||
KS_DEVNET_CONFIG_PATH
|
KS_DEVNET_CONFIG_PATH # composition explicite facultative
|
||||||
KS_DEVNET_PROFILE
|
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 ;
|
## Wallet et politique d'exécution
|
||||||
- `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.
|
|
||||||
|
|
||||||
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
|
## Invariants
|
||||||
|
|
||||||
- aucun secret réel dans les fichiers versionnés ;
|
- aucun secret réel dans les fichiers versionnés ;
|
||||||
- tous les schémas actifs sous `config/schemas/` ;
|
- tous les schémas actifs sous `config/schemas/` ;
|
||||||
- aucun retour au fichier monolithique `app.config.json` ;
|
- 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 ;
|
- 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.
|
||||||
|
|||||||
@@ -1,88 +1,49 @@
|
|||||||
<!-- file: docs/guides/LOGGING.md -->
|
<!-- file: docs/guides/LOGGING.md -->
|
||||||
<!-- version: 6 -->
|
<!-- version: 7 -->
|
||||||
|
|
||||||
# Guide de logging et tracing
|
# Guide de logging et tracing
|
||||||
|
|
||||||
## Objectif
|
## 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` ;
|
La racine des logs n'est pas un profil :
|
||||||
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 `<binary>.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 :
|
|
||||||
|
|
||||||
```text
|
```text
|
||||||
ks-pipeline.backfill
|
logging.config.json.logs_directory = ${KS_LOGS_DIRECTORY:-logs}
|
||||||
ks-pipeline.decode-replay
|
|
||||||
ks-onchain-transport.http
|
|
||||||
ks-lib-executor.spl.token-2022
|
|
||||||
ks-lib-materializer.compliance.audit
|
|
||||||
ks-lib-materializer.token.accounts
|
|
||||||
```
|
```
|
||||||
|
|
||||||
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é ;
|
## Flux de démarrage desktop
|
||||||
- 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.
|
|
||||||
|
|
||||||
## 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_CONFIG_PATH` remplace le chemin du document et `KS_LOGS_DIRECTORY` remplace uniquement sa racine de sortie.
|
||||||
- `ks-logging/USAGE.md` ;
|
|
||||||
- `config/README.md` ;
|
## Sécurité
|
||||||
- `docs/OPERATION_NAMING_CONVENTION.md`.
|
|
||||||
|
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`.
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
<!-- file: docs/plans/V0_5_1_KHADHROONY_SOLANA_NAMESPACE_AND_CONFIG_PLAN.md -->
|
<!-- file: docs/plans/V0_5_1_KHADHROONY_SOLANA_NAMESPACE_AND_CONFIG_PLAN.md -->
|
||||||
<!-- version: 6 -->
|
<!-- version: 7 -->
|
||||||
|
|
||||||
# Plan temporaire `0.5.1` — namespace Khadhroony Solana et configuration sûre
|
# Plan temporaire `0.5.1` — namespace Khadhroony Solana et configuration sûre
|
||||||
|
|
||||||
## 1. Statut et règle de cette prerelease
|
## 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
|
## 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
|
## 4. Inventaire des crates et ordre de migration
|
||||||
|
|
||||||
| Package actuel | Identifiant Rust actuel | Package cible | Identifiant Rust cible | Dépendances workspace directes actuelles | Ordre |
|
| 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-core` | `kb_core` | `ks-core` | `ks_core` | — | 1 |
|
||||||
| `kb-program-ids` | `kb_program_ids` | `ks-program-ids` | `ks_program_ids` | — | 2 |
|
| `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-config` | `kb_config` | `ks-config` | `ks_config` | kb-core | 3 |
|
||||||
| `kb-logging` | `kb_logging` | `ks-logging` | `ks_logging` | kb-core | 4 |
|
| `kb-logging` | `kb_logging` | `ks-logging` | `ks_logging` | kb-core | 4 |
|
||||||
| `kb-wallet` | `kb_wallet` | `ks-wallet` | `ks_wallet` | kb-core | 5 |
|
| `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-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-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-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` | `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-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.
|
`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.
|
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 |
|
| Crate | Références package / fichiers | Références identifiant Rust / fichiers |
|
||||||
|------------------------------|------------------------------:|---------------------------------------:|
|
|---|---:|---:|
|
||||||
| `kb-core` | 51 / 32 | 3165 / 407 |
|
| `kb-core` | 51 / 32 | 3165 / 407 |
|
||||||
| `kb-config` | 56 / 33 | 201 / 44 |
|
| `kb-config` | 56 / 33 | 201 / 44 |
|
||||||
| `kb-lib` | 3145 / 550 | 2785 / 97 |
|
| `kb-lib` | 3145 / 550 | 2785 / 97 |
|
||||||
| `kb-logging` | 72 / 27 | 22 / 3 |
|
| `kb-logging` | 72 / 27 | 22 / 3 |
|
||||||
| `kb-program-ids` | 42 / 28 | 1413 / 334 |
|
| `kb-program-ids` | 42 / 28 | 1413 / 334 |
|
||||||
| `kb-pipeline` | 287 / 112 | 436 / 38 |
|
| `kb-pipeline` | 287 / 112 | 436 / 38 |
|
||||||
| `kb-pipeline-demo-scenarios` | 145 / 75 | 173 / 18 |
|
| `kb-pipeline-demo-scenarios` | 145 / 75 | 173 / 18 |
|
||||||
| `kb-onchain-transport` | 125 / 56 | 530 / 50 |
|
| `kb-onchain-transport` | 125 / 56 | 530 / 50 |
|
||||||
| `kb-store` | 157 / 81 | 344 / 30 |
|
| `kb-store` | 157 / 81 | 344 / 30 |
|
||||||
| `kb-wallet` | 75 / 30 | 53 / 17 |
|
| `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.
|
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.*`
|
### 6.1 Inventaire exhaustif des identités `kb-lib.*`
|
||||||
|
|
||||||
| Identité actuelle | Identité cible |
|
| Identité actuelle | Identité cible |
|
||||||
|----------------------------------------------------------------|----------------------------------------------------------------|
|
|---|---|
|
||||||
| `kb-lib.decoder.adapter.saber_decimal_wrapper` | `ks-lib-decoder.adapter.saber_decimal_wrapper` |
|
| `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.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.jupiter_lock` | `ks-lib-decoder.admin.jupiter_lock` |
|
||||||
| `kb-lib.decoder.admin.pump_fees` | `ks-lib-decoder.admin.pump_fees` |
|
| `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_v1` | `ks-lib-decoder.amm.aldrin_v1` |
|
||||||
| `kb-lib.decoder.amm.aldrin_v2` | `ks-lib-decoder.amm.aldrin_v2` |
|
| `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.alphaq` | `ks-lib-decoder.amm.alphaq` |
|
||||||
| `kb-lib.decoder.amm.believe` | `ks-lib-decoder.amm.believe` |
|
| `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.bonk_swap` | `ks-lib-decoder.amm.bonk_swap` |
|
||||||
| `kb-lib.decoder.amm.fluxbeam` | `ks-lib-decoder.amm.fluxbeam` |
|
| `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.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_gamma` | `ks-lib-decoder.amm.goosefx_gamma` |
|
||||||
| `kb-lib.decoder.amm.goosefx_v2` | `ks-lib-decoder.amm.goosefx_v2` |
|
| `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.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.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_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.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_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.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.obric_v2` | `ks-lib-decoder.amm.obric_v2` |
|
||||||
| `kb-lib.decoder.amm.one_dex` | `ks-lib-decoder.amm.one_dex` |
|
| `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.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.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` | `ks-lib-decoder.amm.solfi` |
|
||||||
| `kb-lib.decoder.amm.solfi_v2` | `ks-lib-decoder.amm.solfi_v2` |
|
| `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.vertigo` | `ks-lib-decoder.amm.vertigo` |
|
||||||
| `kb-lib.decoder.amm.virtuals` | `ks-lib-decoder.amm.virtuals` |
|
| `kb-lib.decoder.amm.virtuals` | `ks-lib-decoder.amm.virtuals` |
|
||||||
| `kb-lib.decoder.amm.woofi` | `ks-lib-decoder.amm.woofi` |
|
| `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.zero_fi` | `ks-lib-decoder.amm.zero_fi` |
|
||||||
| `kb-lib.decoder.amm.zora` | `ks-lib-decoder.amm.zora` |
|
| `kb-lib.decoder.amm.zora` | `ks-lib-decoder.amm.zora` |
|
||||||
| `kb-lib.decoder.anchor` | `ks-lib-decoder.anchor` |
|
| `kb-lib.decoder.anchor` | `ks-lib-decoder.anchor` |
|
||||||
| `kb-lib.decoder.api` | `ks-lib-decoder.api` |
|
| `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` | `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.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_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.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.byreal` | `ks-lib-decoder.clmm.byreal` |
|
||||||
| `kb-lib.decoder.clmm.fusion` | `ks-lib-decoder.clmm.fusion` |
|
| `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.orca_whirlpool` | `ks-lib-decoder.clmm.orca_whirlpool` |
|
||||||
| `kb-lib.decoder.clmm.pancake_swap` | `ks-lib-decoder.clmm.pancake_swap` |
|
| `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.raydium` | `ks-lib-decoder.clmm.raydium` |
|
||||||
| `kb-lib.decoder.clmm.stabble` | `ks-lib-decoder.clmm.stabble` |
|
| `kb-lib.decoder.clmm.stabble` | `ks-lib-decoder.clmm.stabble` |
|
||||||
| `kb-lib.decoder.cpmm.raydium` | `ks-lib-decoder.cpmm.raydium` |
|
| `kb-lib.decoder.cpmm.raydium` | `ks-lib-decoder.cpmm.raydium` |
|
||||||
| `kb-lib.decoder.dlmm.meteora` | `ks-lib-decoder.dlmm.meteora` |
|
| `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_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.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.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_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.metadao_futarchy` | `ks-lib-decoder.governance.metadao_futarchy` |
|
||||||
| `kb-lib.decoder.governance.squads_multisig` | `ks-lib-decoder.governance.squads_multisig` |
|
| `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.boop_fun` | `ks-lib-decoder.launchpad.boop_fun` |
|
||||||
| `kb-lib.decoder.launchpad.metadao_ico` | `ks-lib-decoder.launchpad.metadao_ico` |
|
| `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.meteora_dbc` | `ks-lib-decoder.launchpad.meteora_dbc` |
|
||||||
| `kb-lib.decoder.launchpad.moonit` | `ks-lib-decoder.launchpad.moonit` |
|
| `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.orca_wavebreak` | `ks-lib-decoder.launchpad.orca_wavebreak` |
|
||||||
| `kb-lib.decoder.launchpad.printr` | `ks-lib-decoder.launchpad.printr` |
|
| `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_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.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.raydium_launchlab` | `ks-lib-decoder.launchpad.raydium_launchlab` |
|
||||||
| `kb-lib.decoder.launchpad.virtuals` | `ks-lib-decoder.launchpad.virtuals` |
|
| `kb-lib.decoder.launchpad.virtuals` | `ks-lib-decoder.launchpad.virtuals` |
|
||||||
| `kb-lib.decoder.lending.clone` | `ks-lib-decoder.lending.clone` |
|
| `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_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_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_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.jupiter_lend_liquidity` | `ks-lib-decoder.lending.jupiter_lend_liquidity` |
|
||||||
| `kb-lib.decoder.lending.kamino` | `ks-lib-decoder.lending.kamino` |
|
| `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.lending.marginfi_v2` | `ks-lib-decoder.lending.marginfi_v2` |
|
||||||
| `kb-lib.decoder.lock.raydium_lp` | `ks-lib-decoder.lock.raydium_lp` |
|
| `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.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.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.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.metaplex_bubblegum` | `ks-lib-decoder.nft.metaplex_bubblegum` |
|
||||||
| `kb-lib.decoder.nft.tensor_cnft` | `ks-lib-decoder.nft.tensor_cnft` |
|
| `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` | `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.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.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.drift_v2` | `ks-lib-decoder.perpetuals.drift_v2` |
|
||||||
| `kb-lib.decoder.perpetuals.jupiter` | `ks-lib-decoder.perpetuals.jupiter` |
|
| `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.phoenix_eternal` | `ks-lib-decoder.perpetuals.phoenix_eternal` |
|
||||||
| `kb-lib.decoder.perpetuals.zeta` | `ks-lib-decoder.perpetuals.zeta` |
|
| `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.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_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_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.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_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.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.rwa.ondo_global_markets` | `ks-lib-decoder.rwa.ondo_global_markets` |
|
||||||
| `kb-lib.decoder.solana.core` | `ks-lib-decoder.solana.core` |
|
| `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.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.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.elgamal_registry` | `ks-lib-decoder.spl.elgamal_registry` |
|
||||||
| `kb-lib.decoder.spl.memo` | `ks-lib-decoder.spl.memo` |
|
| `kb-lib.decoder.spl.memo` | `ks-lib-decoder.spl.memo` |
|
||||||
| `kb-lib.decoder.spl.noop` | `ks-lib-decoder.spl.noop` |
|
| `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.single_pool` | `ks-lib-decoder.spl.single_pool` |
|
||||||
| `kb-lib.decoder.spl.stake_pool` | `ks-lib-decoder.spl.stake_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` | `ks-lib-decoder.spl.token` |
|
||||||
| `kb-lib.decoder.spl.token_2022` | `ks-lib-decoder.spl.token_2022` |
|
| `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_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_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_numeraire` | `ks-lib-decoder.stable.swap_numeraire` |
|
||||||
| `kb-lib.decoder.stable.swap_stabble` | `ks-lib-decoder.stable.swap_stabble` |
|
| `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.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.kamino_farm` | `ks-lib-decoder.staking.kamino_farm` |
|
||||||
| `kb-lib.decoder.staking.marinade_finance` | `ks-lib-decoder.staking.marinade_finance` |
|
| `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.staking.solayer` | `ks-lib-decoder.staking.solayer` |
|
||||||
| `kb-lib.decoder.storage.solana_record` | `ks-lib-decoder.storage.solana_record` |
|
| `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.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.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.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.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` | `ks-lib-decoder.vault.kamino` |
|
||||||
| `kb-lib.decoder.vault.kamino_v2` | `ks-lib-decoder.vault.kamino_v2` |
|
| `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.kamino_yvaults` | `ks-lib-decoder.vault.kamino_yvaults` |
|
||||||
| `kb-lib.decoder.vault.meteora` | `ks-lib-decoder.vault.meteora` |
|
| `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.jupiter_lock` | `ks-lib-decoder.vesting.jupiter_lock` |
|
||||||
| `kb-lib.decoder.vesting.streamflow` | `ks-lib-decoder.vesting.streamflow` |
|
| `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.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.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.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.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_v1` | `ks-lib-executor.amm.aldrin_v1` |
|
||||||
| `kb-lib.executor.amm.aldrin_v2` | `ks-lib-executor.amm.aldrin_v2` |
|
| `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.alphaq` | `ks-lib-executor.amm.alphaq` |
|
||||||
| `kb-lib.executor.amm.believe` | `ks-lib-executor.amm.believe` |
|
| `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.bonk_swap` | `ks-lib-executor.amm.bonk_swap` |
|
||||||
| `kb-lib.executor.amm.fluxbeam` | `ks-lib-executor.amm.fluxbeam` |
|
| `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.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_gamma` | `ks-lib-executor.amm.goosefx_gamma` |
|
||||||
| `kb-lib.executor.amm.goosefx_v2` | `ks-lib-executor.amm.goosefx_v2` |
|
| `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.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.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.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_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.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.obric_v2` | `ks-lib-executor.amm.obric_v2` |
|
||||||
| `kb-lib.executor.amm.one_dex` | `ks-lib-executor.amm.one_dex` |
|
| `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.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.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` | `ks-lib-executor.amm.solfi` |
|
||||||
| `kb-lib.executor.amm.solfi_v2` | `ks-lib-executor.amm.solfi_v2` |
|
| `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.vertigo` | `ks-lib-executor.amm.vertigo` |
|
||||||
| `kb-lib.executor.amm.woofi` | `ks-lib-executor.amm.woofi` |
|
| `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.zero_fi` | `ks-lib-executor.amm.zero_fi` |
|
||||||
| `kb-lib.executor.amm.zora` | `ks-lib-executor.amm.zora` |
|
| `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` | `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.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_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.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.byreal` | `ks-lib-executor.clmm.byreal` |
|
||||||
| `kb-lib.executor.clmm.fusion` | `ks-lib-executor.clmm.fusion` |
|
| `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.orca_whirlpool` | `ks-lib-executor.clmm.orca_whirlpool` |
|
||||||
| `kb-lib.executor.clmm.pancake_swap` | `ks-lib-executor.clmm.pancake_swap` |
|
| `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.raydium` | `ks-lib-executor.clmm.raydium` |
|
||||||
| `kb-lib.executor.clmm.stabble` | `ks-lib-executor.clmm.stabble` |
|
| `kb-lib.executor.clmm.stabble` | `ks-lib-executor.clmm.stabble` |
|
||||||
| `kb-lib.executor.cpmm.raydium` | `ks-lib-executor.cpmm.raydium` |
|
| `kb-lib.executor.cpmm.raydium` | `ks-lib-executor.cpmm.raydium` |
|
||||||
| `kb-lib.executor.dlmm.meteora` | `ks-lib-executor.dlmm.meteora` |
|
| `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_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.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.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_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.metadao_futarchy` | `ks-lib-executor.governance.metadao_futarchy` |
|
||||||
| `kb-lib.executor.governance.squads_multisig` | `ks-lib-executor.governance.squads_multisig` |
|
| `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.boop_fun` | `ks-lib-executor.launchpad.boop_fun` |
|
||||||
| `kb-lib.executor.launchpad.metadao_ico` | `ks-lib-executor.launchpad.metadao_ico` |
|
| `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.meteora_dbc` | `ks-lib-executor.launchpad.meteora_dbc` |
|
||||||
| `kb-lib.executor.launchpad.moonit` | `ks-lib-executor.launchpad.moonit` |
|
| `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.orca_wavebreak` | `ks-lib-executor.launchpad.orca_wavebreak` |
|
||||||
| `kb-lib.executor.launchpad.printr` | `ks-lib-executor.launchpad.printr` |
|
| `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_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.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.raydium_launchlab` | `ks-lib-executor.launchpad.raydium_launchlab` |
|
||||||
| `kb-lib.executor.launchpad.virtuals` | `ks-lib-executor.launchpad.virtuals` |
|
| `kb-lib.executor.launchpad.virtuals` | `ks-lib-executor.launchpad.virtuals` |
|
||||||
| `kb-lib.executor.lending.clone` | `ks-lib-executor.lending.clone` |
|
| `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_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_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_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.jupiter_lend_liquidity` | `ks-lib-executor.lending.jupiter_lend_liquidity` |
|
||||||
| `kb-lib.executor.lending.kamino` | `ks-lib-executor.lending.kamino` |
|
| `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.lending.marginfi_v2` | `ks-lib-executor.lending.marginfi_v2` |
|
||||||
| `kb-lib.executor.lock.raydium_lp` | `ks-lib-executor.lock.raydium_lp` |
|
| `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.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.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.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.metaplex_bubblegum` | `ks-lib-executor.nft.metaplex_bubblegum` |
|
||||||
| `kb-lib.executor.nft.tensor_cnft` | `ks-lib-executor.nft.tensor_cnft` |
|
| `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` | `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.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.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.drift_v2` | `ks-lib-executor.perpetuals.drift_v2` |
|
||||||
| `kb-lib.executor.perpetuals.jupiter` | `ks-lib-executor.perpetuals.jupiter` |
|
| `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.phoenix_eternal` | `ks-lib-executor.perpetuals.phoenix_eternal` |
|
||||||
| `kb-lib.executor.perpetuals.zeta` | `ks-lib-executor.perpetuals.zeta` |
|
| `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.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_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.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_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.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.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.core` | `ks-lib-executor.solana.core` |
|
||||||
| `kb-lib.executor.solana.transaction` | `ks-lib-executor.solana.transaction` |
|
| `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.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.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.elgamal_registry` | `ks-lib-executor.spl.elgamal_registry` |
|
||||||
| `kb-lib.executor.spl.memo` | `ks-lib-executor.spl.memo` |
|
| `kb-lib.executor.spl.memo` | `ks-lib-executor.spl.memo` |
|
||||||
| `kb-lib.executor.spl.noop` | `ks-lib-executor.spl.noop` |
|
| `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.single_pool` | `ks-lib-executor.spl.single_pool` |
|
||||||
| `kb-lib.executor.spl.stake_pool` | `ks-lib-executor.spl.stake_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` | `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.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_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_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_numeraire` | `ks-lib-executor.stable.swap_numeraire` |
|
||||||
| `kb-lib.executor.stable.swap_stabble` | `ks-lib-executor.stable.swap_stabble` |
|
| `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.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.kamino_farm` | `ks-lib-executor.staking.kamino_farm` |
|
||||||
| `kb-lib.executor.staking.marinade_finance` | `ks-lib-executor.staking.marinade_finance` |
|
| `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.staking.solayer` | `ks-lib-executor.staking.solayer` |
|
||||||
| `kb-lib.executor.storage.solana_record` | `ks-lib-executor.storage.solana_record` |
|
| `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.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.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.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.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` | `ks-lib-executor.vault.kamino` |
|
||||||
| `kb-lib.executor.vault.kamino_v2` | `ks-lib-executor.vault.kamino_v2` |
|
| `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.kamino_yvaults` | `ks-lib-executor.vault.kamino_yvaults` |
|
||||||
| `kb-lib.executor.vault.meteora` | `ks-lib-executor.vault.meteora` |
|
| `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.jupiter_lock` | `ks-lib-executor.vesting.jupiter_lock` |
|
||||||
| `kb-lib.executor.vesting.streamflow` | `ks-lib-executor.vesting.streamflow` |
|
| `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.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.executor.weighted.swap_stabble` | `ks-lib-executor.weighted.swap_stabble` |
|
||||||
| `kb-lib.materializer.admin` | `ks-lib-materializer.admin` |
|
| `kb-lib.materializer.admin` | `ks-lib-materializer.admin` |
|
||||||
| `kb-lib.materializer.bridge` | `ks-lib-materializer.bridge` |
|
| `kb-lib.materializer.bridge` | `ks-lib-materializer.bridge` |
|
||||||
| `kb-lib.materializer.compliance.audit` | `ks-lib-materializer.compliance.audit` |
|
| `kb-lib.materializer.compliance.audit` | `ks-lib-materializer.compliance.audit` |
|
||||||
| `kb-lib.materializer.fees` | `ks-lib-materializer.fees` |
|
| `kb-lib.materializer.fees` | `ks-lib-materializer.fees` |
|
||||||
| `kb-lib.materializer.governance` | `ks-lib-materializer.governance` |
|
| `kb-lib.materializer.governance` | `ks-lib-materializer.governance` |
|
||||||
| `kb-lib.materializer.lending` | `ks-lib-materializer.lending` |
|
| `kb-lib.materializer.lending` | `ks-lib-materializer.lending` |
|
||||||
| `kb-lib.materializer.lifecycle` | `ks-lib-materializer.lifecycle` |
|
| `kb-lib.materializer.lifecycle` | `ks-lib-materializer.lifecycle` |
|
||||||
| `kb-lib.materializer.liquidity` | `ks-lib-materializer.liquidity` |
|
| `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.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.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.metadata.token_2022` | `ks-lib-materializer.metadata.token_2022` |
|
||||||
| `kb-lib.materializer.nft` | `ks-lib-materializer.nft` |
|
| `kb-lib.materializer.nft` | `ks-lib-materializer.nft` |
|
||||||
| `kb-lib.materializer.oracle` | `ks-lib-materializer.oracle` |
|
| `kb-lib.materializer.oracle` | `ks-lib-materializer.oracle` |
|
||||||
| `kb-lib.materializer.orderbook` | `ks-lib-materializer.orderbook` |
|
| `kb-lib.materializer.orderbook` | `ks-lib-materializer.orderbook` |
|
||||||
| `kb-lib.materializer.perpetuals` | `ks-lib-materializer.perpetuals` |
|
| `kb-lib.materializer.perpetuals` | `ks-lib-materializer.perpetuals` |
|
||||||
| `kb-lib.materializer.pool.state` | `ks-lib-materializer.pool.state` |
|
| `kb-lib.materializer.pool.state` | `ks-lib-materializer.pool.state` |
|
||||||
| `kb-lib.materializer.rewards` | `ks-lib-materializer.rewards` |
|
| `kb-lib.materializer.rewards` | `ks-lib-materializer.rewards` |
|
||||||
| `kb-lib.materializer.risk` | `ks-lib-materializer.risk` |
|
| `kb-lib.materializer.risk` | `ks-lib-materializer.risk` |
|
||||||
| `kb-lib.materializer.routing` | `ks-lib-materializer.routing` |
|
| `kb-lib.materializer.routing` | `ks-lib-materializer.routing` |
|
||||||
| `kb-lib.materializer.staking` | `ks-lib-materializer.staking` |
|
| `kb-lib.materializer.staking` | `ks-lib-materializer.staking` |
|
||||||
| `kb-lib.materializer.token.accounts` | `ks-lib-materializer.token.accounts` |
|
| `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.token.metadata_risk` | `ks-lib-materializer.token.metadata_risk` |
|
||||||
| `kb-lib.materializer.trades` | `ks-lib-materializer.trades` |
|
| `kb-lib.materializer.trades` | `ks-lib-materializer.trades` |
|
||||||
| `kb-lib.materializer.transaction.annotations` | `ks-lib-materializer.transaction.annotations` |
|
| `kb-lib.materializer.transaction.annotations` | `ks-lib-materializer.transaction.annotations` |
|
||||||
| `kb-lib.materializer.vault` | `ks-lib-materializer.vault` |
|
| `kb-lib.materializer.vault` | `ks-lib-materializer.vault` |
|
||||||
|
|
||||||
## 7. Inventaire des variables d'environnement
|
## 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
|
### 7.2 Mapping code/config/tests — 85 noms
|
||||||
|
|
||||||
| Nom actuel | Nom cible | Classe |
|
| Nom actuel | Nom cible | Classe |
|
||||||
|------------------------------|-----------------------------------|----------------|
|
|---|---|---|
|
||||||
| `HELIUS_API_KEY` | `KS_SECRET_HELIUS_API_KEY` | `secret` |
|
| `HELIUS_API_KEY` | `KS_SECRET_HELIUS_API_KEY` | `secret` |
|
||||||
| `KB_CONFIG_PATH` | `KB_APP_DEMO_DESKTOP_CONFIG_PATH` | `internal` Bot |
|
| `KB_CONFIG_PATH` | `KB_APP_DEMO_DESKTOP_CONFIG_PATH` | `internal` Bot |
|
||||||
| `KB_CONFIG_TEST_MISSING` | `KS_CONFIG_TEST_MISSING` | `internal` |
|
| `KB_CONFIG_TEST_MISSING` | `KS_CONFIG_TEST_MISSING` | `internal` |
|
||||||
| `KB_DEVNET_AIRDROP_LAMPORTS` | `KS_DEVNET_AIRDROP_LAMPORTS` | `internal` |
|
| `KB_DEVNET_AIRDROP_LAMPORTS` | `KS_DEVNET_AIRDROP_LAMPORTS` | `internal` |
|
||||||
| `KB_DEVNET_CONFIG_PATH` | `KS_DEVNET_CONFIG_PATH` | `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-*`.
|
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` |
|
| `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
|
### 7.3 Mapping guide opérateur Devnet — 17 noms
|
||||||
|
|
||||||
| Nom actuel | Nom cible | Classe |
|
| Nom actuel | Nom cible | Classe |
|
||||||
|---------------------------------------|---------------------------------------|------------|
|
|---|---|---|
|
||||||
| `KB_CONFIRM_DEVNET_DATABASE_RESET` | `KS_CONFIRM_DEVNET_DATABASE_RESET` | `internal` |
|
| `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_DESTINATION_ATA` | `KS_DEVNET_CLASSIC_DESTINATION_ATA` | `internal` |
|
||||||
| `KB_DEVNET_CLASSIC_MINT` | `KS_DEVNET_CLASSIC_MINT` | `internal` |
|
| `KB_DEVNET_CLASSIC_MINT` | `KS_DEVNET_CLASSIC_MINT` | `internal` |
|
||||||
| `KB_DEVNET_CLASSIC_MINT_KEYPAIR` | `KS_DEVNET_CLASSIC_MINT_KEYPAIR` | `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` | `KS_DEVNET_CLASSIC_RECIPIENT` | `internal` |
|
||||||
| `KB_DEVNET_CLASSIC_RECIPIENT_KEYPAIR` | `KS_DEVNET_CLASSIC_RECIPIENT_KEYPAIR` | `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_CLASSIC_SOURCE_ATA` | `KS_DEVNET_CLASSIC_SOURCE_ATA` | `internal` |
|
||||||
| `KB_DEVNET_RECIPIENT` | `KS_DEVNET_RECIPIENT` | `internal` |
|
| `KB_DEVNET_RECIPIENT` | `KS_DEVNET_RECIPIENT` | `internal` |
|
||||||
| `KB_DEVNET_SCENARIO` | `KS_DEVNET_SCENARIO` | `internal` |
|
| `KB_DEVNET_SCENARIO` | `KS_DEVNET_SCENARIO` | `internal` |
|
||||||
| `KB_DEVNET_SIGNATURE` | `KS_DEVNET_SIGNATURE` | `internal` |
|
| `KB_DEVNET_SIGNATURE` | `KS_DEVNET_SIGNATURE` | `internal` |
|
||||||
| `KB_DEVNET_TOKEN_2022_FIXTURE` | `KS_DEVNET_TOKEN_2022_FIXTURE` | `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` | `KS_PUBLIC_DEVNET_TOKEN_2022_MINT` | `public` |
|
||||||
| `KB_DEVNET_TOKEN_2022_MINT_KEYPAIR` | `KS_DEVNET_TOKEN_2022_MINT_KEYPAIR` | `internal` |
|
| `KB_DEVNET_TOKEN_2022_MINT_KEYPAIR` | `KS_DEVNET_TOKEN_2022_MINT_KEYPAIR` | `internal` |
|
||||||
| `KB_DEVNET_VALIDATION_DIR` | `KS_DEVNET_VALIDATION_DIR` | `internal` |
|
| `KB_DEVNET_VALIDATION_DIR` | `KS_DEVNET_VALIDATION_DIR` | `internal` |
|
||||||
| `KB_DEVNET_WALLET_PUBKEY` | `KS_PUBLIC_DEVNET_WALLET_ADDRESS` | `public` |
|
| `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_2022_PROGRAM_ID` | `KS_PUBLIC_SPL_TOKEN_2022_PROGRAM_ID` | `public` |
|
||||||
| `KB_SPL_TOKEN_PROGRAM_ID` | `KS_PUBLIC_SPL_TOKEN_PROGRAM_ID` | `public` |
|
| `KB_SPL_TOKEN_PROGRAM_ID` | `KS_PUBLIC_SPL_TOKEN_PROGRAM_ID` | `public` |
|
||||||
|
|
||||||
## 8. Décomposition des documents de configuration
|
## 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 `<binary>.default.config.json` n'existe que lorsqu'un binaire doit remplacer des fichiers/profils ou porter des paramètres propres à l'application.
|
||||||
|
|
||||||
```text
|
Les documents partagés à l'issue de `pre.007` sont :
|
||||||
config/<binary>.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 :
|
|
||||||
|
|
||||||
```text
|
```text
|
||||||
config/logging.config.json
|
config/logging.config.json
|
||||||
config/transport.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
|
||||||
```
|
```
|
||||||
|
|
||||||
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
|
## 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_*` ;
|
- 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 ;
|
- 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 ;
|
- 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 ;
|
- 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'indépendance des profils généralistes et logging ;
|
||||||
- l'absence de duplication structurelle des types logging entre `ks-config` et `ks-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
|
### `0.5.1-pre.006` — composition binaire + transport + listeners
|
||||||
|
|
||||||
- **implémenté** : remplacement du rôle de `app.config.json` par des compositions `<binary>.default.config.json` ;
|
- **implémenté** : remplacement du rôle de `app.config.json` par des compositions `<binary>.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 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é** : 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 ;
|
- **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 ;
|
- **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.
|
- 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` ;
|
- **implémenté** : extraction de `database` vers `store.config.json` ;
|
||||||
- supprimer `DataConfig` en réaffectant `logs_directory` au document logging et `wallets_directory` au document wallet selon leur ownership réel ;
|
- **implémenté** : suppression de `DataConfig`, avec `logs_directory` global dans logging et `wallets_directory` global dans wallet ;
|
||||||
- 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 ;
|
- **implémenté** : extraction du stockage/alias/persistance wallet vers `wallet.config.json` avec chemins relatifs à la racine globale ;
|
||||||
- sortir les paramètres identité/stockage temporaire du wallet vers un document wallet et préparer le contrat `0.5.2` ;
|
- **implémenté** : déplacement de `localnet_send_enabled`, `devnet_send_enabled`, `testnet_send_enabled` et `mainnet_send_enabled` vers `execution.config.json` ;
|
||||||
- déplacer `localnet_send_enabled`, `devnet_send_enabled`, `testnet_send_enabled` et `mainnet_send_enabled` hors du wallet vers la politique d'exécution ;
|
- **implémenté** : extraction complète des politiques d'exécution vers `execution.config.json` ;
|
||||||
- extraire les politiques d'exécution vers un document indépendant ;
|
- **implémenté** : déplacement de `auto_reconnect` vers des classes de defaults WebSocket nommées, avec overrides par endpoint ;
|
||||||
- décider si `app.auto_reconnect_default`, actuellement conservé pour compatibilité, appartient au transport ou doit être supprimé avec migration explicite ;
|
- **implémenté** : remplacement des `active_profile` des documents partagés par des `default_profile` autonomes ;
|
||||||
- sortir `demo` de `ks-config` vers une configuration réellement possédée par `kb-app-demo-desktop` ;
|
- **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` ;
|
||||||
- réduire les compositions à des références vers profils spécialisés et configuration dédiée du binaire ;
|
- **implémenté** : validation par `ks-config` de l'existence des profils référencés par une composition ;
|
||||||
- vérifier qu'un nouveau worker peut ajouter son fichier de composition/dédié sans modifier `ks-config` pour enregistrer son existence.
|
- **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 ;
|
- 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 ;
|
- 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 ;
|
- retirer `AppConfig`/`ProfileConfig` résolus du payload Tauri public ;
|
||||||
- borner les DTO TS-RS et diagnostics ;
|
- borner les DTO TS-RS et diagnostics ;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: docs/rules/RULES_SPECIFIC_KHADHROONY.md -->
|
<!-- file: docs/rules/RULES_SPECIFIC_KHADHROONY.md -->
|
||||||
<!-- version: 17 -->
|
<!-- version: 18 -->
|
||||||
|
|
||||||
# Règles spécifiques à `khadhroony-bot3`
|
# 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
|
## 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.
|
- 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 `<binary>.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`.
|
- Le nom par défaut d'une composition binaire suit `<binary>.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.
|
||||||
- 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 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`.
|
||||||
- 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.
|
- 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.
|
||||||
- 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`.
|
- 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 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.
|
- 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` 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.
|
- 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 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 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 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_*`.
|
- 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 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
|
## Ordre de développement cible
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,13 @@
|
|||||||
<!-- file: kb-app-demo-desktop/CHANGELOG.md -->
|
<!-- file: kb-app-demo-desktop/CHANGELOG.md -->
|
||||||
<!-- version: 46 -->
|
<!-- version: 47 -->
|
||||||
|
|
||||||
# CHANGELOG — kb-app-demo-desktop
|
# 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`
|
## `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` ;
|
- 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` ;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: kb-app-demo-desktop/src/app_state.rs
|
// file: kb-app-demo-desktop/src/app_state.rs
|
||||||
// version: 14
|
// version: 15
|
||||||
|
|
||||||
//! Shared Tauri application state and startup initialization.
|
//! Shared Tauri application state and startup initialization.
|
||||||
|
|
||||||
@@ -44,12 +44,8 @@ impl crate::AppState {
|
|||||||
std::result::Result::Ok(profile) => profile.clone(),
|
std::result::Result::Ok(profile) => profile.clone(),
|
||||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
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 =
|
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 app_config = composed.app_config;
|
||||||
let active_profile = match ks_config::active_profile(&app_config) {
|
let active_profile = match ks_config::active_profile(&app_config) {
|
||||||
std::result::Result::Ok(profile) => profile.clone(),
|
std::result::Result::Ok(profile) => profile.clone(),
|
||||||
@@ -62,19 +58,15 @@ impl crate::AppState {
|
|||||||
std::result::Result::Ok(config) => config,
|
std::result::Result::Ok(config) => config,
|
||||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||||
};
|
};
|
||||||
for profile in &composed.composition.profiles {
|
let logging_profile_name = ks_config::resolve_profile_selection(
|
||||||
match ks_logging::logging_profile(&logging_document, profile.logging_profile.as_str()) {
|
composition_profile.logging_profile.as_deref(),
|
||||||
std::result::Result::Ok(_) => (),
|
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),
|
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) {
|
let logging_guard = match ks_logging::init_logging(&logging_config) {
|
||||||
std::result::Result::Ok(guard) => guard,
|
std::result::Result::Ok(guard) => guard,
|
||||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: kb-app-demo-desktop/src/demo_execution_metadata_metaplex_token_metadata.rs
|
// 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.
|
//! 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::Ok(value) => value,
|
||||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
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(
|
return std::result::Result::Err(
|
||||||
"selected profile does not authorize Devnet submissions required by Metaplex fixtures"
|
"selected profile does not authorize Devnet submissions required by Metaplex fixtures"
|
||||||
.to_string(),
|
.to_string(),
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: kb-app-demo-desktop/src/demo_execution_solana_core.rs
|
// file: kb-app-demo-desktop/src/demo_execution_solana_core.rs
|
||||||
// version: 16
|
// version: 17
|
||||||
|
|
||||||
//! Tauri adapter for bounded Solana Core execution on Devnet.
|
//! 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(),
|
wallet_alias: profile.wallet.temporary_wallet_alias.clone(),
|
||||||
max_spend_lamports: profile.execution.devnet_max_spend_lamports,
|
max_spend_lamports: profile.execution.devnet_max_spend_lamports,
|
||||||
max_airdrop_lamports: profile.execution.devnet_airdrop_max_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,
|
require_operator_confirmation: profile.execution.require_operator_confirmation,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,16 @@
|
|||||||
<!-- file: ks-config/CHANGELOG.md -->
|
<!-- file: ks-config/CHANGELOG.md -->
|
||||||
<!-- version: 16 -->
|
<!-- version: 17 -->
|
||||||
|
|
||||||
# CHANGELOG — ks-config
|
# 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`
|
## `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` ;
|
- 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` ;
|
||||||
|
|||||||
@@ -1,49 +1,50 @@
|
|||||||
<!-- file: ks-config/README.md -->
|
<!-- file: ks-config/README.md -->
|
||||||
<!-- version: 9 -->
|
<!-- version: 10 -->
|
||||||
|
|
||||||
# ks-config
|
# 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
|
## Responsabilités
|
||||||
|
|
||||||
- charger l'environnement du workspace et résoudre les placeholders `${KS_*}` / `${KB_*}` ;
|
- 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/` ;
|
- 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 et profils runtime ;
|
- fournir les types partagés d'endpoints, listeners, stockage, wallet, exécution et profils runtime ;
|
||||||
- résoudre les références d'une composition vers des profils transport/listeners ;
|
- 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 ;
|
- reconstruire temporairement `AppConfig/ProfileConfig` pour les consommateurs existants ;
|
||||||
- rester indépendant de la liste des applications et workers futurs.
|
- 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 ;
|
Un futur worker pourra ajouter son propre `<binary>.default.config.json` uniquement s'il doit remplacer des fichiers/profils ou ajouter des paramètres propres au binaire.
|
||||||
- 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 `<binary>.default.config.json` sans créer une branche spécifique dans `ks-config`.
|
|
||||||
|
|
||||||
## Documents spécialisés actuels
|
## Documents spécialisés actuels
|
||||||
|
|
||||||
- `config/transport.config.json` / `config/schemas/transport.config.schema.json` ;
|
- `config/transport.config.json` / `config/schemas/transport.config.schema.json` ;
|
||||||
- `config/listeners.config.json` / `config/schemas/listeners.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/logging.config.json` reste possédé par `ks-logging` ;
|
||||||
- `config/schemas/resolved.app.config.schema.json` décrit uniquement le contrat runtime transitoire.
|
- `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
|
## API publique principale
|
||||||
|
|
||||||
- `read_composed_app_config_with_environment` ;
|
- `read_composed_app_config_with_environment` ;
|
||||||
|
- `read_default_shared_app_config_with_environment` ;
|
||||||
- `parse_composition_json`, `composition_profile`, `active_composition_profile` ;
|
- `parse_composition_json`, `composition_profile`, `active_composition_profile` ;
|
||||||
- `read_transport_json_file_with_environment`, `transport_profile` ;
|
- `read_transport_json_file_with_environment`, `transport_profile`, `default_transport_profile` ;
|
||||||
- `read_listeners_json_file_with_environment`, `listeners_profile` ;
|
- `read_listeners_json_file_with_environment`, `listeners_profile`, `default_listeners_profile` ;
|
||||||
- `compose_app_config` ;
|
- `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` ;
|
- `load_workspace_environment`, `resolve_environment_placeholders` ;
|
||||||
- `AppConfig`, `ProfileConfig` pendant la phase de compatibilité.
|
- `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-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
|
## Documents
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
<!-- file: ks-config/TODO.md -->
|
<!-- file: ks-config/TODO.md -->
|
||||||
<!-- version: 8 -->
|
<!-- version: 9 -->
|
||||||
|
|
||||||
# TODO — ks-config
|
# TODO — ks-config
|
||||||
|
|
||||||
## Série `0.5.x`
|
## 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` - 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` - 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` - 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` - 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/`.
|
- [ ] 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.
|
- [ ] 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.
|
- [ ] Documentation - maintenir guides, exemples et `.env.example` avec l’implémentation correspondante.
|
||||||
|
|||||||
@@ -1,15 +1,28 @@
|
|||||||
<!-- file: ks-config/USAGE.md -->
|
<!-- file: ks-config/USAGE.md -->
|
||||||
<!-- version: 8 -->
|
<!-- version: 9 -->
|
||||||
|
|
||||||
# Utilisation de ks-config
|
# Utilisation de ks-config
|
||||||
|
|
||||||
## Rôle de la crate
|
## 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
|
```rust
|
||||||
let composed = match ks_config::read_composed_app_config_with_environment(
|
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),
|
Err(error) => return Err(error),
|
||||||
};
|
};
|
||||||
let config = composed.app_config;
|
let config = composed.app_config;
|
||||||
let profile = match ks_config::active_profile(&config) {
|
|
||||||
Ok(value) => value,
|
|
||||||
Err(error) => return Err(error),
|
|
||||||
};
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Le chargeur :
|
Le chargeur :
|
||||||
|
|
||||||
1. charge l'environnement du workspace ;
|
1. charge l'environnement du workspace ;
|
||||||
2. valide la composition ;
|
2. valide la composition ;
|
||||||
3. résout les chemins `transport` et `listeners` déclarés par la composition ;
|
3. résout les six chemins spécialisés ;
|
||||||
4. charge et valide les deux documents spécialisés ;
|
4. charge et valide transport, listeners, store, wallet et execution ;
|
||||||
5. sélectionne les profils référencés ;
|
5. vérifie l'existence du profil logging référencé sans créer de dépendance `ks-config -> ks-logging` ;
|
||||||
6. reconstruit le contrat runtime `AppConfig/ProfileConfig` ;
|
6. applique les overrides ou les `default_profile` ;
|
||||||
7. applique les invariants métier existants.
|
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
|
```rust
|
||||||
let transport = match ks_config::read_transport_json_file_with_environment(
|
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,
|
Ok(value) => value,
|
||||||
Err(error) => return Err(error),
|
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,
|
Ok(value) => value,
|
||||||
Err(error) => return Err(error),
|
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
|
`default_listeners_profile` ou `listeners_profile` sélectionnent un ensemble de subscriptions. `resolved_listener_config` le convertit vers le contrat runtime transitoire `ListenerConfig`.
|
||||||
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),
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
`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
|
## 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.
|
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
|
```text
|
||||||
let raw = r#"{"databaseUrl":"${KS_SECRET_POSTGRES_DEVNET_URL:-postgres://localhost/ks}"}"#;
|
KS_LOGS_DIRECTORY
|
||||||
let resolved = ks_config::resolve_environment_placeholders(raw);
|
KS_WALLETS_DIRECTORY
|
||||||
assert!(resolved.contains("databaseUrl"));
|
|
||||||
```
|
```
|
||||||
|
|
||||||
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
|
## 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` ;
|
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.
|
||||||
- `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.
|
|
||||||
|
|||||||
@@ -1,10 +1,16 @@
|
|||||||
// file: ks-config/src/composition.rs
|
// file: ks-config/src/composition.rs
|
||||||
// version: 1
|
// version: 2
|
||||||
|
|
||||||
//! Binary composition configuration and resolution into the shared runtime profile contract.
|
//! Binary composition configuration and resolution into the shared runtime profile contract.
|
||||||
|
|
||||||
const COMPOSITION_JSON_SCHEMA: &str =
|
const COMPOSITION_JSON_SCHEMA: &str =
|
||||||
include_str!("../../config/schemas/composition.config.schema.json");
|
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.
|
/// Root composition document used by a binary to select shared configuration profiles.
|
||||||
#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize)]
|
#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize)]
|
||||||
@@ -26,33 +32,58 @@ pub struct CompositionConfigSources {
|
|||||||
pub transport: std::string::String,
|
pub transport: std::string::String,
|
||||||
/// Listener configuration document path relative to the workspace root unless absolute.
|
/// Listener configuration document path relative to the workspace root unless absolute.
|
||||||
pub listeners: std::string::String,
|
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)]
|
#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize)]
|
||||||
pub struct CompositionProfileConfig {
|
pub struct CompositionProfileConfig {
|
||||||
/// Composition profile code.
|
/// Composition profile code.
|
||||||
pub name: std::string::String,
|
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,
|
pub app: crate::AppSectionConfig,
|
||||||
/// Logging profile selected from the referenced logging document.
|
/// Optional logging profile override.
|
||||||
pub logging_profile: std::string::String,
|
pub logging_profile: std::option::Option<std::string::String>,
|
||||||
/// Transport profile selected from the referenced transport document.
|
/// Optional transport profile override.
|
||||||
pub transport_profile: std::string::String,
|
pub transport_profile: std::option::Option<std::string::String>,
|
||||||
/// Listener profile selected from the referenced listener document.
|
/// Optional listener profile override.
|
||||||
pub listeners_profile: std::string::String,
|
pub listeners_profile: std::option::Option<std::string::String>,
|
||||||
/// Database configuration retained until the store split prerelease.
|
/// Optional store profile override.
|
||||||
pub database: crate::DatabaseConfig,
|
pub store_profile: std::option::Option<std::string::String>,
|
||||||
/// Local data directories retained until their owning configuration files are split.
|
/// Optional wallet profile override.
|
||||||
pub data: crate::DataConfig,
|
pub wallet_profile: std::option::Option<std::string::String>,
|
||||||
/// Wallet configuration retained until the wallet split prerelease.
|
/// Optional execution profile override.
|
||||||
pub wallet: crate::WalletConfig,
|
pub execution_profile: std::option::Option<std::string::String>,
|
||||||
/// Execution policy retained until the execution split prerelease.
|
/// Desktop demo flags retained until the application-specific split is completed.
|
||||||
pub execution: crate::ExecutionConfig,
|
|
||||||
/// Desktop demo flags retained until the binary-specific split prerelease.
|
|
||||||
pub demo: crate::DemoConfig,
|
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.
|
/// Returns the embedded binary composition JSON Schema text.
|
||||||
pub fn composition_json_schema_text() -> &'static str {
|
pub fn composition_json_schema_text() -> &'static str {
|
||||||
return COMPOSITION_JSON_SCHEMA;
|
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.
|
/// Parses the embedded binary composition JSON Schema into a JSON value.
|
||||||
pub fn composition_json_schema_value() -> ks_core::Result<serde_json::Value> {
|
pub fn composition_json_schema_value() -> ks_core::Result<serde_json::Value> {
|
||||||
let schema_result = serde_json::from_str::<serde_json::Value>(COMPOSITION_JSON_SCHEMA);
|
let result = serde_json::from_str::<serde_json::Value>(COMPOSITION_JSON_SCHEMA);
|
||||||
return match schema_result {
|
return match result {
|
||||||
std::result::Result::Ok(schema) => std::result::Result::Ok(schema),
|
std::result::Result::Ok(value) => std::result::Result::Ok(value),
|
||||||
std::result::Result::Err(error) => std::result::Result::Err(ks_core::Error::new(
|
std::result::Result::Err(error) => std::result::Result::Err(ks_core::Error::new(
|
||||||
"composition_config_schema_parse_failed",
|
"composition_config_schema_parse_failed",
|
||||||
error.to_string(),
|
error.to_string(),
|
||||||
@@ -73,11 +104,11 @@ pub fn composition_json_schema_value() -> ks_core::Result<serde_json::Value> {
|
|||||||
/// Validates raw binary composition JSON against its embedded schema.
|
/// Validates raw binary composition JSON against its embedded schema.
|
||||||
pub fn validate_composition_json_schema(raw_json: &str) -> ks_core::Result<()> {
|
pub fn validate_composition_json_schema(raw_json: &str) -> ks_core::Result<()> {
|
||||||
let schema = match composition_json_schema_value() {
|
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),
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||||
};
|
};
|
||||||
let instance = match serde_json::from_str::<serde_json::Value>(raw_json) {
|
let instance = match serde_json::from_str::<serde_json::Value>(raw_json) {
|
||||||
std::result::Result::Ok(instance) => instance,
|
std::result::Result::Ok(value) => value,
|
||||||
std::result::Result::Err(error) => {
|
std::result::Result::Err(error) => {
|
||||||
return std::result::Result::Err(ks_core::Error::new(
|
return std::result::Result::Err(ks_core::Error::new(
|
||||||
"composition_config_json_parse_failed",
|
"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) {
|
let validator = match jsonschema::validator_for(&schema) {
|
||||||
std::result::Result::Ok(validator) => validator,
|
std::result::Result::Ok(value) => value,
|
||||||
std::result::Result::Err(error) => {
|
std::result::Result::Err(error) => {
|
||||||
return std::result::Result::Err(ks_core::Error::new(
|
return std::result::Result::Err(ks_core::Error::new(
|
||||||
"composition_config_schema_compile_failed",
|
"composition_config_schema_compile_failed",
|
||||||
@@ -110,7 +141,7 @@ pub fn parse_composition_json(raw_json: &str) -> ks_core::Result<CompositionConf
|
|||||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||||
}
|
}
|
||||||
let document = match serde_json::from_str::<CompositionConfigDocument>(raw_json) {
|
let document = match serde_json::from_str::<CompositionConfigDocument>(raw_json) {
|
||||||
std::result::Result::Ok(document) => document,
|
std::result::Result::Ok(value) => value,
|
||||||
std::result::Result::Err(error) => {
|
std::result::Result::Err(error) => {
|
||||||
return std::result::Result::Err(ks_core::Error::new(
|
return std::result::Result::Err(ks_core::Error::new(
|
||||||
"composition_config_json_decode_failed",
|
"composition_config_json_decode_failed",
|
||||||
@@ -129,7 +160,7 @@ pub fn read_composition_json_file(
|
|||||||
path: &std::path::Path,
|
path: &std::path::Path,
|
||||||
) -> ks_core::Result<CompositionConfigDocument> {
|
) -> ks_core::Result<CompositionConfigDocument> {
|
||||||
let raw_json = match std::fs::read_to_string(path) {
|
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) => {
|
std::result::Result::Err(error) => {
|
||||||
return std::result::Result::Err(ks_core::Error::new(
|
return std::result::Result::Err(ks_core::Error::new(
|
||||||
"composition_config_file_read_failed",
|
"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),
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||||
}
|
}
|
||||||
let raw_json = match std::fs::read_to_string(path) {
|
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) => {
|
std::result::Result::Err(error) => {
|
||||||
return std::result::Result::Err(ks_core::Error::new(
|
return std::result::Result::Err(ks_core::Error::new(
|
||||||
"composition_config_file_read_failed",
|
"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(&crate::resolve_environment_placeholders(&raw_json));
|
||||||
return parse_composition_json(&resolved);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns one named binary composition profile.
|
/// Returns one named binary composition profile.
|
||||||
@@ -197,36 +227,79 @@ pub fn resolve_composition_source_path(
|
|||||||
return workspace_root.join(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(
|
pub fn compose_app_config(
|
||||||
document: &CompositionConfigDocument,
|
document: &CompositionConfigDocument,
|
||||||
transport: &crate::TransportConfigDocument,
|
transport: &crate::TransportConfigDocument,
|
||||||
listeners: &crate::ListenersConfigDocument,
|
listeners: &crate::ListenersConfigDocument,
|
||||||
|
store: &crate::StoreConfigDocument,
|
||||||
|
wallet: &crate::WalletConfigDocument,
|
||||||
|
execution: &crate::ExecutionConfigDocument,
|
||||||
) -> ks_core::Result<crate::AppConfig> {
|
) -> ks_core::Result<crate::AppConfig> {
|
||||||
let mut profiles = std::vec::Vec::<crate::ProfileConfig>::new();
|
let mut profiles = std::vec::Vec::<crate::ProfileConfig>::new();
|
||||||
for profile in &document.profiles {
|
for profile in &document.profiles {
|
||||||
let transport_profile =
|
let transport_name = resolve_profile_selection(
|
||||||
match crate::transport_profile(transport, profile.transport_profile.as_str()) {
|
profile.transport_profile.as_deref(),
|
||||||
std::result::Result::Ok(value) => value,
|
transport.default_profile.as_str(),
|
||||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
);
|
||||||
};
|
let listeners_name = resolve_profile_selection(
|
||||||
let listeners_profile =
|
profile.listeners_profile.as_deref(),
|
||||||
match crate::listeners_profile(listeners, profile.listeners_profile.as_str()) {
|
listeners.default_profile.as_str(),
|
||||||
std::result::Result::Ok(value) => value,
|
);
|
||||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
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 {
|
profiles.push(crate::ProfileConfig {
|
||||||
name: profile.name.clone(),
|
name: profile.name.clone(),
|
||||||
app: profile.app.clone(),
|
app: profile.app.clone(),
|
||||||
database: profile.database.clone(),
|
database: store_profile.database.clone(),
|
||||||
data: profile.data.clone(),
|
|
||||||
solana: crate::SolanaConfig {
|
solana: crate::SolanaConfig {
|
||||||
http_endpoints: transport_profile.http_endpoints.clone(),
|
http_endpoints: transport_profile.http_endpoints,
|
||||||
ws_endpoints: transport_profile.ws_endpoints.clone(),
|
ws_endpoints: transport_profile.ws_endpoints,
|
||||||
listeners: crate::resolved_listener_config(listeners_profile),
|
listeners: crate::resolved_listener_config(listeners_profile),
|
||||||
},
|
},
|
||||||
wallet: profile.wallet.clone(),
|
wallet: wallet_profile,
|
||||||
execution: profile.execution.clone(),
|
execution: execution_profile.config.clone(),
|
||||||
demo: profile.demo.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(
|
pub fn read_composed_app_config_with_environment(
|
||||||
composition_path: &std::path::Path,
|
composition_path: &std::path::Path,
|
||||||
workspace_root: &std::path::Path,
|
workspace_root: &std::path::Path,
|
||||||
) -> ks_core::Result<crate::ComposedAppConfig> {
|
) -> ks_core::Result<ComposedAppConfig> {
|
||||||
let composition =
|
let composition =
|
||||||
match read_composition_json_file_with_environment(composition_path, workspace_root) {
|
match read_composition_json_file_with_environment(composition_path, workspace_root) {
|
||||||
std::result::Result::Ok(value) => value,
|
std::result::Result::Ok(value) => value,
|
||||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
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 =
|
let transport_path =
|
||||||
resolve_composition_source_path(workspace_root, composition.sources.transport.as_str());
|
resolve_composition_source_path(workspace_root, composition.sources.transport.as_str());
|
||||||
let listeners_path =
|
let listeners_path =
|
||||||
resolve_composition_source_path(workspace_root, composition.sources.listeners.as_str());
|
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 =
|
let transport =
|
||||||
match crate::read_transport_json_file_with_environment(&transport_path, workspace_root) {
|
match crate::read_transport_json_file_with_environment(&transport_path, workspace_root) {
|
||||||
std::result::Result::Ok(value) => value,
|
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::Ok(value) => value,
|
||||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
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::Ok(value) => value,
|
||||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
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,
|
composition,
|
||||||
app_config,
|
app_config,
|
||||||
|
logging_path,
|
||||||
transport_path,
|
transport_path,
|
||||||
listeners_path,
|
listeners_path,
|
||||||
|
store_path,
|
||||||
|
wallet_path,
|
||||||
|
execution_path,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resolved application configuration and the source documents needed by a binary.
|
/// Loads canonical shared documents and composes their independent default profiles without an application composition file.
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
pub fn read_default_shared_app_config_with_environment(
|
||||||
pub struct ComposedAppConfig {
|
workspace_root: &std::path::Path,
|
||||||
/// Parsed binary composition document.
|
) -> ks_core::Result<crate::AppConfig> {
|
||||||
pub composition: CompositionConfigDocument,
|
let transport_path = workspace_root.join(DEFAULT_TRANSPORT_CONFIG_PATH);
|
||||||
/// Resolved runtime application configuration.
|
let listeners_path = workspace_root.join(DEFAULT_LISTENERS_CONFIG_PATH);
|
||||||
pub app_config: crate::AppConfig,
|
let store_path = workspace_root.join(DEFAULT_STORE_CONFIG_PATH);
|
||||||
/// Resolved transport source path.
|
let wallet_path = workspace_root.join(DEFAULT_WALLET_CONFIG_PATH);
|
||||||
pub transport_path: std::path::PathBuf,
|
let execution_path = workspace_root.join(DEFAULT_EXECUTION_CONFIG_PATH);
|
||||||
/// Resolved listener source path.
|
let transport =
|
||||||
pub listeners_path: std::path::PathBuf,
|
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.
|
/// Validates a typed binary composition document before referenced documents are loaded.
|
||||||
pub fn validate_composition_document(document: &CompositionConfigDocument) -> ks_core::Result<()> {
|
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::Ok(()) => (),
|
||||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
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.logging,
|
||||||
&document.sources.transport,
|
&document.sources.transport,
|
||||||
&document.sources.listeners,
|
&document.sources.listeners,
|
||||||
|
&document.sources.store,
|
||||||
|
&document.sources.wallet,
|
||||||
|
&document.sources.execution,
|
||||||
] {
|
] {
|
||||||
match crate::require_non_empty(source, "composition.sources") {
|
match crate::require_non_empty(source, "composition.sources") {
|
||||||
std::result::Result::Ok(()) => (),
|
std::result::Result::Ok(()) => (),
|
||||||
@@ -317,12 +487,19 @@ pub fn validate_composition_document(document: &CompositionConfigDocument) -> ks
|
|||||||
std::result::Result::Ok(()) => (),
|
std::result::Result::Ok(()) => (),
|
||||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||||
}
|
}
|
||||||
for reference in
|
for reference in [
|
||||||
[&profile.logging_profile, &profile.transport_profile, &profile.listeners_profile]
|
profile.logging_profile.as_ref(),
|
||||||
{
|
profile.transport_profile.as_ref(),
|
||||||
match crate::require_non_empty(reference, "composition.profile.reference") {
|
profile.listeners_profile.as_ref(),
|
||||||
std::result::Result::Ok(()) => (),
|
profile.store_profile.as_ref(),
|
||||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
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()) {
|
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::<serde_json::Value>(&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::<std::string::String>::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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
const DEFAULT_COMPOSITION: &str =
|
const DEFAULT_COMPOSITION: &str =
|
||||||
include_str!("../../config/kb-app-demo-desktop.default.config.json");
|
include_str!("../../config/kb-app-demo-desktop.default.config.json");
|
||||||
const EXAMPLE_COMPOSITION: &str =
|
const EXAMPLE_COMPOSITION: &str =
|
||||||
include_str!("../../config/example.kb-app-demo-desktop.default.config.json");
|
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]
|
#[test]
|
||||||
fn default_and_example_compositions_validate() {
|
fn default_and_example_compositions_validate() {
|
||||||
@@ -354,20 +602,19 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[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) {
|
let composition = match super::parse_composition_json(DEFAULT_COMPOSITION) {
|
||||||
std::result::Result::Ok(value) => value,
|
std::result::Result::Ok(value) => value,
|
||||||
std::result::Result::Err(error) => panic!("default composition must parse: {error}"),
|
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::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) {
|
assert!(profile.logging_profile.is_none());
|
||||||
std::result::Result::Ok(value) => value,
|
assert!(profile.transport_profile.is_none());
|
||||||
std::result::Result::Err(error) => panic!("default listeners must parse: {error}"),
|
assert!(profile.store_profile.is_none());
|
||||||
};
|
|
||||||
let resolved = super::compose_app_config(&composition, &transport, &listeners);
|
|
||||||
assert!(resolved.is_ok());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
172
ks-config/src/execution.rs
Normal file
172
ks-config/src/execution.rs
Normal file
@@ -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<ExecutionProfileConfig>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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<ExecutionConfigDocument> {
|
||||||
|
let schema = match serde_json::from_str::<serde_json::Value>(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::<serde_json::Value>(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::<ExecutionConfigDocument>(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<ExecutionConfigDocument> {
|
||||||
|
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::<std::string::String>::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());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,16 +1,19 @@
|
|||||||
// file: ks-config/src/lib.rs
|
// 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)]
|
#![warn(missing_docs)]
|
||||||
#![deny(unreachable_pub)]
|
#![deny(unreachable_pub)]
|
||||||
#![forbid(unsafe_code)]
|
#![forbid(unsafe_code)]
|
||||||
|
|
||||||
mod composition;
|
mod composition;
|
||||||
mod environment;
|
mod environment;
|
||||||
|
mod execution;
|
||||||
mod listeners;
|
mod listeners;
|
||||||
mod settings;
|
mod settings;
|
||||||
|
mod store;
|
||||||
mod transport;
|
mod transport;
|
||||||
|
mod wallet;
|
||||||
|
|
||||||
/// Exposes the resolved application configuration plus its composition source.
|
/// Exposes the resolved application configuration plus its composition source.
|
||||||
pub use self::composition::ComposedAppConfig;
|
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;
|
pub use self::composition::read_composition_json_file;
|
||||||
/// Exposes binary composition loading with workspace environment resolution.
|
/// Exposes binary composition loading with workspace environment resolution.
|
||||||
pub use self::composition::read_composition_json_file_with_environment;
|
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.
|
/// Exposes composition source path resolution.
|
||||||
pub use self::composition::resolve_composition_source_path;
|
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.
|
/// Exposes typed binary composition validation.
|
||||||
pub use self::composition::validate_composition_document;
|
pub use self::composition::validate_composition_document;
|
||||||
/// Exposes binary composition JSON Schema validation.
|
/// Exposes binary composition JSON Schema validation.
|
||||||
@@ -50,12 +57,28 @@ pub use self::environment::EnvironmentLoadReport;
|
|||||||
pub use self::environment::load_workspace_environment;
|
pub use self::environment::load_workspace_environment;
|
||||||
/// Exposes environment placeholder resolution.
|
/// Exposes environment placeholder resolution.
|
||||||
pub use self::environment::resolve_environment_placeholders;
|
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.
|
/// Exposes the independent listener configuration document.
|
||||||
pub use self::listeners::ListenersConfigDocument;
|
pub use self::listeners::ListenersConfigDocument;
|
||||||
/// Exposes one named listener profile.
|
/// Exposes one named listener profile.
|
||||||
pub use self::listeners::ListenersProfileConfig;
|
pub use self::listeners::ListenersProfileConfig;
|
||||||
/// Exposes the active listener profile resolver.
|
/// Exposes the default listener profile resolver.
|
||||||
pub use self::listeners::active_listeners_profile;
|
pub use self::listeners::default_listeners_profile;
|
||||||
/// Exposes the listener JSON Schema text.
|
/// Exposes the listener JSON Schema text.
|
||||||
pub use self::listeners::listeners_json_schema_text;
|
pub use self::listeners::listeners_json_schema_text;
|
||||||
/// Exposes the listener JSON Schema value parser.
|
/// 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;
|
pub use self::listeners::validate_listeners_json_schema;
|
||||||
/// Exposes the account listener configuration type.
|
/// Exposes the account listener configuration type.
|
||||||
pub use self::settings::AccountListenerConfig;
|
pub use self::settings::AccountListenerConfig;
|
||||||
/// Exposes the root application configuration type.
|
/// Exposes the root resolved runtime configuration type.
|
||||||
pub use self::settings::AppConfig;
|
pub use self::settings::AppConfig;
|
||||||
/// Exposes the application section configuration type.
|
/// Exposes the application metadata configuration type.
|
||||||
pub use self::settings::AppSectionConfig;
|
pub use self::settings::AppSectionConfig;
|
||||||
/// Exposes the local data configuration type.
|
|
||||||
pub use self::settings::DataConfig;
|
|
||||||
/// Exposes the database configuration type.
|
/// Exposes the database configuration type.
|
||||||
pub use self::settings::DatabaseConfig;
|
pub use self::settings::DatabaseConfig;
|
||||||
/// Exposes the demo application configuration type.
|
/// Exposes the temporary demo application configuration type.
|
||||||
pub use self::settings::DemoConfig;
|
pub use self::settings::DemoConfig;
|
||||||
/// Exposes the endpoint role configuration type.
|
/// Exposes the endpoint role configuration type.
|
||||||
pub use self::settings::EndpointRoleConfig;
|
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;
|
pub use self::settings::config_json_schema_value;
|
||||||
/// Exposes resolved runtime configuration parsing from a JSON string.
|
/// Exposes resolved runtime configuration parsing from a JSON string.
|
||||||
pub use self::settings::parse_config_json;
|
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;
|
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;
|
pub use self::settings::read_config_json_file_with_environment;
|
||||||
/// Exposes the compact JSON serializer for resolved runtime configuration values.
|
/// Exposes the compact JSON serializer for resolved runtime configuration values.
|
||||||
pub use self::settings::serialize_config_json;
|
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;
|
pub use self::settings::validate_config;
|
||||||
/// Exposes the JSON Schema validator for raw resolved runtime configuration JSON.
|
/// Exposes the JSON Schema validator for raw resolved runtime configuration JSON.
|
||||||
pub use self::settings::validate_config_json_schema;
|
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.
|
/// Exposes the independent transport configuration document.
|
||||||
pub use self::transport::TransportConfigDocument;
|
pub use self::transport::TransportConfigDocument;
|
||||||
/// Exposes one named transport profile.
|
/// Exposes one fully resolved transport profile.
|
||||||
pub use self::transport::TransportProfileConfig;
|
pub use self::transport::TransportProfileConfig;
|
||||||
/// Exposes the active transport profile resolver.
|
/// Exposes one transport source profile.
|
||||||
pub use self::transport::active_transport_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.
|
/// Exposes transport configuration parsing from JSON.
|
||||||
pub use self::transport::parse_transport_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.
|
/// Exposes transport configuration loading with workspace environment resolution.
|
||||||
pub use self::transport::read_transport_json_file_with_environment;
|
pub use self::transport::read_transport_json_file_with_environment;
|
||||||
/// Exposes the transport JSON Schema text.
|
/// Exposes the transport JSON Schema text.
|
||||||
@@ -152,12 +199,36 @@ pub use self::transport::transport_profile;
|
|||||||
pub use self::transport::validate_transport_document;
|
pub use self::transport::validate_transport_document;
|
||||||
/// Exposes transport JSON Schema validation.
|
/// Exposes transport JSON Schema validation.
|
||||||
pub use self::transport::validate_transport_json_schema;
|
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;
|
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;
|
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;
|
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;
|
pub(crate) use self::settings::validate_ws_endpoint;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: ks-config/src/listeners.rs
|
// file: ks-config/src/listeners.rs
|
||||||
// version: 1
|
// version: 2
|
||||||
|
|
||||||
//! Independent Solana listener configuration document loading and validation.
|
//! 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.
|
/// Root listener document containing independently selectable named profiles.
|
||||||
#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize)]
|
#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize)]
|
||||||
pub struct ListenersConfigDocument {
|
pub struct ListenersConfigDocument {
|
||||||
/// Active listener profile used by consumers that do not provide an explicit selection.
|
/// Default listener profile used by consumers that do not provide an explicit selection.
|
||||||
pub active_profile: std::string::String,
|
pub default_profile: std::string::String,
|
||||||
/// Named listener profiles available in this document.
|
/// Named listener profiles available in this document.
|
||||||
pub profiles: std::vec::Vec<ListenersProfileConfig>,
|
pub profiles: std::vec::Vec<ListenersProfileConfig>,
|
||||||
}
|
}
|
||||||
@@ -157,11 +157,11 @@ pub fn listeners_profile<'a>(
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the active listener profile declared by the document.
|
/// Returns the default listener profile declared by the document.
|
||||||
pub fn active_listeners_profile(
|
pub fn default_listeners_profile(
|
||||||
document: &ListenersConfigDocument,
|
document: &ListenersConfigDocument,
|
||||||
) -> ks_core::Result<&ListenersProfileConfig> {
|
) -> 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.
|
/// 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.
|
/// Validates a typed listener configuration document.
|
||||||
pub fn validate_listeners_document(document: &ListenersConfigDocument) -> ks_core::Result<()> {
|
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::Ok(()) => (),
|
||||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
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),
|
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::Ok(_) => std::result::Result::Ok(()),
|
||||||
std::result::Result::Err(error) => std::result::Result::Err(error),
|
std::result::Result::Err(error) => std::result::Result::Err(error),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: ks-config/src/settings.rs
|
// file: ks-config/src/settings.rs
|
||||||
// version: 21
|
// version: 22
|
||||||
|
|
||||||
//! Resolved runtime configuration models retained during the `0.5.1` source-document split.
|
//! Resolved runtime configuration models retained during the `0.5.1` source-document split.
|
||||||
|
|
||||||
@@ -31,8 +31,6 @@ pub struct ProfileConfig {
|
|||||||
pub app: AppSectionConfig,
|
pub app: AppSectionConfig,
|
||||||
/// Database configuration.
|
/// Database configuration.
|
||||||
pub database: DatabaseConfig,
|
pub database: DatabaseConfig,
|
||||||
/// Local data directories.
|
|
||||||
pub data: DataConfig,
|
|
||||||
/// Solana endpoint and listener configuration.
|
/// Solana endpoint and listener configuration.
|
||||||
pub solana: SolanaConfig,
|
pub solana: SolanaConfig,
|
||||||
/// Wallet configuration.
|
/// Wallet configuration.
|
||||||
@@ -54,8 +52,6 @@ pub struct AppSectionConfig {
|
|||||||
pub name: std::string::String,
|
pub name: std::string::String,
|
||||||
/// Environment name.
|
/// Environment name.
|
||||||
pub environment: std::string::String,
|
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.
|
/// Database backend configuration.
|
||||||
@@ -112,16 +108,6 @@ pub struct SqliteConfig {
|
|||||||
pub use_wal: bool,
|
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.
|
/// Solana endpoints and listener configuration.
|
||||||
#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize, TS)]
|
#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize, TS)]
|
||||||
#[ts(export, export_to = "../frontend/ts/bindings/ks_config/settings/SolanaConfig.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,
|
pub temporary_wallet_alias: std::string::String,
|
||||||
/// Persists the managed temporary wallet between process restarts.
|
/// Persists the managed temporary wallet between process restarts.
|
||||||
pub temporary_wallet_persist: bool,
|
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.
|
/// Execution safety configuration.
|
||||||
@@ -334,6 +312,14 @@ pub struct WalletConfig {
|
|||||||
export_to = "../frontend/ts/bindings/ks_config/settings/ExecutionConfig.ts"
|
export_to = "../frontend/ts/bindings/ks_config/settings/ExecutionConfig.ts"
|
||||||
)]
|
)]
|
||||||
pub struct ExecutionConfig {
|
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.
|
/// Enables dry-run for newly prepared execution policies.
|
||||||
pub dry_run_default: bool,
|
pub dry_run_default: bool,
|
||||||
/// Requires simulation before send.
|
/// Requires simulation before send.
|
||||||
@@ -598,10 +584,6 @@ fn validate_profile(profile: &ProfileConfig) -> ks_core::Result<()> {
|
|||||||
std::result::Result::Ok(()) => (),
|
std::result::Result::Ok(()) => (),
|
||||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
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) {
|
match validate_solana(&profile.solana) {
|
||||||
std::result::Result::Ok(()) => (),
|
std::result::Result::Ok(()) => (),
|
||||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
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");
|
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" {
|
if config.backend != "postgres" && config.backend != "sqlite" {
|
||||||
return std::result::Result::Err(ks_core::Error::new(
|
return std::result::Result::Err(ks_core::Error::new(
|
||||||
"database_backend_invalid",
|
"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");
|
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<()> {
|
fn validate_solana(config: &SolanaConfig) -> ks_core::Result<()> {
|
||||||
if config.http_endpoints.is_empty() {
|
if config.http_endpoints.is_empty() {
|
||||||
return std::result::Result::Err(ks_core::Error::new(
|
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");
|
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") {
|
match require_non_empty(&config.wallet_dir, "wallet.wallet_dir") {
|
||||||
std::result::Result::Ok(()) => (),
|
std::result::Result::Ok(()) => (),
|
||||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
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(());
|
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
|
let configured_spend = config
|
||||||
.localnet_max_spend_lamports
|
.localnet_max_spend_lamports
|
||||||
.saturating_add(config.devnet_max_spend_lamports)
|
.saturating_add(config.devnet_max_spend_lamports)
|
||||||
@@ -985,11 +959,15 @@ fn validate_wallet_execution_pair(
|
|||||||
execution: &ExecutionConfig,
|
execution: &ExecutionConfig,
|
||||||
) -> ks_core::Result<()> {
|
) -> ks_core::Result<()> {
|
||||||
for (enabled, limit, cluster) in [
|
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,
|
execution.mainnet_max_spend_lamports,
|
||||||
"mainnet-beta",
|
"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(
|
return std::result::Result::Err(ks_core::Error::new(
|
||||||
"execution_mainnet_send_without_confirmation",
|
"execution_mainnet_send_without_confirmation",
|
||||||
"mainnet sending requires operator confirmation",
|
"mainnet sending requires operator confirmation",
|
||||||
|
|||||||
186
ks-config/src/store.rs
Normal file
186
ks-config/src/store.rs
Normal file
@@ -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<StoreProfileConfig>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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<serde_json::Value> {
|
||||||
|
let result = serde_json::from_str::<serde_json::Value>(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::<serde_json::Value>(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<StoreConfigDocument> {
|
||||||
|
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::<StoreConfigDocument>(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<StoreConfigDocument> {
|
||||||
|
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::<std::string::String>::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());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,28 +1,99 @@
|
|||||||
// file: ks-config/src/transport.rs
|
// file: ks-config/src/transport.rs
|
||||||
// version: 1
|
// version: 2
|
||||||
|
|
||||||
//! Independent Solana transport configuration document loading and validation.
|
//! Independent Solana transport configuration document loading and validation.
|
||||||
|
|
||||||
const TRANSPORT_JSON_SCHEMA: &str =
|
const TRANSPORT_JSON_SCHEMA: &str =
|
||||||
include_str!("../../config/schemas/transport.config.schema.json");
|
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)]
|
#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize)]
|
||||||
pub struct TransportConfigDocument {
|
pub struct TransportConfigDocument {
|
||||||
/// Active transport profile used by consumers that do not provide an explicit selection.
|
/// Default transport profile used when a composition does not override it.
|
||||||
pub active_profile: std::string::String,
|
pub default_profile: std::string::String,
|
||||||
|
/// Named WebSocket endpoint defaults resolved before endpoint-specific overrides.
|
||||||
|
pub ws_endpoint_defaults: std::vec::Vec<WsEndpointDefaultsConfig>,
|
||||||
/// Named transport profiles available in this document.
|
/// Named transport profiles available in this document.
|
||||||
pub profiles: std::vec::Vec<TransportProfileConfig>,
|
pub profiles: std::vec::Vec<TransportProfileSourceConfig>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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)]
|
#[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<u64>,
|
||||||
|
/// Optional request timeout override.
|
||||||
|
pub request_timeout_ms: std::option::Option<u64>,
|
||||||
|
/// Optional unsubscribe timeout override.
|
||||||
|
pub unsubscribe_timeout_ms: std::option::Option<u64>,
|
||||||
|
/// Optional writer channel capacity override.
|
||||||
|
pub write_channel_capacity: std::option::Option<u32>,
|
||||||
|
/// Optional event channel capacity override.
|
||||||
|
pub event_channel_capacity: std::option::Option<u32>,
|
||||||
|
/// Optional automatic reconnect override.
|
||||||
|
pub auto_reconnect: std::option::Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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<crate::EndpointRoleConfig>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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<crate::HttpEndpointConfig>,
|
||||||
|
/// Standard Solana WebSocket endpoints before shared defaults are resolved.
|
||||||
|
pub ws_endpoints: std::vec::Vec<WsEndpointSourceConfig>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fully resolved transport profile consumed by transport clients.
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub struct TransportProfileConfig {
|
pub struct TransportProfileConfig {
|
||||||
/// Profile code.
|
/// Profile code.
|
||||||
pub name: std::string::String,
|
pub name: std::string::String,
|
||||||
/// HTTP JSON-RPC endpoints.
|
/// HTTP JSON-RPC endpoints.
|
||||||
pub http_endpoints: std::vec::Vec<crate::HttpEndpointConfig>,
|
pub http_endpoints: std::vec::Vec<crate::HttpEndpointConfig>,
|
||||||
/// Standard Solana WebSocket endpoints.
|
/// Standard Solana WebSocket endpoints with defaults and overrides resolved.
|
||||||
pub ws_endpoints: std::vec::Vec<crate::WsEndpointConfig>,
|
pub ws_endpoints: std::vec::Vec<crate::WsEndpointConfig>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,9 +104,9 @@ pub fn transport_json_schema_text() -> &'static str {
|
|||||||
|
|
||||||
/// Parses the embedded transport JSON Schema into a JSON value.
|
/// Parses the embedded transport JSON Schema into a JSON value.
|
||||||
pub fn transport_json_schema_value() -> ks_core::Result<serde_json::Value> {
|
pub fn transport_json_schema_value() -> ks_core::Result<serde_json::Value> {
|
||||||
let schema_result = serde_json::from_str::<serde_json::Value>(TRANSPORT_JSON_SCHEMA);
|
let result = serde_json::from_str::<serde_json::Value>(TRANSPORT_JSON_SCHEMA);
|
||||||
return match schema_result {
|
return match result {
|
||||||
std::result::Result::Ok(schema) => std::result::Result::Ok(schema),
|
std::result::Result::Ok(value) => std::result::Result::Ok(value),
|
||||||
std::result::Result::Err(error) => std::result::Result::Err(ks_core::Error::new(
|
std::result::Result::Err(error) => std::result::Result::Err(ks_core::Error::new(
|
||||||
"transport_config_schema_parse_failed",
|
"transport_config_schema_parse_failed",
|
||||||
error.to_string(),
|
error.to_string(),
|
||||||
@@ -46,11 +117,11 @@ pub fn transport_json_schema_value() -> ks_core::Result<serde_json::Value> {
|
|||||||
/// Validates raw transport JSON against the embedded transport schema.
|
/// Validates raw transport JSON against the embedded transport schema.
|
||||||
pub fn validate_transport_json_schema(raw_json: &str) -> ks_core::Result<()> {
|
pub fn validate_transport_json_schema(raw_json: &str) -> ks_core::Result<()> {
|
||||||
let schema = match transport_json_schema_value() {
|
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),
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||||
};
|
};
|
||||||
let instance = match serde_json::from_str::<serde_json::Value>(raw_json) {
|
let instance = match serde_json::from_str::<serde_json::Value>(raw_json) {
|
||||||
std::result::Result::Ok(instance) => instance,
|
std::result::Result::Ok(value) => value,
|
||||||
std::result::Result::Err(error) => {
|
std::result::Result::Err(error) => {
|
||||||
return std::result::Result::Err(ks_core::Error::new(
|
return std::result::Result::Err(ks_core::Error::new(
|
||||||
"transport_config_json_parse_failed",
|
"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) {
|
let validator = match jsonschema::validator_for(&schema) {
|
||||||
std::result::Result::Ok(validator) => validator,
|
std::result::Result::Ok(value) => value,
|
||||||
std::result::Result::Err(error) => {
|
std::result::Result::Err(error) => {
|
||||||
return std::result::Result::Err(ks_core::Error::new(
|
return std::result::Result::Err(ks_core::Error::new(
|
||||||
"transport_config_schema_compile_failed",
|
"transport_config_schema_compile_failed",
|
||||||
@@ -83,7 +154,7 @@ pub fn parse_transport_json(raw_json: &str) -> ks_core::Result<TransportConfigDo
|
|||||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||||
}
|
}
|
||||||
let document = match serde_json::from_str::<TransportConfigDocument>(raw_json) {
|
let document = match serde_json::from_str::<TransportConfigDocument>(raw_json) {
|
||||||
std::result::Result::Ok(document) => document,
|
std::result::Result::Ok(value) => value,
|
||||||
std::result::Result::Err(error) => {
|
std::result::Result::Err(error) => {
|
||||||
return std::result::Result::Err(ks_core::Error::new(
|
return std::result::Result::Err(ks_core::Error::new(
|
||||||
"transport_config_json_decode_failed",
|
"transport_config_json_decode_failed",
|
||||||
@@ -97,23 +168,7 @@ pub fn parse_transport_json(raw_json: &str) -> ks_core::Result<TransportConfigDo
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reads and parses one transport configuration document from a filesystem path.
|
/// Loads environment placeholders and parses one transport configuration file.
|
||||||
pub fn read_transport_json_file(
|
|
||||||
path: &std::path::Path,
|
|
||||||
) -> ks_core::Result<TransportConfigDocument> {
|
|
||||||
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.
|
|
||||||
pub fn read_transport_json_file_with_environment(
|
pub fn read_transport_json_file_with_environment(
|
||||||
path: &std::path::Path,
|
path: &std::path::Path,
|
||||||
workspace_root: &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),
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||||
}
|
}
|
||||||
let raw_json = match std::fs::read_to_string(path) {
|
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) => {
|
std::result::Result::Err(error) => {
|
||||||
return std::result::Result::Err(ks_core::Error::new(
|
return std::result::Result::Err(ks_core::Error::new(
|
||||||
"transport_config_file_read_failed",
|
"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(&crate::resolve_environment_placeholders(&raw_json));
|
||||||
return parse_transport_json(&resolved);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns one named transport profile.
|
/// Resolves one named transport profile including WebSocket defaults and overrides.
|
||||||
pub fn transport_profile<'a>(
|
pub fn transport_profile(
|
||||||
|
document: &TransportConfigDocument,
|
||||||
|
profile_name: &str,
|
||||||
|
) -> ks_core::Result<TransportProfileConfig> {
|
||||||
|
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::<crate::WsEndpointConfig>::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<TransportProfileConfig> {
|
||||||
|
return transport_profile(document, document.default_profile.as_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn source_transport_profile<'a>(
|
||||||
document: &'a TransportConfigDocument,
|
document: &'a TransportConfigDocument,
|
||||||
profile_name: &str,
|
profile_name: &str,
|
||||||
) -> ks_core::Result<&'a TransportProfileConfig> {
|
) -> ks_core::Result<&'a TransportProfileSourceConfig> {
|
||||||
for profile in &document.profiles {
|
for profile in &document.profiles {
|
||||||
if profile.name == profile_name {
|
if profile.name == profile_name {
|
||||||
return std::result::Result::Ok(profile);
|
return std::result::Result::Ok(profile);
|
||||||
@@ -151,19 +288,57 @@ pub fn transport_profile<'a>(
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the active transport profile declared by the document.
|
fn ws_endpoint_defaults<'a>(
|
||||||
pub fn active_transport_profile(
|
document: &'a TransportConfigDocument,
|
||||||
document: &TransportConfigDocument,
|
defaults_name: &str,
|
||||||
) -> ks_core::Result<&TransportProfileConfig> {
|
) -> ks_core::Result<&'a WsEndpointDefaultsConfig> {
|
||||||
return transport_profile(document, document.active_profile.as_str());
|
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.
|
/// Validates a typed transport configuration document.
|
||||||
pub fn validate_transport_document(document: &TransportConfigDocument) -> ks_core::Result<()> {
|
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::Ok(()) => (),
|
||||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
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::<std::string::String>::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() {
|
if document.profiles.is_empty() {
|
||||||
return std::result::Result::Err(ks_core::Error::new(
|
return std::result::Result::Err(ks_core::Error::new(
|
||||||
"transport_config_profiles_empty",
|
"transport_config_profiles_empty",
|
||||||
@@ -195,13 +370,21 @@ pub fn validate_transport_document(document: &TransportConfigDocument) -> ks_cor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for endpoint in &profile.ws_endpoints {
|
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::Ok(()) => (),
|
||||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
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::Ok(_) => std::result::Result::Ok(()),
|
||||||
std::result::Result::Err(error) => std::result::Result::Err(error),
|
std::result::Result::Err(error) => std::result::Result::Err(error),
|
||||||
};
|
};
|
||||||
@@ -219,22 +402,19 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn schema_rejects_unsupported_transport_surface() {
|
fn websocket_defaults_are_resolved_before_endpoint_overrides() {
|
||||||
let value_result = serde_json::from_str::<serde_json::Value>(DEFAULT_TRANSPORT);
|
let document = match super::parse_transport_json(DEFAULT_TRANSPORT) {
|
||||||
let mut value = match value_result {
|
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::Ok(value) => value,
|
||||||
std::result::Result::Err(error) => {
|
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!([]);
|
assert_eq!(profile.ws_endpoints[0].event_channel_capacity, 1024);
|
||||||
let raw_result = serde_json::to_string(&value);
|
assert_eq!(profile.ws_endpoints[1].event_channel_capacity, 4096);
|
||||||
let raw = match raw_result {
|
assert!(!profile.ws_endpoints[0].auto_reconnect);
|
||||||
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());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
216
ks-config/src/wallet.rs
Normal file
216
ks-config/src/wallet.rs
Normal file
@@ -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<WalletProfileConfig>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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<WalletConfigDocument> {
|
||||||
|
let schema = match serde_json::from_str::<serde_json::Value>(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::<serde_json::Value>(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::<WalletConfigDocument>(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<WalletConfigDocument> {
|
||||||
|
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<crate::WalletConfig> {
|
||||||
|
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::<std::string::String>::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());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: ks-config/tests/external_composition_api.rs
|
// file: ks-config/tests/external_composition_api.rs
|
||||||
// version: 1
|
// version: 2
|
||||||
|
|
||||||
//! External API coverage for binary composition and shared configuration documents.
|
//! 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::Ok(value) => value,
|
||||||
std::result::Result::Err(error) => panic!("listeners must parse: {error}"),
|
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::Ok(value) => value,
|
||||||
std::result::Result::Err(error) => panic!("composition must resolve: {error}"),
|
std::result::Result::Err(error) => panic!("composition must resolve: {error}"),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,8 +1,15 @@
|
|||||||
<!-- file: ks-logging/CHANGELOG.md -->
|
<!-- file: ks-logging/CHANGELOG.md -->
|
||||||
<!-- version: 15 -->
|
<!-- version: 18 -->
|
||||||
|
|
||||||
# CHANGELOG — ks-logging
|
# 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`
|
## `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 ;
|
- 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 ;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: ks-logging/README.md -->
|
<!-- file: ks-logging/README.md -->
|
||||||
<!-- version: 7 -->
|
<!-- version: 8 -->
|
||||||
|
|
||||||
# ks-logging
|
# ks-logging
|
||||||
|
|
||||||
@@ -7,39 +7,21 @@
|
|||||||
|
|
||||||
## Responsabilités
|
## 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` ;
|
- valider `config/schemas/logging.config.schema.json` ;
|
||||||
- sélectionner un profil logging indépendamment du profil applicatif ;
|
- posséder `logs_directory` comme valeur globale indépendante des profils ;
|
||||||
- posséder `LoggingConfig`, `LogTargetConfig` et `LogTargetFilterConfig` ;
|
- sélectionner un `default_profile` ou un profil explicitement choisi par une composition ;
|
||||||
- sélectionner les routes activées ;
|
- résoudre les chemins relatifs des targets fichier sous `logs_directory` ;
|
||||||
- appliquer niveaux, targets exactes et préfixes wildcard ;
|
- initialiser les routes `tracing` sans exposer de secret.
|
||||||
- créer les writers non bloquants et gérer les formats/rotations ;
|
|
||||||
- conserver les `WorkerGuard` pendant toute la durée du processus.
|
|
||||||
|
|
||||||
## 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`.
|
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é.
|
||||||
|
|
||||||
## 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`.
|
|
||||||
|
|
||||||
## Documents
|
## Documents
|
||||||
|
|
||||||
- [Utilisation](USAGE.md)
|
- [Utilisation](USAGE.md)
|
||||||
- [Travaux restants](TODO.md)
|
|
||||||
- [Historique](CHANGELOG.md)
|
- [Historique](CHANGELOG.md)
|
||||||
- [Configuration locale](../config/README.md)
|
- [Guide logging](../docs/guides/LOGGING.md)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: ks-logging/USAGE.md -->
|
<!-- file: ks-logging/USAGE.md -->
|
||||||
<!-- version: 6 -->
|
<!-- version: 7 -->
|
||||||
|
|
||||||
# Utilisation de ks-logging
|
# Utilisation de ks-logging
|
||||||
|
|
||||||
@@ -13,114 +13,48 @@ let document = match ks_logging::read_logging_json_file_with_environment(
|
|||||||
Ok(value) => value,
|
Ok(value) => value,
|
||||||
Err(error) => return Err(error),
|
Err(error) => return Err(error),
|
||||||
};
|
};
|
||||||
let config = match ks_logging::active_logging_profile(&document) {
|
let config = match ks_logging::default_logging_profile(&document) {
|
||||||
Ok(value) => value,
|
|
||||||
Err(error) => return Err(error),
|
|
||||||
};
|
|
||||||
let guard = match ks_logging::init_logging(config) {
|
|
||||||
Ok(value) => value,
|
Ok(value) => value,
|
||||||
Err(error) => return Err(error),
|
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.
|
Le document possède :
|
||||||
|
|
||||||
## Format du document
|
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"active_profile": "local_devnet",
|
"logs_directory": "${KS_LOGS_DIRECTORY:-logs}",
|
||||||
"profiles": [
|
"default_profile": "local_devnet",
|
||||||
{
|
"profiles": []
|
||||||
"name": "local_devnet",
|
|
||||||
"default_level": "info",
|
|
||||||
"targets": [],
|
|
||||||
"target_filters": []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
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
|
Une application peut sélectionner un autre profil avec :
|
||||||
|
|
||||||
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` :
|
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
let config = ks_logging::LoggingConfig {
|
let config = match ks_logging::logging_profile(&document, "mainnet_research") {
|
||||||
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) {
|
|
||||||
Ok(value) => value,
|
Ok(value) => value,
|
||||||
Err(error) => return Err(error),
|
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
|
```rust
|
||||||
let file_route = ks_logging::LogTargetConfig {
|
match ks_logging::init_logging(&config) {
|
||||||
name: "operations-json".to_string(),
|
Ok(()) => (),
|
||||||
enabled: true,
|
Err(error) => return Err(error),
|
||||||
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}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_eq!(guard.route_count(), guard.route_names().len());
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Erreurs
|
L'initialisation reste globale au processus. Les binaires doivent donc résoudre leur configuration avant le premier appel à `init_logging`.
|
||||||
|
|
||||||
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`.
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: ks-logging/src/document.rs
|
// file: ks-logging/src/document.rs
|
||||||
// version: 2
|
// version: 5
|
||||||
|
|
||||||
//! Independent logging configuration document loading and validation.
|
//! 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.
|
/// Root logging document containing independently selectable named profiles.
|
||||||
#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize)]
|
#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize)]
|
||||||
pub struct LoggingConfigDocument {
|
pub struct LoggingConfigDocument {
|
||||||
/// Active logging profile name.
|
/// Root directory for file logs, independent from profile selection.
|
||||||
pub active_profile: std::string::String,
|
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.
|
/// Named logging profiles available in this document.
|
||||||
pub profiles: std::vec::Vec<LoggingProfileConfig>,
|
pub profiles: std::vec::Vec<LoggingProfileConfig>,
|
||||||
}
|
}
|
||||||
@@ -132,14 +134,44 @@ pub fn read_logging_json_file_with_environment(
|
|||||||
return parse_logging_json(&resolved);
|
return parse_logging_json(&resolved);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns one named runtime logging profile.
|
/// Returns one named runtime logging profile with file paths resolved below the global log root.
|
||||||
pub fn logging_profile<'a>(
|
pub fn logging_profile(
|
||||||
|
document: &LoggingConfigDocument,
|
||||||
|
profile_name: &str,
|
||||||
|
) -> ks_core::Result<crate::LoggingConfig> {
|
||||||
|
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<crate::LoggingConfig> {
|
||||||
|
return logging_profile(document, document.default_profile.as_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn source_logging_profile<'a>(
|
||||||
document: &'a LoggingConfigDocument,
|
document: &'a LoggingConfigDocument,
|
||||||
profile_name: &str,
|
profile_name: &str,
|
||||||
) -> ks_core::Result<&'a crate::LoggingConfig> {
|
) -> ks_core::Result<&'a LoggingProfileConfig> {
|
||||||
for profile in &document.profiles {
|
for profile in &document.profiles {
|
||||||
if profile.name == profile_name {
|
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(
|
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.
|
/// Validates a typed logging configuration document.
|
||||||
pub fn validate_logging_document(document: &LoggingConfigDocument) -> ks_core::Result<()> {
|
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::Ok(()) => (),
|
||||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
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::<std::string::String>::new();
|
let mut names = std::collections::BTreeSet::<std::string::String>::new();
|
||||||
let mut active_count = 0_u32;
|
|
||||||
for profile in &document.profiles {
|
for profile in &document.profiles {
|
||||||
match require_non_empty(&profile.name, "logging.profile.name") {
|
match require_non_empty(&profile.name, "logging.profile.name") {
|
||||||
std::result::Result::Ok(()) => (),
|
std::result::Result::Ok(()) => (),
|
||||||
@@ -180,24 +208,15 @@ pub fn validate_logging_document(document: &LoggingConfigDocument) -> ks_core::R
|
|||||||
profile.name.clone(),
|
profile.name.clone(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
if profile.name == document.active_profile {
|
|
||||||
active_count += 1;
|
|
||||||
}
|
|
||||||
match validate_logging_config(&profile.config) {
|
match validate_logging_config(&profile.config) {
|
||||||
std::result::Result::Ok(()) => (),
|
std::result::Result::Ok(()) => (),
|
||||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if active_count != 1 {
|
return match default_logging_profile(document) {
|
||||||
return std::result::Result::Err(ks_core::Error::new(
|
std::result::Result::Ok(_) => std::result::Result::Ok(()),
|
||||||
"active_logging_profile_count_invalid",
|
std::result::Result::Err(error) => std::result::Result::Err(error),
|
||||||
format!(
|
};
|
||||||
"active logging profile '{}' must match exactly one profile",
|
|
||||||
document.active_profile
|
|
||||||
),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
return std::result::Result::Ok(());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_logging_config(config: &crate::LoggingConfig) -> ks_core::Result<()> {
|
fn validate_logging_config(config: &crate::LoggingConfig) -> ks_core::Result<()> {
|
||||||
@@ -416,28 +435,35 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[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_result = super::parse_logging_json(DEFAULT_LOGGING_CONFIG);
|
||||||
let document = match document_result {
|
let document = match document_result {
|
||||||
std::result::Result::Ok(value) => value,
|
std::result::Result::Ok(value) => value,
|
||||||
std::result::Result::Err(error) => panic!("default logging config must parse: {error}"),
|
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_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());
|
assert!(super::logging_profile(&document, "local_devnet").is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn default_logging_config_routes_global_and_operational_crate_files() {
|
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 {
|
let document = match document_result {
|
||||||
std::result::Result::Ok(value) => value,
|
std::result::Result::Ok(value) => value,
|
||||||
std::result::Result::Err(error) => panic!("default logging config must parse: {error}"),
|
std::result::Result::Err(error) => panic!("default logging config must parse: {error}"),
|
||||||
};
|
};
|
||||||
let operational_crates = tracing_crate_names();
|
let operational_crates = tracing_crate_names();
|
||||||
assert!(!operational_crates.is_empty());
|
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() {
|
let directory = match profile.name.as_str() {
|
||||||
"local_devnet" => "devnet",
|
"local_devnet" => "devnet",
|
||||||
"mainnet_research" => "mainnet_research",
|
"mainnet_research" => "mainnet_research",
|
||||||
@@ -450,7 +476,7 @@ mod tests {
|
|||||||
("error.jsonl", "error", "json"),
|
("error.jsonl", "error", "json"),
|
||||||
] {
|
] {
|
||||||
let expected_path = format!("logs/{directory}/{suffix}");
|
let expected_path = format!("logs/{directory}/{suffix}");
|
||||||
assert!(profile.config.targets.iter().any(|target| {
|
assert!(resolved.targets.iter().any(|target| {
|
||||||
return target.enabled
|
return target.enabled
|
||||||
&& target.path == expected_path
|
&& target.path == expected_path
|
||||||
&& target.level == level
|
&& target.level == level
|
||||||
@@ -458,7 +484,7 @@ mod tests {
|
|||||||
&& target.targets == std::vec!["*".to_string()];
|
&& target.targets == std::vec!["*".to_string()];
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
assert!(profile.config.targets.iter().any(|target| {
|
assert!(resolved.targets.iter().any(|target| {
|
||||||
return target.enabled
|
return target.enabled
|
||||||
&& target.path == format!("logs/{directory}/app.log")
|
&& target.path == format!("logs/{directory}/app.log")
|
||||||
&& target.level == "debug"
|
&& target.level == "debug"
|
||||||
@@ -477,7 +503,7 @@ mod tests {
|
|||||||
crate_name.as_str()
|
crate_name.as_str()
|
||||||
};
|
};
|
||||||
let expected_path = format!("logs/{directory}/{route_directory}/{suffix}");
|
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
|
return target.enabled
|
||||||
&& target.path == expected_path
|
&& target.path == expected_path
|
||||||
&& target.level == level
|
&& 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-wallet\""));
|
assert!(DEFAULT_LOGGING_CONFIG.contains("\"ks-wallet\""));
|
||||||
assert!(DEFAULT_LOGGING_CONFIG.contains("\"ks-pipeline\""));
|
assert!(DEFAULT_LOGGING_CONFIG.contains("\"ks-pipeline\""));
|
||||||
assert!(DEFAULT_LOGGING_CONFIG.contains("logs/devnet/ks-pipeline/debug.log"));
|
assert!(
|
||||||
assert!(DEFAULT_LOGGING_CONFIG.contains("logs/mainnet_research/ks-pipeline/info.log"));
|
DEFAULT_LOGGING_CONFIG.contains("\"logs_directory\": \"${KS_LOGS_DIRECTORY:-logs}\"")
|
||||||
assert!(DEFAULT_LOGGING_CONFIG.contains("logs/mainnet/ks-pipeline/error.jsonl"));
|
);
|
||||||
|
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"));
|
assert!(!DEFAULT_LOGGING_CONFIG.contains("ks_pipeline"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn logging_parser_rejects_missing_active_profile() {
|
fn logging_parser_rejects_missing_default_profile() {
|
||||||
let mut value = parse_default_value();
|
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));
|
let result = super::parse_logging_json(&value_to_json(&value));
|
||||||
assert!(result.is_err());
|
assert!(result.is_err());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: ks-logging/src/lib.rs
|
// file: ks-logging/src/lib.rs
|
||||||
// version: 9
|
// version: 10
|
||||||
|
|
||||||
//! Logging and tracing initialization for applications and workers.
|
//! Logging and tracing initialization for applications and workers.
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
@@ -26,8 +26,8 @@ pub use self::config::LoggingConfig;
|
|||||||
pub use self::document::LoggingConfigDocument;
|
pub use self::document::LoggingConfigDocument;
|
||||||
/// Exposes one named logging profile.
|
/// Exposes one named logging profile.
|
||||||
pub use self::document::LoggingProfileConfig;
|
pub use self::document::LoggingProfileConfig;
|
||||||
/// Exposes active logging profile resolution.
|
/// Exposes default logging profile resolution.
|
||||||
pub use self::document::active_logging_profile;
|
pub use self::document::default_logging_profile;
|
||||||
/// Exposes the embedded logging JSON Schema text.
|
/// Exposes the embedded logging JSON Schema text.
|
||||||
pub use self::document::logging_json_schema_text;
|
pub use self::document::logging_json_schema_text;
|
||||||
/// Exposes the embedded logging JSON Schema parser.
|
/// Exposes the embedded logging JSON Schema parser.
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
<!-- file: ks-onchain-transport/CHANGELOG.md -->
|
<!-- file: ks-onchain-transport/CHANGELOG.md -->
|
||||||
<!-- version: 14 -->
|
<!-- version: 15 -->
|
||||||
|
|
||||||
# CHANGELOG — ks-onchain-transport
|
# 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`
|
## `0.5.1-pre.006`
|
||||||
|
|
||||||
- permet de construire directement les pools HTTP et WebSocket depuis `ks_config::TransportProfileConfig` ;
|
- permet de construire directement les pools HTTP et WebSocket depuis `ks_config::TransportProfileConfig` ;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: ks-onchain-transport/src/execution_rpc.rs
|
// file: ks-onchain-transport/src/execution_rpc.rs
|
||||||
// version: 11
|
// version: 12
|
||||||
|
|
||||||
//! Typed Solana JSON-RPC adapters used by execution orchestration.
|
//! Typed Solana JSON-RPC adapters used by execution orchestration.
|
||||||
|
|
||||||
@@ -2403,6 +2403,10 @@ mod tests {
|
|||||||
dry_run_default: true,
|
dry_run_default: true,
|
||||||
require_simulation: true,
|
require_simulation: true,
|
||||||
require_operator_confirmation: 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,
|
localnet_max_spend_lamports: 1_000_000,
|
||||||
devnet_max_spend_lamports: 1_000_000,
|
devnet_max_spend_lamports: 1_000_000,
|
||||||
testnet_max_spend_lamports: 0,
|
testnet_max_spend_lamports: 0,
|
||||||
|
|||||||
@@ -1,8 +1,14 @@
|
|||||||
<!-- file: ks-pipeline-demo-scenarios/CHANGELOG.md -->
|
<!-- file: ks-pipeline-demo-scenarios/CHANGELOG.md -->
|
||||||
<!-- version: 77 -->
|
<!-- version: 78 -->
|
||||||
|
|
||||||
# CHANGELOG — ks-pipeline-demo-scenarios
|
# 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`
|
## `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 ;
|
- 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 ;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: ks-pipeline-demo-scenarios/USAGE.md -->
|
<!-- file: ks-pipeline-demo-scenarios/USAGE.md -->
|
||||||
<!-- version: 30 -->
|
<!-- version: 31 -->
|
||||||
|
|
||||||
# Utilisation de ks-pipeline-demo-scenarios
|
# 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
|
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.
|
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.
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: ks-pipeline-demo-scenarios/src/environment.rs
|
// file: ks-pipeline-demo-scenarios/src/environment.rs
|
||||||
// version: 6
|
// version: 7
|
||||||
|
|
||||||
//! Environment initialization for opt-in demonstration scenarios.
|
//! 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);
|
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<ks_config::AppConfig> {
|
||||||
|
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.
|
/// Resolves one compatible Devnet profile for demo scenarios.
|
||||||
///
|
///
|
||||||
/// An explicit name has priority, followed by `KS_DEVNET_PROFILE`, then the
|
/// An explicit name has priority, followed by `KS_DEVNET_PROFILE`, then the
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: ks-pipeline-demo-scenarios/src/lib.rs
|
// file: ks-pipeline-demo-scenarios/src/lib.rs
|
||||||
// version: 28
|
// version: 29
|
||||||
|
|
||||||
#![forbid(unsafe_code)]
|
#![forbid(unsafe_code)]
|
||||||
#![deny(unreachable_pub)]
|
#![deny(unreachable_pub)]
|
||||||
@@ -17,6 +17,8 @@ mod spl;
|
|||||||
pub use self::environment::DevnetProfileStoreReadiness;
|
pub use self::environment::DevnetProfileStoreReadiness;
|
||||||
/// Loads the workspace environment for opt-in demo and Devnet scenarios.
|
/// Loads the workspace environment for opt-in demo and Devnet scenarios.
|
||||||
pub use self::environment::initialize_demo_scenario_environment;
|
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.
|
/// Verifies or initializes the PostgreSQL schema selected by one Devnet profile.
|
||||||
pub use self::environment::prepare_demo_devnet_profile_store;
|
pub use self::environment::prepare_demo_devnet_profile_store;
|
||||||
/// Resolves one configured Devnet profile for UI, CLI and test scenarios.
|
/// Resolves one configured Devnet profile for UI, CLI and test scenarios.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/collection_verify_campaign.rs
|
// 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.
|
//! Confirmed Devnet collection parent/member `Verify -> Unverify` campaign.
|
||||||
|
|
||||||
@@ -643,21 +643,8 @@ mod tests {
|
|||||||
std::option::Option::Some(value) => value,
|
std::option::Option::Some(value) => value,
|
||||||
std::option::Option::None => panic!("workspace root cannot be resolved"),
|
std::option::Option::None => panic!("workspace root cannot be resolved"),
|
||||||
};
|
};
|
||||||
let config_path = match std::env::var("KS_DEVNET_CONFIG_PATH") {
|
let config = crate::load_demo_scenario_config(workspace_root)
|
||||||
std::result::Result::Ok(value) => {
|
.unwrap_or_else(|error| panic!("Devnet configuration loading failed: {error}"));
|
||||||
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 profile_name = std::env::var("KS_DEVNET_PROFILE").ok();
|
let profile_name = std::env::var("KS_DEVNET_PROFILE").ok();
|
||||||
let mut profile = crate::resolve_demo_devnet_profile(&config, profile_name.as_deref())
|
let mut profile = crate::resolve_demo_devnet_profile(&config, profile_name.as_deref())
|
||||||
.unwrap_or_else(|error| panic!("Devnet profile resolution failed: {error}"));
|
.unwrap_or_else(|error| panic!("Devnet profile resolution failed: {error}"));
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/create_mint_campaign.rs
|
// 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.
|
//! Confirmed Devnet `Create -> Mint` campaign for one Metaplex asset family.
|
||||||
|
|
||||||
@@ -59,7 +59,7 @@ where
|
|||||||
+ Sync,
|
+ Sync,
|
||||||
O: crate::SolanaExecutionObserver,
|
O: crate::SolanaExecutionObserver,
|
||||||
{
|
{
|
||||||
if !profile.wallet.devnet_send_enabled {
|
if !profile.execution.devnet_send_enabled {
|
||||||
return std::result::Result::Err(ks_core::Error::config(
|
return std::result::Result::Err(ks_core::Error::config(
|
||||||
"Metaplex Create -> Mint Devnet campaign requires devnet_send_enabled=true",
|
"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::Some(value) => value,
|
||||||
std::option::Option::None => panic!("workspace root cannot be resolved"),
|
std::option::Option::None => panic!("workspace root cannot be resolved"),
|
||||||
};
|
};
|
||||||
let config_path = match std::env::var("KS_DEVNET_CONFIG_PATH") {
|
let config = crate::load_demo_scenario_config(workspace_root)
|
||||||
std::result::Result::Ok(value) => {
|
.unwrap_or_else(|error| panic!("Devnet configuration loading failed: {error}"));
|
||||||
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 profile_name = std::env::var("KS_DEVNET_PROFILE").ok();
|
let profile_name = std::env::var("KS_DEVNET_PROFILE").ok();
|
||||||
let mut profile = crate::resolve_demo_devnet_profile(&config, profile_name.as_deref())
|
let mut profile = crate::resolve_demo_devnet_profile(&config, profile_name.as_deref())
|
||||||
.unwrap_or_else(|error| panic!("Devnet profile resolution failed: {error}"));
|
.unwrap_or_else(|error| panic!("Devnet profile resolution failed: {error}"));
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/devnet_execution.rs
|
// 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.
|
//! Real Devnet simulation and submission for current Metaplex Token Metadata operations.
|
||||||
|
|
||||||
@@ -1185,7 +1185,7 @@ fn validate_profile(
|
|||||||
"Metaplex Devnet orchestration requires simulation",
|
"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(
|
return std::result::Result::Err(ks_core::Error::config(
|
||||||
"Devnet transaction submission is disabled by the wallet profile",
|
"Devnet transaction submission is disabled by the wallet profile",
|
||||||
));
|
));
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/escrow_campaign.rs
|
// 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.
|
//! Confirmed Devnet Token Owned Escrow campaign for current Metaplex escrow operations.
|
||||||
|
|
||||||
@@ -71,7 +71,7 @@ where
|
|||||||
+ Sync,
|
+ Sync,
|
||||||
O: crate::SolanaExecutionObserver,
|
O: crate::SolanaExecutionObserver,
|
||||||
{
|
{
|
||||||
if !profile.wallet.devnet_send_enabled {
|
if !profile.execution.devnet_send_enabled {
|
||||||
return std::result::Result::Err(ks_core::Error::config(
|
return std::result::Result::Err(ks_core::Error::config(
|
||||||
"Metaplex escrow Devnet campaign requires devnet_send_enabled=true",
|
"Metaplex escrow Devnet campaign requires devnet_send_enabled=true",
|
||||||
));
|
));
|
||||||
@@ -968,21 +968,8 @@ mod tests {
|
|||||||
std::option::Option::Some(value) => value,
|
std::option::Option::Some(value) => value,
|
||||||
std::option::Option::None => panic!("workspace root cannot be resolved"),
|
std::option::Option::None => panic!("workspace root cannot be resolved"),
|
||||||
};
|
};
|
||||||
let config_path = match std::env::var("KS_DEVNET_CONFIG_PATH") {
|
let config = crate::load_demo_scenario_config(workspace_root)
|
||||||
std::result::Result::Ok(value) => {
|
.unwrap_or_else(|error| panic!("Devnet configuration loading failed: {error}"));
|
||||||
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 profile_name = std::env::var("KS_DEVNET_PROFILE").ok();
|
let profile_name = std::env::var("KS_DEVNET_PROFILE").ok();
|
||||||
let mut profile = crate::resolve_demo_devnet_profile(&config, profile_name.as_deref())
|
let mut profile = crate::resolve_demo_devnet_profile(&config, profile_name.as_deref())
|
||||||
.unwrap_or_else(|error| panic!("Devnet profile resolution failed: {error}"));
|
.unwrap_or_else(|error| panic!("Devnet profile resolution failed: {error}"));
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/fixture.rs
|
// 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.
|
//! 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",
|
"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(
|
return std::result::Result::Err(ks_core::Error::config(
|
||||||
"Metaplex fixture preparation requires devnet_send_enabled=true",
|
"Metaplex fixture preparation requires devnet_send_enabled=true",
|
||||||
));
|
));
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/maintenance_campaign.rs
|
// 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.
|
//! 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::Some(value) => value,
|
||||||
std::option::Option::None => panic!("workspace root cannot be resolved"),
|
std::option::Option::None => panic!("workspace root cannot be resolved"),
|
||||||
};
|
};
|
||||||
let config_path = match std::env::var("KS_DEVNET_CONFIG_PATH") {
|
let config = crate::load_demo_scenario_config(workspace_root)
|
||||||
std::result::Result::Ok(value) => {
|
.unwrap_or_else(|error| panic!("Devnet configuration loading failed: {error}"));
|
||||||
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 profile_name = std::env::var("KS_DEVNET_PROFILE").ok();
|
let profile_name = std::env::var("KS_DEVNET_PROFILE").ok();
|
||||||
let mut profile = crate::resolve_demo_devnet_profile(&config, profile_name.as_deref())
|
let mut profile = crate::resolve_demo_devnet_profile(&config, profile_name.as_deref())
|
||||||
.unwrap_or_else(|error| panic!("Devnet profile resolution failed: {error}"));
|
.unwrap_or_else(|error| panic!("Devnet profile resolution failed: {error}"));
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/pnft_lifecycle_campaign.rs
|
// 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.
|
//! 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::Some(value) => value,
|
||||||
std::option::Option::None => panic!("workspace root cannot be resolved"),
|
std::option::Option::None => panic!("workspace root cannot be resolved"),
|
||||||
};
|
};
|
||||||
let config_path = match std::env::var("KS_DEVNET_CONFIG_PATH") {
|
let config = crate::load_demo_scenario_config(workspace_root)
|
||||||
std::result::Result::Ok(value) => {
|
.unwrap_or_else(|error| panic!("Devnet configuration loading failed: {error}"));
|
||||||
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 profile_name = std::env::var("KS_DEVNET_PROFILE").ok();
|
let profile_name = std::env::var("KS_DEVNET_PROFILE").ok();
|
||||||
let mut profile = crate::resolve_demo_devnet_profile(&config, profile_name.as_deref())
|
let mut profile = crate::resolve_demo_devnet_profile(&config, profile_name.as_deref())
|
||||||
.unwrap_or_else(|error| panic!("Devnet profile resolution failed: {error}"));
|
.unwrap_or_else(|error| panic!("Devnet profile resolution failed: {error}"));
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/print_burn_campaign.rs
|
// 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.
|
//! Devnet printable master NFT `Print -> Burn` campaign.
|
||||||
|
|
||||||
@@ -1175,21 +1175,8 @@ mod tests {
|
|||||||
std::option::Option::Some(value) => value,
|
std::option::Option::Some(value) => value,
|
||||||
std::option::Option::None => panic!("workspace root cannot be resolved"),
|
std::option::Option::None => panic!("workspace root cannot be resolved"),
|
||||||
};
|
};
|
||||||
let config_path = match std::env::var("KS_DEVNET_CONFIG_PATH") {
|
let config = crate::load_demo_scenario_config(workspace_root)
|
||||||
std::result::Result::Ok(value) => {
|
.unwrap_or_else(|error| panic!("Devnet configuration loading failed: {error}"));
|
||||||
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 profile_name = std::env::var("KS_DEVNET_PROFILE").ok();
|
let profile_name = std::env::var("KS_DEVNET_PROFILE").ok();
|
||||||
let mut profile = crate::resolve_demo_devnet_profile(&config, profile_name.as_deref())
|
let mut profile = crate::resolve_demo_devnet_profile(&config, profile_name.as_deref())
|
||||||
.unwrap_or_else(|error| panic!("Devnet profile resolution failed: {error}"));
|
.unwrap_or_else(|error| panic!("Devnet profile resolution failed: {error}"));
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: ks-pipeline-demo-scenarios/src/metadata/metaplex_token_metadata/use_campaign.rs
|
// 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.
|
//! 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::Some(value) => value,
|
||||||
std::option::Option::None => panic!("workspace root cannot be resolved"),
|
std::option::Option::None => panic!("workspace root cannot be resolved"),
|
||||||
};
|
};
|
||||||
let config_path = match std::env::var("KS_DEVNET_CONFIG_PATH") {
|
let config = crate::load_demo_scenario_config(workspace_root)
|
||||||
std::result::Result::Ok(value) => {
|
.unwrap_or_else(|error| panic!("Devnet configuration loading failed: {error}"));
|
||||||
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 profile_name = std::env::var("KS_DEVNET_PROFILE").ok();
|
let profile_name = std::env::var("KS_DEVNET_PROFILE").ok();
|
||||||
let mut profile = crate::resolve_demo_devnet_profile(&config, profile_name.as_deref())
|
let mut profile = crate::resolve_demo_devnet_profile(&config, profile_name.as_deref())
|
||||||
.unwrap_or_else(|error| panic!("Devnet profile resolution failed: {error}"));
|
.unwrap_or_else(|error| panic!("Devnet profile resolution failed: {error}"));
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: ks-pipeline-demo-scenarios/src/metadata/solana_program/devnet_execution.rs
|
// 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.
|
//! Real Devnet simulation and controlled submission for Solana Program Metadata.
|
||||||
|
|
||||||
@@ -635,7 +635,7 @@ fn validate_profile(
|
|||||||
"Solana Program Metadata orchestration requires simulation",
|
"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(
|
return std::result::Result::Err(ks_core::Error::config(
|
||||||
"Devnet transaction submission is disabled by the wallet profile",
|
"Devnet transaction submission is disabled by the wallet profile",
|
||||||
));
|
));
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: ks-pipeline-demo-scenarios/src/metadata/solana_program/fixture.rs
|
// file: ks-pipeline-demo-scenarios/src/metadata/solana_program/fixture.rs
|
||||||
// version: 7
|
// version: 8
|
||||||
|
|
||||||
//! Native Solana Program Metadata fixture preparation for Devnet journeys.
|
//! 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"
|
if profile.wallet.cluster != "devnet"
|
||||||
|| !profile.wallet.devnet_send_enabled
|
|| !profile.execution.devnet_send_enabled
|
||||||
|| !profile.wallet.temporary_wallet_enabled
|
|| !profile.wallet.temporary_wallet_enabled
|
||||||
|| !profile.wallet.temporary_wallet_persist
|
|| !profile.wallet.temporary_wallet_persist
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: ks-pipeline-demo-scenarios/src/solana.rs
|
// file: ks-pipeline-demo-scenarios/src/solana.rs
|
||||||
// version: 15
|
// version: 16
|
||||||
|
|
||||||
//! Devnet Solana execution orchestration with canonical post-validation.
|
//! 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
|
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(
|
return std::result::Result::Err(ks_core::Error::config(
|
||||||
"Devnet transaction submission is disabled by the wallet profile",
|
"Devnet transaction submission is disabled by the wallet profile",
|
||||||
));
|
));
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: ks-pipeline-demo-scenarios/src/spl/associated_token_account.rs
|
// file: ks-pipeline-demo-scenarios/src/spl/associated_token_account.rs
|
||||||
// version: 14
|
// version: 15
|
||||||
|
|
||||||
//! Devnet ATA execution with stateful and canonical post-validation.
|
//! Devnet ATA execution with stateful and canonical post-validation.
|
||||||
|
|
||||||
@@ -686,7 +686,7 @@ fn validate_profile(
|
|||||||
"SPL Associated Token Account Devnet orchestration requires simulation",
|
"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(
|
return std::result::Result::Err(ks_core::Error::config(
|
||||||
"Devnet transaction submission is disabled by the wallet profile",
|
"Devnet transaction submission is disabled by the wallet profile",
|
||||||
));
|
));
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: ks-pipeline-demo-scenarios/src/spl/memo.rs
|
// file: ks-pipeline-demo-scenarios/src/spl/memo.rs
|
||||||
// version: 10
|
// version: 11
|
||||||
|
|
||||||
//! Devnet SPL Memo v4 execution with canonical post-validation.
|
//! Devnet SPL Memo v4 execution with canonical post-validation.
|
||||||
|
|
||||||
@@ -544,7 +544,7 @@ fn validate_profile(
|
|||||||
"Memo Devnet orchestration requires simulation",
|
"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(
|
return std::result::Result::Err(ks_core::Error::config(
|
||||||
"Devnet transaction submission is disabled by the wallet profile",
|
"Devnet transaction submission is disabled by the wallet profile",
|
||||||
));
|
));
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: ks-pipeline-demo-scenarios/src/spl/token/execution.rs
|
// 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.
|
//! Devnet classic SPL Token execution with stateful and canonical post-validation.
|
||||||
|
|
||||||
@@ -642,7 +642,7 @@ fn validate_profile(
|
|||||||
"SPL Token Devnet orchestration requires simulation",
|
"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(
|
return std::result::Result::Err(ks_core::Error::config(
|
||||||
"Devnet transaction submission is disabled by the wallet profile",
|
"Devnet transaction submission is disabled by the wallet profile",
|
||||||
));
|
));
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: ks-pipeline-demo-scenarios/src/spl/token/lifecycle.rs
|
// file: ks-pipeline-demo-scenarios/src/spl/token/lifecycle.rs
|
||||||
// version: 13
|
// version: 14
|
||||||
|
|
||||||
//! Controlled Devnet lifecycle for freshly prepared classic SPL Token accounts.
|
//! Controlled Devnet lifecycle for freshly prepared classic SPL Token accounts.
|
||||||
|
|
||||||
@@ -295,7 +295,7 @@ where
|
|||||||
return std::result::Result::Err(error);
|
return std::result::Result::Err(error);
|
||||||
}
|
}
|
||||||
if profile.wallet.cluster != "devnet"
|
if profile.wallet.cluster != "devnet"
|
||||||
|| !profile.wallet.devnet_send_enabled
|
|| !profile.execution.devnet_send_enabled
|
||||||
|| !profile.execution.require_simulation
|
|| !profile.execution.require_simulation
|
||||||
{
|
{
|
||||||
return std::result::Result::Err(ks_core::Error::config(
|
return std::result::Result::Err(ks_core::Error::config(
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: ks-pipeline-demo-scenarios/src/spl/token_2022/devnet_execution.rs
|
// 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.
|
//! Devnet Token-2022 execution with stateful and canonical post-validation.
|
||||||
|
|
||||||
@@ -882,7 +882,7 @@ fn validate_profile(
|
|||||||
"Token-2022 Devnet orchestration requires simulation",
|
"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(
|
return std::result::Result::Err(ks_core::Error::config(
|
||||||
"Devnet transaction submission is disabled by the wallet profile",
|
"Devnet transaction submission is disabled by the wallet profile",
|
||||||
));
|
));
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: ks-pipeline-demo-scenarios/src/spl/token_2022/metadata/campaign.rs
|
// 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.
|
//! 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::Some(value) => value,
|
||||||
std::option::Option::None => panic!("workspace root cannot be resolved"),
|
std::option::Option::None => panic!("workspace root cannot be resolved"),
|
||||||
};
|
};
|
||||||
let config_path = match std::env::var("KS_DEVNET_CONFIG_PATH") {
|
let config = crate::load_demo_scenario_config(workspace_root)
|
||||||
std::result::Result::Ok(value) => {
|
.unwrap_or_else(|error| panic!("Devnet configuration loading failed: {error}"));
|
||||||
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 profile_name = std::env::var("KS_DEVNET_PROFILE").ok();
|
let profile_name = std::env::var("KS_DEVNET_PROFILE").ok();
|
||||||
let mut profile = crate::resolve_demo_devnet_profile(&config, profile_name.as_deref())
|
let mut profile = crate::resolve_demo_devnet_profile(&config, profile_name.as_deref())
|
||||||
.unwrap_or_else(|error| panic!("Devnet profile resolution failed: {error}"));
|
.unwrap_or_else(|error| panic!("Devnet profile resolution failed: {error}"));
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: ks-pipeline-demo-scenarios/src/spl/token_2022/metadata/fixture.rs
|
// 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.
|
//! 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",
|
"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(
|
return std::result::Result::Err(ks_core::Error::config(
|
||||||
"Token-2022 metadata fixture requires enabled Devnet submission and explicit operator confirmation",
|
"Token-2022 metadata fixture requires enabled Devnet submission and explicit operator confirmation",
|
||||||
));
|
));
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# file: scripts/audit_khadhroony_workspace_rules.py
|
# file: scripts/audit_khadhroony_workspace_rules.py
|
||||||
# version: 22
|
# version: 23
|
||||||
|
|
||||||
"""Audit mechanically verifiable rules specific to khadhroony-bot3."""
|
"""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_]*)")
|
placeholder = re.compile(r"\$\{([A-Z][A-Z0-9_]*)")
|
||||||
for relative in [
|
for relative in [
|
||||||
"config/kb-app-demo-desktop.default.config.json",
|
"config/kb-app-demo-desktop.default.config.json",
|
||||||
"config/ks-pipeline-demo-scenarios.default.config.json",
|
|
||||||
"config/logging.config.json",
|
"config/logging.config.json",
|
||||||
"config/transport.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",
|
||||||
"config/example.kb-app-demo-desktop.default.config.json",
|
"config/example.kb-app-demo-desktop.default.config.json",
|
||||||
"config/example.logging.config.json",
|
"config/example.logging.config.json",
|
||||||
"config/example.transport.config.json",
|
"config/example.transport.config.json",
|
||||||
"config/example.listeners.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
|
config = root / relative
|
||||||
if not config.is_file():
|
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]:
|
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] = []
|
violations: list[Violation] = []
|
||||||
required_files = [
|
required_files = [
|
||||||
"config/kb-app-demo-desktop.default.config.json",
|
"config/kb-app-demo-desktop.default.config.json",
|
||||||
"config/ks-pipeline-demo-scenarios.default.config.json",
|
|
||||||
"config/logging.config.json",
|
"config/logging.config.json",
|
||||||
"config/transport.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",
|
||||||
"config/example.kb-app-demo-desktop.default.config.json",
|
"config/example.kb-app-demo-desktop.default.config.json",
|
||||||
"config/example.logging.config.json",
|
"config/example.logging.config.json",
|
||||||
"config/example.transport.config.json",
|
"config/example.transport.config.json",
|
||||||
"config/example.listeners.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/composition.config.schema.json",
|
||||||
"config/schemas/logging.config.schema.json",
|
"config/schemas/logging.config.schema.json",
|
||||||
"config/schemas/transport.config.schema.json",
|
"config/schemas/transport.config.schema.json",
|
||||||
"config/schemas/listeners.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",
|
"config/schemas/resolved.app.config.schema.json",
|
||||||
]
|
]
|
||||||
for relative in required_files:
|
for relative in required_files:
|
||||||
if (root / relative).is_file():
|
if (root / relative).is_file():
|
||||||
continue
|
continue
|
||||||
violations.append(
|
violations.append(Violation("KH_CFG001", relative, 1, "required split configuration file is missing"))
|
||||||
Violation(
|
composition_path = root / "config/kb-app-demo-desktop.default.config.json"
|
||||||
"KH_CFG001",
|
if composition_path.is_file():
|
||||||
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
|
|
||||||
try:
|
try:
|
||||||
composition = json.loads(path.read_text(encoding="utf-8"))
|
composition = json.loads(composition_path.read_text(encoding="utf-8"))
|
||||||
except json.JSONDecodeError:
|
except json.JSONDecodeError:
|
||||||
composition = None
|
composition = None
|
||||||
if not isinstance(composition, dict):
|
if isinstance(composition, dict):
|
||||||
continue
|
sources = composition.get("sources")
|
||||||
sources = composition.get("sources")
|
expected_sources = {"logging", "transport", "listeners", "store", "wallet", "execution"}
|
||||||
if not isinstance(sources, dict) or set(sources) != {"logging", "transport", "listeners"}:
|
if not isinstance(sources, dict) or set(sources) != expected_sources:
|
||||||
violations.append(
|
violations.append(Violation("KH_CFG002", composition_path.relative_to(root).as_posix(), 1, "binary composition must reference all six shared configuration sources"))
|
||||||
Violation(
|
for index, profile in enumerate(composition.get("profiles", []), 1):
|
||||||
"KH_CFG002",
|
if not isinstance(profile, dict):
|
||||||
relative,
|
continue
|
||||||
1,
|
forbidden = {"solana", "logging", "database", "data", "wallet", "execution"}
|
||||||
"binary composition must reference logging, transport and listeners sources",
|
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"]:
|
||||||
for index, profile in enumerate(composition.get("profiles", []), 1):
|
if key in profile and (not isinstance(profile.get(key), str) or not profile.get(key, "").strip()):
|
||||||
if not isinstance(profile, dict):
|
violations.append(Violation("KH_CFG004", composition_path.relative_to(root).as_posix(), index, f"optional composition override `{key}` must be non-empty when present"))
|
||||||
continue
|
shared = [
|
||||||
if "solana" in profile or "logging" in profile:
|
"config/logging.config.json",
|
||||||
violations.append(
|
"config/transport.config.json",
|
||||||
Violation(
|
"config/listeners.config.json",
|
||||||
"KH_CFG003",
|
"config/store.config.json",
|
||||||
relative,
|
"config/wallet.config.json",
|
||||||
index,
|
"config/execution.config.json",
|
||||||
"composition profiles must reference transport/listener/logging profiles instead of embedding them",
|
]
|
||||||
)
|
for relative in shared:
|
||||||
)
|
|
||||||
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"]:
|
|
||||||
path = root / relative
|
path = root / relative
|
||||||
if not path.is_file():
|
if not path.is_file():
|
||||||
continue
|
continue
|
||||||
@@ -1040,28 +1028,47 @@ def audit_configuration_split(root: pathlib.Path) -> list[Violation]:
|
|||||||
document = None
|
document = None
|
||||||
if not isinstance(document, dict):
|
if not isinstance(document, dict):
|
||||||
continue
|
continue
|
||||||
if not isinstance(document.get("active_profile"), str):
|
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 active_profile"))
|
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"):
|
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"))
|
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 = [
|
legacy = [
|
||||||
"config/app.config.json",
|
"config/app.config.json",
|
||||||
"config/example.app.config.json",
|
"config/example.app.config.json",
|
||||||
"config/schemas/app.config.schema.json",
|
"config/schemas/app.config.schema.json",
|
||||||
"config/example.config.json",
|
"config/example.config.json",
|
||||||
"config/schema.config.json",
|
"config/schema.config.json",
|
||||||
|
"config/ks-pipeline-demo-scenarios.default.config.json",
|
||||||
]
|
]
|
||||||
for relative in legacy:
|
for relative in legacy:
|
||||||
if not (root / relative).exists():
|
if not (root / relative).exists():
|
||||||
continue
|
continue
|
||||||
violations.append(
|
violations.append(Violation("KH_CFG007", relative, 1, "legacy or unnecessary configuration composition file must be removed"))
|
||||||
Violation(
|
|
||||||
"KH_CFG007",
|
|
||||||
relative,
|
|
||||||
1,
|
|
||||||
"legacy monolithic application configuration file must be removed",
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return violations
|
return violations
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,7 @@
|
|||||||
"name": "local_devnet",
|
"name": "local_devnet",
|
||||||
"app": {
|
"app": {
|
||||||
"name": "khadhroony-bot3",
|
"name": "khadhroony-bot3",
|
||||||
"environment": "development",
|
"environment": "development"
|
||||||
"auto_reconnect_default": false
|
|
||||||
},
|
},
|
||||||
"database": {
|
"database": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
@@ -26,10 +25,6 @@
|
|||||||
"use_wal": true
|
"use_wal": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"data": {
|
|
||||||
"wallets_directory": "wallets",
|
|
||||||
"logs_directory": "logs"
|
|
||||||
},
|
|
||||||
"solana": {
|
"solana": {
|
||||||
"http_endpoints": [
|
"http_endpoints": [
|
||||||
{
|
{
|
||||||
@@ -209,11 +204,7 @@
|
|||||||
"cluster": "devnet",
|
"cluster": "devnet",
|
||||||
"temporary_wallet_enabled": true,
|
"temporary_wallet_enabled": true,
|
||||||
"temporary_wallet_alias": "local-devnet-operator",
|
"temporary_wallet_alias": "local-devnet-operator",
|
||||||
"temporary_wallet_persist": true,
|
"temporary_wallet_persist": true
|
||||||
"localnet_send_enabled": false,
|
|
||||||
"devnet_send_enabled": true,
|
|
||||||
"testnet_send_enabled": false,
|
|
||||||
"mainnet_send_enabled": false
|
|
||||||
},
|
},
|
||||||
"execution": {
|
"execution": {
|
||||||
"dry_run_default": true,
|
"dry_run_default": true,
|
||||||
@@ -229,7 +220,11 @@
|
|||||||
"send_max_retries": 3,
|
"send_max_retries": 3,
|
||||||
"confirmation_poll_interval_ms": 500,
|
"confirmation_poll_interval_ms": 500,
|
||||||
"confirmation_max_attempts": 120,
|
"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": {
|
"demo": {
|
||||||
"live_demo_enabled": true,
|
"live_demo_enabled": true,
|
||||||
|
|||||||
@@ -5,8 +5,7 @@
|
|||||||
"name": "local_devnet",
|
"name": "local_devnet",
|
||||||
"app": {
|
"app": {
|
||||||
"name": "khadhroony-bot3",
|
"name": "khadhroony-bot3",
|
||||||
"environment": "development",
|
"environment": "development"
|
||||||
"auto_reconnect_default": false
|
|
||||||
},
|
},
|
||||||
"database": {
|
"database": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
@@ -26,10 +25,6 @@
|
|||||||
"use_wal": true
|
"use_wal": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"data": {
|
|
||||||
"wallets_directory": "wallets",
|
|
||||||
"logs_directory": "logs"
|
|
||||||
},
|
|
||||||
"solana": {
|
"solana": {
|
||||||
"http_endpoints": [
|
"http_endpoints": [
|
||||||
{
|
{
|
||||||
@@ -209,11 +204,7 @@
|
|||||||
"cluster": "devnet",
|
"cluster": "devnet",
|
||||||
"temporary_wallet_enabled": true,
|
"temporary_wallet_enabled": true,
|
||||||
"temporary_wallet_alias": "local-devnet-operator",
|
"temporary_wallet_alias": "local-devnet-operator",
|
||||||
"temporary_wallet_persist": true,
|
"temporary_wallet_persist": true
|
||||||
"localnet_send_enabled": false,
|
|
||||||
"devnet_send_enabled": true,
|
|
||||||
"testnet_send_enabled": false,
|
|
||||||
"mainnet_send_enabled": false
|
|
||||||
},
|
},
|
||||||
"execution": {
|
"execution": {
|
||||||
"dry_run_default": true,
|
"dry_run_default": true,
|
||||||
@@ -229,7 +220,11 @@
|
|||||||
"send_max_retries": 3,
|
"send_max_retries": 3,
|
||||||
"confirmation_poll_interval_ms": 500,
|
"confirmation_poll_interval_ms": 500,
|
||||||
"confirmation_max_attempts": 120,
|
"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": {
|
"demo": {
|
||||||
"live_demo_enabled": true,
|
"live_demo_enabled": true,
|
||||||
@@ -240,8 +235,7 @@
|
|||||||
"name": "mainnet_research",
|
"name": "mainnet_research",
|
||||||
"app": {
|
"app": {
|
||||||
"name": "khadhroony-bot3",
|
"name": "khadhroony-bot3",
|
||||||
"environment": "research",
|
"environment": "research"
|
||||||
"auto_reconnect_default": false
|
|
||||||
},
|
},
|
||||||
"database": {
|
"database": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
@@ -261,10 +255,6 @@
|
|||||||
"use_wal": true
|
"use_wal": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"data": {
|
|
||||||
"wallets_directory": "wallets",
|
|
||||||
"logs_directory": "logs"
|
|
||||||
},
|
|
||||||
"solana": {
|
"solana": {
|
||||||
"http_endpoints": [
|
"http_endpoints": [
|
||||||
{
|
{
|
||||||
@@ -644,11 +634,7 @@
|
|||||||
"cluster": "mainnet-beta",
|
"cluster": "mainnet-beta",
|
||||||
"temporary_wallet_enabled": false,
|
"temporary_wallet_enabled": false,
|
||||||
"temporary_wallet_alias": "mainnet_research-disabled",
|
"temporary_wallet_alias": "mainnet_research-disabled",
|
||||||
"temporary_wallet_persist": false,
|
"temporary_wallet_persist": false
|
||||||
"localnet_send_enabled": false,
|
|
||||||
"devnet_send_enabled": false,
|
|
||||||
"testnet_send_enabled": false,
|
|
||||||
"mainnet_send_enabled": false
|
|
||||||
},
|
},
|
||||||
"execution": {
|
"execution": {
|
||||||
"dry_run_default": true,
|
"dry_run_default": true,
|
||||||
@@ -664,7 +650,11 @@
|
|||||||
"send_max_retries": 3,
|
"send_max_retries": 3,
|
||||||
"confirmation_poll_interval_ms": 500,
|
"confirmation_poll_interval_ms": 500,
|
||||||
"confirmation_max_attempts": 120,
|
"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": {
|
"demo": {
|
||||||
"live_demo_enabled": true,
|
"live_demo_enabled": true,
|
||||||
@@ -675,8 +665,7 @@
|
|||||||
"name": "mainnet",
|
"name": "mainnet",
|
||||||
"app": {
|
"app": {
|
||||||
"name": "khadhroony-bot3",
|
"name": "khadhroony-bot3",
|
||||||
"environment": "mainnet",
|
"environment": "mainnet"
|
||||||
"auto_reconnect_default": false
|
|
||||||
},
|
},
|
||||||
"database": {
|
"database": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
@@ -696,10 +685,6 @@
|
|||||||
"use_wal": true
|
"use_wal": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"data": {
|
|
||||||
"wallets_directory": "wallets",
|
|
||||||
"logs_directory": "logs"
|
|
||||||
},
|
|
||||||
"solana": {
|
"solana": {
|
||||||
"http_endpoints": [
|
"http_endpoints": [
|
||||||
{
|
{
|
||||||
@@ -1079,11 +1064,7 @@
|
|||||||
"cluster": "mainnet-beta",
|
"cluster": "mainnet-beta",
|
||||||
"temporary_wallet_enabled": false,
|
"temporary_wallet_enabled": false,
|
||||||
"temporary_wallet_alias": "mainnet-disabled",
|
"temporary_wallet_alias": "mainnet-disabled",
|
||||||
"temporary_wallet_persist": false,
|
"temporary_wallet_persist": false
|
||||||
"localnet_send_enabled": false,
|
|
||||||
"devnet_send_enabled": false,
|
|
||||||
"testnet_send_enabled": false,
|
|
||||||
"mainnet_send_enabled": false
|
|
||||||
},
|
},
|
||||||
"execution": {
|
"execution": {
|
||||||
"dry_run_default": true,
|
"dry_run_default": true,
|
||||||
@@ -1099,7 +1080,11 @@
|
|||||||
"send_max_retries": 3,
|
"send_max_retries": 3,
|
||||||
"confirmation_poll_interval_ms": 500,
|
"confirmation_poll_interval_ms": 500,
|
||||||
"confirmation_max_attempts": 120,
|
"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": {
|
"demo": {
|
||||||
"live_demo_enabled": true,
|
"live_demo_enabled": true,
|
||||||
|
|||||||
Reference in New Issue
Block a user