v0.5.1-pre.008

This commit is contained in:
2026-08-10 11:21:07 +02:00
parent a2820062eb
commit 0e05c660c6
90 changed files with 2490 additions and 1613 deletions

View File

@@ -1,5 +1,5 @@
# file: Cargo.toml # file: Cargo.toml
# version: 51 # version: 52
[workspace] [workspace]
resolver = "3" resolver = "3"
@@ -18,7 +18,7 @@ members = [
] ]
[workspace.package] [workspace.package]
version = "0.5.1-pre.7" version = "0.5.1-pre.8"
edition = "2024" edition = "2024"
license = "MIT" license = "MIT"
repository = "https://git.sasedev.com/Sasedev/khadhroony-bot3" repository = "https://git.sasedev.com/Sasedev/khadhroony-bot3"

View File

@@ -1,5 +1,5 @@
<!-- file: README.md --> <!-- file: README.md -->
<!-- version: 22 --> <!-- version: 23 -->
# Khadhroony Bot3 # Khadhroony Bot3
@@ -23,6 +23,8 @@ Le workspace contient onze crates :
La série `0.5.x` prépare la séparation explicite entre le domaine applicatif `khadhroony-bot` et les bibliothèques généralistes `khadhroony-solana`. Depuis `0.5.1-pre.002`, les dix crates généralistes utilisent les noms `ks-*` / `ks_*`, tandis que `kb-app-demo-desktop` conserve son nom et reste une application du workspace Bot. Le workspace, le dépôt et le répertoire racine restent nommés `khadhroony-bot3` pendant cette migration et ne doivent pas être renommés avant `1.0` ou une version ultérieure explicitement dédiée. La politique de migration est définie dans [`docs/decisions/KHADHROONY_SOLANA_NAMESPACE_POLICY.md`](docs/decisions/KHADHROONY_SOLANA_NAMESPACE_POLICY.md). La série `0.5.x` prépare la séparation explicite entre le domaine applicatif `khadhroony-bot` et les bibliothèques généralistes `khadhroony-solana`. Depuis `0.5.1-pre.002`, les dix crates généralistes utilisent les noms `ks-*` / `ks_*`, tandis que `kb-app-demo-desktop` conserve son nom et reste une application du workspace Bot. Le workspace, le dépôt et le répertoire racine restent nommés `khadhroony-bot3` pendant cette migration et ne doivent pas être renommés avant `1.0` ou une version ultérieure explicitement dédiée. La politique de migration est définie dans [`docs/decisions/KHADHROONY_SOLANA_NAMESPACE_POLICY.md`](docs/decisions/KHADHROONY_SOLANA_NAMESPACE_POLICY.md).
Depuis `0.5.1-pre.008`, les contrats de configuration source/runtime susceptibles de contenir des secrets restent backend-only et ne sont ni sérialisables ni `Debug` par défaut. Les crates `ks-config` et `ks-lib` ne génèrent plus de bindings TS-RS ; les surfaces TypeScript/Tauri sont possédées par les applications via des DTO explicites.
Références : Références :
- [`docs/architecture/ARCHITECTURE.md`](docs/architecture/ARCHITECTURE.md) ; - [`docs/architecture/ARCHITECTURE.md`](docs/architecture/ARCHITECTURE.md) ;

View File

@@ -1,5 +1,5 @@
<!-- file: config/README.md --> <!-- file: config/README.md -->
<!-- version: 22 --> <!-- version: 24 -->
# Configuration locale # Configuration locale
@@ -36,15 +36,17 @@ config/
├── store.config.json ├── store.config.json
├── wallet.config.json ├── wallet.config.json
├── execution.config.json ├── execution.config.json
├── example.kb-app-demo-desktop.default.config.json ├── exemples/
├── example.logging.config.json ├── example.kb-app-demo-desktop.default.config.json
├── example.transport.config.json ├── example.logging.config.json
├── example.listeners.config.json ├── example.transport.config.json
├── example.store.config.json ├── example.listeners.config.json
├── example.wallet.config.json ├── example.store.config.json
├── example.execution.config.json ├── example.wallet.config.json
│ └── example.execution.config.json
└── schemas/ └── schemas/
├── composition.config.schema.json ├── composition.config.schema.json
├── kb-app-demo-desktop.application.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
@@ -54,7 +56,7 @@ config/
└── resolved.app.config.schema.json └── resolved.app.config.schema.json
``` ```
`resolved.app.config.schema.json` décrit uniquement le contrat transitoire `AppConfig/ProfileConfig` reconstruit en mémoire. Aucun binaire ne charge ce schéma comme document source applicatif. `resolved.app.config.schema.json` décrit uniquement le contrat transitoire `AppConfig/ProfileConfig` reconstruit en mémoire. Aucun binaire ne charge ce schéma comme document source applicatif. La section `application` d'une composition reste opaque pour `ks-config`; `kb-app-demo-desktop` la valide avec `config/schemas/kb-app-demo-desktop.application.config.schema.json`.
## Defaults et overrides ## Defaults et overrides
@@ -110,14 +112,20 @@ Les defaults portent les timeouts, capacités de channels et `auto_reconnect`. U
`store.config.json` possède la sélection et les paramètres PostgreSQL/SQLite. Le split reste structurel : aucune migration SQL n'est introduite par `0.5.1`. `store.config.json` possède la sélection et les paramètres PostgreSQL/SQLite. Le split reste structurel : aucune migration SQL n'est introduite par `0.5.1`.
## Frontière source/runtime/public/diagnostic
Les documents source et le contrat runtime résolu peuvent contenir des valeurs sensibles après résolution des placeholders. Ils restent backend-only et ne sont ni sérialisables ni `Debug` par défaut. Les applications construisent explicitement leurs DTO publics et diagnostics bornés, sans sérialiser puis masquer un `AppConfig/ProfileConfig` complet.
Une valeur composée hérite de la sensibilité la plus forte de ses placeholders (`Secret > Internal > Public`). Une URL ou un DSN incorporant un `KS_SECRET_*`/`KB_SECRET_*` est donc secret même si le champ final ne porte pas le mot `SECRET`.
## Variables d'environnement ## 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 séparation source/runtime/public/diagnostic et le camouflage systématique sont traités après ce split. Les secrets ne sont jamais écrits en clair dans le dépôt. Depuis `0.5.1-pre.008`, les contrats source/runtime sensibles restent backend-only, tandis que les applications exposent uniquement des DTO publics ou diagnostics explicitement bornés.
## Exemples et schémas ## Exemples et schémas
Les exemples sous `config/` sont des références conformes et ne sont pas chargés automatiquement. Tous les schémas actifs résident exclusivement sous `config/schemas/`. Les exemples sous `config/exemples/` sont des références conformes et ne sont pas chargés automatiquement. Tous les schémas actifs résident exclusivement sous `config/schemas/`.
Les fichiers historiques `app.config.json`, `example.app.config.json`, `schemas/app.config.schema.json`, `example.config.json`, `schema.config.json` et `ks-pipeline-demo-scenarios.default.config.json` sont obsolètes dans l'architecture courante. 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.

View File

@@ -11,13 +11,13 @@
"profiles": [ "profiles": [
{ {
"name": "local_devnet", "name": "local_devnet",
"app": { "application": {
"name": "khadhroony-bot3", "name": "khadhroony-bot3",
"environment": "development" "environment": "development",
}, "demo": {
"demo": { "live_demo_enabled": true,
"live_demo_enabled": true, "trading_demo_enabled": false
"trading_demo_enabled": false }
} }
} }
] ]

View File

@@ -11,24 +11,24 @@
"profiles": [ "profiles": [
{ {
"name": "local_devnet", "name": "local_devnet",
"app": { "application": {
"name": "khadhroony-bot3", "name": "khadhroony-bot3",
"environment": "development" "environment": "development",
}, "demo": {
"demo": { "live_demo_enabled": true,
"live_demo_enabled": true, "trading_demo_enabled": false
"trading_demo_enabled": false }
} }
}, },
{ {
"name": "mainnet_research", "name": "mainnet_research",
"app": { "application": {
"name": "khadhroony-bot3", "name": "khadhroony-bot3",
"environment": "research" "environment": "research",
}, "demo": {
"demo": { "live_demo_enabled": true,
"live_demo_enabled": true, "trading_demo_enabled": false
"trading_demo_enabled": false }
}, },
"logging_profile": "mainnet_research", "logging_profile": "mainnet_research",
"transport_profile": "mainnet_research", "transport_profile": "mainnet_research",
@@ -39,13 +39,13 @@
}, },
{ {
"name": "mainnet", "name": "mainnet",
"app": { "application": {
"name": "khadhroony-bot3", "name": "khadhroony-bot3",
"environment": "mainnet" "environment": "mainnet",
}, "demo": {
"demo": { "live_demo_enabled": true,
"live_demo_enabled": true, "trading_demo_enabled": false
"trading_demo_enabled": false }
}, },
"logging_profile": "mainnet", "logging_profile": "mainnet",
"transport_profile": "mainnet", "transport_profile": "mainnet",

View File

@@ -29,38 +29,6 @@
"type": "string", "type": "string",
"minLength": 1 "minLength": 1
}, },
"app_section": {
"type": "object",
"additionalProperties": false,
"required": [
"name",
"environment"
],
"properties": {
"name": {
"$ref": "#/$defs/non_empty_string"
},
"environment": {
"$ref": "#/$defs/non_empty_string"
}
}
},
"demo": {
"type": "object",
"additionalProperties": false,
"required": [
"live_demo_enabled",
"trading_demo_enabled"
],
"properties": {
"live_demo_enabled": {
"type": "boolean"
},
"trading_demo_enabled": {
"type": "boolean"
}
}
},
"sources": { "sources": {
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
@@ -97,19 +65,14 @@
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
"required": [ "required": [
"name", "name"
"app",
"demo"
], ],
"properties": { "properties": {
"name": { "name": {
"$ref": "#/$defs/non_empty_string" "$ref": "#/$defs/non_empty_string"
}, },
"app": { "application": {
"$ref": "#/$defs/app_section" "type": "object"
},
"demo": {
"$ref": "#/$defs/demo"
}, },
"logging_profile": { "logging_profile": {
"$ref": "#/$defs/non_empty_string" "$ref": "#/$defs/non_empty_string"

View File

@@ -0,0 +1,43 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://khadhroony.local/schemas/kb-app-demo-desktop.application.config.schema.json",
"title": "Khadhroony Bot desktop application composition fragment",
"type": "object",
"additionalProperties": false,
"required": [
"name",
"environment",
"demo"
],
"properties": {
"name": {
"type": "string",
"minLength": 1
},
"environment": {
"type": "string",
"minLength": 1
},
"demo": {
"$ref": "#/$defs/demo"
}
},
"$defs": {
"demo": {
"type": "object",
"additionalProperties": false,
"required": [
"live_demo_enabled",
"trading_demo_enabled"
],
"properties": {
"live_demo_enabled": {
"type": "boolean"
},
"trading_demo_enabled": {
"type": "boolean"
}
}
}
}
}

View File

@@ -30,20 +30,15 @@
"additionalProperties": false, "additionalProperties": false,
"required": [ "required": [
"name", "name",
"app",
"database", "database",
"solana", "solana",
"wallet", "wallet",
"execution", "execution"
"demo"
], ],
"properties": { "properties": {
"name": { "name": {
"$ref": "#/$defs/non_empty_string" "$ref": "#/$defs/non_empty_string"
}, },
"app": {
"$ref": "#/$defs/app_section"
},
"database": { "database": {
"$ref": "#/$defs/database" "$ref": "#/$defs/database"
}, },
@@ -55,25 +50,6 @@
}, },
"execution": { "execution": {
"$ref": "#/$defs/execution" "$ref": "#/$defs/execution"
},
"demo": {
"$ref": "#/$defs/demo"
}
}
},
"app_section": {
"type": "object",
"additionalProperties": false,
"required": [
"name",
"environment"
],
"properties": {
"name": {
"$ref": "#/$defs/non_empty_string"
},
"environment": {
"$ref": "#/$defs/non_empty_string"
} }
} }
}, },
@@ -608,22 +584,6 @@
"type": "boolean" "type": "boolean"
} }
} }
},
"demo": {
"type": "object",
"additionalProperties": false,
"required": [
"live_demo_enabled",
"trading_demo_enabled"
],
"properties": {
"live_demo_enabled": {
"type": "boolean"
},
"trading_demo_enabled": {
"type": "boolean"
}
}
} }
} }
} }

View File

@@ -559,7 +559,7 @@ Dans **Décodeur spl_token — TransferChecked** :
| Compte source | `$KS_DEVNET_CLASSIC_SOURCE_ATA` | | Compte source | `$KS_DEVNET_CLASSIC_SOURCE_ATA` |
| Mint | `$KS_DEVNET_CLASSIC_MINT` | | Mint | `$KS_DEVNET_CLASSIC_MINT` |
| Compte destination | `$KS_DEVNET_CLASSIC_DESTINATION_ATA` | | Compte destination | `$KS_DEVNET_CLASSIC_DESTINATION_ATA` |
| Autorité simple | `$KS_PUBLIC_DEVNET_WALLET_ADDRESS` | | Autorité simple | `$KS_PUBLIC_DEVNET_WALLET_ADDRESS` |
| Montant brut exact | `1000000000` pour 1 token avec 9 décimales | | Montant brut exact | `1000000000` pour 1 token avec 9 décimales |
| Decimals | `9` | | Decimals | `9` |

View File

@@ -5,19 +5,19 @@
## 1. Inventaire ## 1. Inventaire
| Crate | Type | Responsabilité principale | État documentaire | | Crate | Type | Responsabilité principale | État documentaire |
|------------------------------|------------------------|--------------------------------------------------------------------|------------------------------------------| |------------------------------|------------------------|--------------------------------------------------------------------------------------|------------------------------------------|
| `ks-core` | bibliothèque | erreurs, résultat et identité de module partagés | README/TODO/USAGE/CHANGELOG présents | | `ks-core` | bibliothèque | erreurs, résultat et identité de module partagés | README/TODO/USAGE/CHANGELOG présents |
| `ks-config` | bibliothèque | compositions binaires, documents JSON partagés, environnement, validation et profils | migration/restructuration en `0.5.1` | | `ks-config` | bibliothèque | compositions binaires, documents JSON partagés, environnement, validation et profils | migration/restructuration en `0.5.1` |
| `ks-lib` | bibliothèque | modèles, décodeurs, exécuteurs et matérialisateurs consolidés | README/TODO/USAGE/CHANGELOG présents | | `ks-lib` | bibliothèque | modèles, décodeurs, exécuteurs et matérialisateurs consolidés | README/TODO/USAGE/CHANGELOG présents |
| `ks-logging` | bibliothèque | initialisation du logging et du tracing | README/TODO/USAGE/CHANGELOG présents | | `ks-logging` | bibliothèque | initialisation du logging et du tracing | README/TODO/USAGE/CHANGELOG présents |
| `ks-program-ids` | bibliothèque | registre des programmes et comptes Solana connus | README/TODO/USAGE/CHANGELOG présents | | `ks-program-ids` | bibliothèque | registre des programmes et comptes Solana connus | README/TODO/USAGE/CHANGELOG présents |
| `ks-pipeline` | bibliothèque | backfill, extraction, replay, stateful, préflight et orchestration | README/TODO/USAGE/CHANGELOG présents | | `ks-pipeline` | bibliothèque | backfill, extraction, replay, stateful, préflight et orchestration | README/TODO/USAGE/CHANGELOG présents |
| `ks-pipeline-demo-scenarios` | bibliothèque + binaire | scénarios Devnet réutilisables et CLI | nom `ks-*` actif ; audit `0.5.4` | | `ks-pipeline-demo-scenarios` | bibliothèque + binaire | scénarios Devnet réutilisables et CLI | nom `ks-*` actif ; audit `0.5.4` |
| `ks-onchain-transport` | bibliothèque | transports RPC HTTP/WebSocket et pools dendpoints | README/TODO/USAGE/CHANGELOG présents | | `ks-onchain-transport` | bibliothèque | transports RPC HTTP/WebSocket et pools dendpoints | README/TODO/USAGE/CHANGELOG présents |
| `ks-store` | bibliothèque | contrats de stockage et adaptateur PostgreSQL | nom `ks-*` actif ; normalisation `0.5.3` | | `ks-store` | bibliothèque | contrats de stockage et adaptateur PostgreSQL | nom `ks-*` actif ; normalisation `0.5.3` |
| `ks-wallet` | bibliothèque | wallet temporaire et frontière de signataire | nom `ks-*` actif ; refonte `0.5.2` | | `ks-wallet` | bibliothèque | wallet temporaire et frontière de signataire | nom `ks-*` actif ; refonte `0.5.2` |
| `kb-app-demo-desktop` | bibliothèque + binaire | application de démonstration Tauri | documentée ; réconciliation en `0.5.4` | | `kb-app-demo-desktop` | bibliothèque + binaire | application de démonstration Tauri | documentée ; réconciliation en `0.5.4` |
La table décrit les noms physiques actifs depuis `0.5.1-pre.002`. La migration a renommé les dix crates généralistes en `ks-core`, `ks-config`, `ks-lib`, `ks-logging`, `ks-program-ids`, `ks-pipeline`, `ks-pipeline-demo-scenarios`, `ks-onchain-transport`, `ks-store` et `ks-wallet`. `kb-app-demo-desktop` conserve son nom parce quil appartient au domaine applicatif Bot. Voir [`../decisions/KHADHROONY_SOLANA_NAMESPACE_POLICY.md`](../decisions/KHADHROONY_SOLANA_NAMESPACE_POLICY.md). La table décrit les noms physiques actifs depuis `0.5.1-pre.002`. La migration a renommé les dix crates généralistes en `ks-core`, `ks-config`, `ks-lib`, `ks-logging`, `ks-program-ids`, `ks-pipeline`, `ks-pipeline-demo-scenarios`, `ks-onchain-transport`, `ks-store` et `ks-wallet`. `kb-app-demo-desktop` conserve son nom parce quil appartient au domaine applicatif Bot. Voir [`../decisions/KHADHROONY_SOLANA_NAMESPACE_POLICY.md`](../decisions/KHADHROONY_SOLANA_NAMESPACE_POLICY.md).

View File

@@ -25,18 +25,18 @@ Le renommage des bibliothèques internes ne renomme pas le workspace, le dépôt
La migration `0.5.1-pre.002` renomme les dix crates généralistes suivantes : La migration `0.5.1-pre.002` renomme les dix crates généralistes suivantes :
| Nom `0.5.0` | Nom cible | | Nom `0.5.0` | Nom cible |
|---|---| |------------------------------|------------------------------|
| `kb-core` | `ks-core` | | `kb-core` | `ks-core` |
| `kb-config` | `ks-config` | | `kb-config` | `ks-config` |
| `kb-lib` | `ks-lib` | | `kb-lib` | `ks-lib` |
| `kb-logging` | `ks-logging` | | `kb-logging` | `ks-logging` |
| `kb-program-ids` | `ks-program-ids` | | `kb-program-ids` | `ks-program-ids` |
| `kb-pipeline` | `ks-pipeline` | | `kb-pipeline` | `ks-pipeline` |
| `kb-pipeline-demo-scenarios` | `ks-pipeline-demo-scenarios` | | `kb-pipeline-demo-scenarios` | `ks-pipeline-demo-scenarios` |
| `kb-onchain-transport` | `ks-onchain-transport` | | `kb-onchain-transport` | `ks-onchain-transport` |
| `kb-store` | `ks-store` | | `kb-store` | `ks-store` |
| `kb-wallet` | `ks-wallet` | | `kb-wallet` | `ks-wallet` |
Les identifiants Rust correspondants migrent dans le même delta vers `ks_*` : par exemple `kb_config` devient `ks_config` et `kb_pipeline_demo_scenarios` devient `ks_pipeline_demo_scenarios`. Les identifiants Rust correspondants migrent dans le même delta vers `ks_*` : par exemple `kb_config` devient `ks_config` et `kb_pipeline_demo_scenarios` devient `ks_pipeline_demo_scenarios`.

View File

@@ -1,5 +1,5 @@
<!-- file: docs/guides/CONFIGURATION.md --> <!-- file: docs/guides/CONFIGURATION.md -->
<!-- version: 7 --> <!-- version: 9 -->
# Guide de configuration # Guide de configuration
@@ -22,7 +22,7 @@ Documents partagés :
- `config/wallet.config.json` ; - `config/wallet.config.json` ;
- `config/execution.config.json`. - `config/execution.config.json`.
Chaque document possède un exemple `example.*.config.json`. Les schémas correspondants résident sous `config/schemas/`. Chaque document possède un exemple sous `config/exemples/` avec un nom `example.*.config.json`. Les schémas correspondants résident sous `config/schemas/`.
`.env`, ou le fichier sélectionné par `KS_ENV_FILE`, fournit les valeurs non versionnées. `.env`, ou le fichier sélectionné par `KS_ENV_FILE`, fournit les valeurs non versionnées.
@@ -145,22 +145,29 @@ appartiennent à `execution.config.json`, avec les limites de dépense, frais, s
## Contrat runtime transitoire ## Contrat runtime transitoire
Pendant `0.5.1`, `ks-config` reconstruit encore `AppConfig/ProfileConfig` afin de préserver les consommateurs existants. Il s'agit d'une projection runtime, pas d'un document source. Pendant `0.5.1`, `ks-config` reconstruit encore `AppConfig/ProfileConfig` afin de préserver les consommateurs backend existants. Il s'agit d'une projection runtime, pas d'un document source ni d'une surface de sortie. Depuis `pre.008`, ces contrats et les documents spécialisés susceptibles de contenir des valeurs résolues ne dérivent ni `serde::Serialize` ni `Debug`.
Les fixtures de compatibilité sont sous `test-fixtures/config/`. Elles ne doivent pas être chargées en production. Les fixtures de compatibilité sont sous `test-fixtures/config/`. Elles ne doivent pas être chargées en production.
## Frontière publique et diagnostic
Une application ne transmet jamais `AppConfig/ProfileConfig` directement. Elle construit :
- une projection publique explicitement typée, limitée aux champs autorisés ;
- un diagnostic borné contenant au plus des états tels que `configured`/`missing` pour les URLs, DSN et chemins sensibles ;
- aucune projection construite par sérialisation du runtime suivie d'un masquage a posteriori.
La sensibilité suit `Secret > Internal > Public`. Une valeur composée hérite de la sensibilité la plus forte des placeholders qu'elle contient ; une URL incorporant `${KS_SECRET_HELIUS_API_KEY}` est donc secrète même si son champ final est simplement `url`.
La section `application` d'une composition est opaque à `ks-config`. Le desktop la valide avec son schéma `config/schemas/kb-app-demo-desktop.application.config.schema.json`; un futur worker pourra posséder son propre schéma sans modifier `ks-config`.
## Frontière TS-RS ## Frontière TS-RS
L'audit `pre.007` confirme que le frontend du desktop n'importe directement aucun binding généré depuis `ks-config` ou `ks-lib`. Les dérivations TS-RS des crates généralistes seront donc réévaluées dans la prerelease suivante : les DTO réellement destinés à Tauri doivent être définis/wrappés dans l'application, sauf contrat TypeScript générique explicitement justifié. Depuis `pre.008`, `ks-config` et `ks-lib` ne dépendent plus de TS-RS et ne possèdent plus de bindings TypeScript générés. Les DTO traversant Tauri appartiennent à `kb-app-demo-desktop` ou à la future application concernée. Une exception dans une crate `ks-*` exige un contrat TypeScript générique indépendant de Tauri explicitement justifié et audité.
## Étape suivante ## Étape suivante
La prochaine prerelease traite ensemble : `pre.009` est une prerelease de clôture : réconciliation documentaire, audits finaux, nettoyage des TODO, archivage du plan/prompt et préparation de `0.5.2`.
- 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

View File

@@ -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: 7 --> <!-- version: 8 -->
# 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`. Les prereleases `pre.001` à `pre.004` ont fermé l'inventaire puis migré crates, identités techniques et variables d'environnement. `pre.005` a séparé le logging, `pre.006` a introduit les compositions de binaires et extrait transport/listeners, et `pre.007` termine maintenant la décomposition des responsabilités partagées avec store/wallet/execution, valeurs globales hors profils et defaults autonomes. `kb-app-demo-desktop` et le workspace racine restent nommés comme avant. Ce document est le plan temporaire de `0.5.1`. Les prereleases `pre.001` à `pre.004` ont fermé l'inventaire puis migré crates, identités techniques et variables d'environnement. `pre.005` à `pre.007` ont terminé le split logging/transport/listeners/store/wallet/execution, les compositions de binaires et les defaults autonomes. `pre.008` ferme maintenant les frontières source/runtime/public/diagnostic, la non-divulgation et TS-RS. `kb-app-demo-desktop` et le workspace racine restent nommés comme avant.
Les contrôles Rust et le démarrage Tauri de `pre.006` ont été validés par l'opérateur le 10 août 2026. Le démarrage confirme le chargement de `kb-app-demo-desktop.default.config.json`, du document logging séparé, des profils transport et du store, avec DSN PostgreSQL masqué dans les logs. `pre.007` doit préserver le comportement runtime tout en changeant l'ownership source des paramètres. Après `pre.007-delta-fix-002`, l'opérateur a validé `cargo fmt`, `cargo check --workspace`, `cargo clippy --all-targets`, l'audit workspace, `cargo test --workspace` et le démarrage Tauri le 10 août 2026. `pre.008` peut donc modifier uniquement les frontières de sortie/configuration sans rouvrir le split structurel déjà validé.
## 2. Invariant du workspace racine ## 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 quelle 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 quelle 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,25 +538,25 @@ La cible `KB_APP_DEMO_DESKTOP_CONFIG_PATH` remplace la transition `KS_CONFIG_PAT
### 7.3 Mapping guide opérateur Devnet — 17 noms ### 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
@@ -595,7 +595,7 @@ Le transport possède des classes WebSocket de defaults nommées. Chaque endpoin
Lorsqu'une composition référence un profil, `ks-config` doit prouver son existence avant de construire le runtime. Lorsqu'aucune composition n'est fournie, les noms des `default_profile` n'ont pas besoin d'être identiques : chaque document est résolu indépendamment. La composition ne devient pas une seconde surface de paramètres : les overrides de champ restent dans le contrat spécialisé qui les possède, et les globals explicitement configurables utilisent leurs variables d'environnement dédiées. Lorsqu'une composition référence un profil, `ks-config` doit prouver son existence avant de construire le runtime. Lorsqu'aucune composition n'est fournie, les noms des `default_profile` n'ont pas besoin d'être identiques : chaque document est résolu indépendamment. La composition ne devient pas une seconde surface de paramètres : les overrides de champ restent dans le contrat spécialisé qui les possède, et les globals explicitement configurables utilisent leurs variables d'environnement dédiées.
`AppConfig/ProfileConfig` demeure provisoirement un **contrat runtime résolu** pour préserver les consommateurs pendant la migration. Les champs applicatifs `app`/`demo` restent dans la composition exclusive du desktop pendant cette transition ; leur ownership Rust/public est traité avec les DTO applicatifs de `pre.008`, sans réintroduire un document généraliste monolithique. `AppConfig/ProfileConfig` demeure provisoirement un **contrat runtime résolu backend-only** pour préserver les consommateurs pendant la migration. Depuis `pre.008`, il ne contient plus les champs propres au desktop, ne dérive plus `serde::Serialize`/`Debug` et ne traverse plus Tauri. La section `application` de la composition est opaque à `ks-config`; le desktop la valide avec son propre schéma sans réintroduire un document généraliste monolithique.
## 9. Source, runtime, public et diagnostic ## 9. Source, runtime, public et diagnostic
@@ -606,38 +606,34 @@ La configuration doit distinguer explicitement :
3. **public** : DTO explicitement construit et strictement borné pour Tauri/TS-RS/UI ; 3. **public** : DTO explicitement construit et strictement borné pour Tauri/TS-RS/UI ;
4. **diagnostic** : surface explicitement demandée, pouvant montrer des valeurs `KS_*` internes non sensibles mais jamais une valeur `KS_SECRET_*`. 4. **diagnostic** : surface explicitement demandée, pouvant montrer des valeurs `KS_*` internes non sensibles mais jamais une valeur `KS_SECRET_*`.
### 9.1 Fuite actuelle à éliminer ### 9.1 Frontière appliquée en `pre.008`
`kb-app-demo-desktop/src/demo_config.rs` transporte actuellement dans `DemoConfigPayload` : `kb-app-demo-desktop/src/demo_config.rs` ne transporte plus `AppConfig` ni `ProfileConfig`. Il construit explicitement :
```text ```text
AppConfig complet runtime backend-only
ProfileConfig actif complet -> DemoConfigPublicPayload
schema_json -> DemoConfigDiagnosticPayload
``` ```
La configuration est déjà résolue avant la construction du payload, puis le frontend affiche les objets via JsonViewer. Cette frontière permet donc à un secret résolu d'atteindre Tauri/UI. La projection publique exclut URLs, DSN, chemins de stockage/wallet et politiques internes non destinées à l'UI. Le diagnostic borné ne transmet pour les valeurs sensibles que des états tels que `configured`/`missing`, plus les métadonnées internes explicitement autorisées.
La correction ne doit jamais suivre le modèle : La correction interdit le modèle :
```text ```text
serialize(runtime_config) -> supprimer quelques champs -> exposer serialize(runtime_config) -> supprimer quelques champs -> exposer
``` ```
La seule direction admise est : Les contrats source/runtime de `ks-config` susceptibles de contenir des secrets résolus ne dérivent ni `serde::Serialize` ni `Debug`, et les sérialiseurs publics historiques de `AppConfig` sont supprimés. La sensibilité des placeholders est classée selon `Secret > Internal > Public`; une chaîne composée contenant un placeholder `KS_SECRET_*`/`KB_SECRET_*` hérite de `Secret`.
```text TS-RS est désormais une frontière applicative : `ks-config` et `ks-lib` ne dépendent plus de `ts-rs` et ne génèrent plus de bindings. `kb-app-demo-desktop` possède les DTO TS-RS nécessaires à ses commandes Tauri.
runtime_config -> construction explicite d'un PublicConfig / ConfigDiagnostics
```
Les types contenant des secrets ne doivent pas être exportés en TS-RS simplement parce qu'ils sont sérialisables côté backend.
## 10. Tests de caractérisation avant et pendant migration ## 10. Tests de caractérisation avant et pendant migration
Avant chaque changement structurel correspondant, conserver ou ajouter des tests qui vérifient : Avant chaque changement structurel correspondant, conserver ou ajouter des tests qui vérifient :
- la topologie des dix crates et leurs dépendances attendues ; - la topologie des dix crates et leurs dépendances attendues ;
- les 13 tests d'API externe et leurs imports par crate root ; - les 14 tests d'API externe et leurs imports par crate root ;
- l'inventaire exact des identités runtime ; - l'inventaire exact des identités runtime ;
- l'absence de `kb-*` / `kb_*` résiduel dans les dix crates après leur prerelease de renommage, hors exceptions explicitement listées ; - l'absence de `kb-*` / `kb_*` résiduel dans les dix crates après leur prerelease de renommage, hors exceptions explicitement listées ;
- le maintien de `khadhroony-bot3` comme nom racine ; - le maintien de `khadhroony-bot3` comme nom racine ;
@@ -730,13 +726,14 @@ Le split partagé est considéré terminé après cette prerelease. Les champs a
### `0.5.1-pre.008` — surfaces publiques sûres, TS-RS et camouflage ### `0.5.1-pre.008` — surfaces publiques sûres, TS-RS et camouflage
- séparer types source/runtime/public/diagnostic ; - **implémenté** : `AppConfig/ProfileConfig` et les documents source/runtime sensibles restent backend-only, sans `serde::Serialize` ni `Debug` ;
- déplacer les DTO réellement destinés à Tauri/TypeScript vers `kb-app-demo-desktop` ou des wrappers applicatifs dédiés ; - **implémenté** : le fragment `application` devient opaque pour `ks-config` et est validé par le schéma possédé par `kb-app-demo-desktop` ;
- 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 ; - **implémenté** : `DemoConfigPayload` remplace la configuration résolue complète par des DTO public/diagnostic construits champ par champ ;
- propager la classification secret/public/internal lors de la résolution des placeholders et valeurs composées ; - **implémenté** : URLs RPC/WS, DSN PostgreSQL et chemins sensibles ne traversent pas le payload configuration ; les diagnostics n'exposent que des états bornés ;
- retirer `AppConfig`/`ProfileConfig` résolus du payload Tauri public ; - **implémenté** : classification `Secret > Internal > Public` des variables et chaînes composées, avec canaris de non-divulgation et erreurs de validation qui n'échoent plus les valeurs rejetées ;
- borner les DTO TS-RS et diagnostics ; - **implémenté** : suppression de `ts-rs` de `ks-config` et `ks-lib`, suppression de leurs dérivations/export et de leurs bindings générés historiques ;
- valider qu'aucun secret ne traverse logs/UI/erreurs/sérialisation. - **implémenté** : audit workspace empêchant la réintroduction de TS-RS dans `ks-*` et de `Serialize`/`Debug` sur les contrats de configuration sensibles sans décision explicite ;
- **implémenté** : test d'API externe de la classification/camouflage et canaris desktop empêchant la fuite de secrets dans les projections Tauri.
### `0.5.1-pre.009` — clôture ### `0.5.1-pre.009` — clôture

View File

@@ -1,5 +1,5 @@
<!-- file: docs/rules/RULES_SPECIFIC_KHADHROONY.md --> <!-- file: docs/rules/RULES_SPECIFIC_KHADHROONY.md -->
<!-- version: 18 --> <!-- version: 21 -->
# Règles spécifiques à `khadhroony-bot3` # Règles spécifiques à `khadhroony-bot3`
@@ -12,7 +12,7 @@ Toute divergence avec une règle générale doit être explicitement documentée
- Tous les noms de fichiers et de répertoires doivent être écrits en anglais. - Tous les noms de fichiers et de répertoires doivent être écrits en anglais.
- Les noms de fichiers et de répertoires ne doivent contenir aucun accent, espace ou caractère spécial inutile. - Les noms de fichiers et de répertoires ne doivent contenir aucun accent, espace ou caractère spécial inutile.
- Les noms internes doivent utiliser le format `snake_case` lorsque c'est applicable. - Les noms internes doivent utiliser le format `snake_case` lorsque c'est applicable.
- Les packages Rust utilisent le préfixe `kb-` ; leur identifiant Rust correspondant utilise automatiquement `kb_`. - Les bibliothèques généralistes Khadhroony Solana utilisent le préfixe Cargo `ks-` et lidentifiant Rust `ks_`; les applications et futures crates réellement propres au domaine Bot utilisent `kb-` / `kb_`.
- Les décodeurs, matérialisateurs et exécuteurs sont des modules de `ks-lib`, pas des crates séparées. - Les décodeurs, matérialisateurs et exécuteurs sont des modules de `ks-lib`, pas des crates séparées.
- Le crate de journalisation s'appelle `ks-logging`. - Le crate de journalisation s'appelle `ks-logging`.
- Les réexports sont regroupés par visibilité : le bloc `pub use` précède le bloc séparé `pub(crate) use`, sans ligne vide interne à un bloc ; leur ordre naturel doit rester compatible avec `cargo fmt`. - Les réexports sont regroupés par visibilité : le bloc `pub use` précède le bloc séparé `pub(crate) use`, sans ligne vide interne à un bloc ; leur ordre naturel doit rester compatible avec `cargo fmt`.
@@ -26,6 +26,8 @@ Toute divergence avec une règle générale doit être explicitement documentée
- Les types Rust exportés vers TypeScript doivent utiliser des noms stables et explicites. - Les types Rust exportés vers TypeScript doivent utiliser des noms stables et explicites.
- Les bindings générés doivent être produits dans un dossier dédié, généralement `../frontend/ts/bindings` ou `#[ts(export, export_to = "../frontend/ts/bindings/MyStruct.ts")]`. - Les bindings générés doivent être produits dans un dossier dédié, généralement `../frontend/ts/bindings` ou `#[ts(export, export_to = "../frontend/ts/bindings/MyStruct.ts")]`.
- Les types purement internes au backend ne doivent pas être exportés vers TypeScript par défaut. - Les types purement internes au backend ne doivent pas être exportés vers TypeScript par défaut.
- Dans létat courant, les crates généralistes `ks-*` ne possèdent ni dépendance `ts-rs`, ni dérivation/export TS-RS, ni dossier de bindings générés. Une exception future exige un contrat TypeScript générique indépendant de Tauri, explicitement documenté et ajouté à laudit workspace.
- Les types source/runtime de `ks-config` susceptibles de contenir des secrets ou valeurs internes ne dérivent ni `serde::Serialize` ni `Debug`. Une surface publique ou diagnostic est construite champ par champ dans lapplication propriétaire ; il est interdit de sérialiser un runtime complet puis de le redacter a posteriori.
- Corriger le type Rust/TS-rs source puis régénérer les bindings ; ne pas considérer une modification manuelle isolée dun fichier généré comme un correctif durable. - Corriger le type Rust/TS-rs source puis régénérer les bindings ; ne pas considérer une modification manuelle isolée dun fichier généré comme un correctif durable.
- `kb-app-demo-desktop/src/tauri.rs` contient les attributs `#[tauri::command]`, lenregistrement des commandes et des wrappers privés minces ; la logique complète doit vivre dans le module fonctionnel correspondant sous une fonction `pub(crate)` testable. - `kb-app-demo-desktop/src/tauri.rs` contient les attributs `#[tauri::command]`, lenregistrement des commandes et des wrappers privés minces ; la logique complète doit vivre dans le module fonctionnel correspondant sous une fonction `pub(crate)` testable.
- Un wrapper Tauri ne doit effectuer que ladaptation des handles/states/arguments, lappel de la fonction de module et le retour du résultat ; toute validation métier, orchestration ou construction de payload doit rester hors de `tauri.rs`. - Un wrapper Tauri ne doit effectuer que ladaptation des handles/states/arguments, lappel de la fonction de module et le retour du résultat ; toute validation métier, orchestration ou construction de payload doit rester hors de `tauri.rs`.
@@ -247,8 +249,8 @@ Aucun renommage massif de modules n'est autorisé sans étape de contrôle dédi
- Les endpoints WebSocket doivent sélectionner une classe de defaults nommée ; leurs timeouts, capacités et politique `auto_reconnect` peuvent être remplacés explicitement au niveau de l'endpoint. - Les 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 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 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 résident exclusivement sous `config/exemples/` avec un nom distinct des fichiers runtime. Les fixtures d'un contrat runtime résolu appartiennent à `test-fixtures/` et ne doivent pas être chargées en production.
- Les fichiers historiques `config/app.config.json`, `config/example.app.config.json`, `config/schemas/app.config.schema.json`, `config/example.config.json`, `config/schema.config.json` et `config/ks-pipeline-demo-scenarios.default.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`, les fichiers `config/example.*.config.json` hors `config/exemples/`, `config/schema.config.json` et `config/ks-pipeline-demo-scenarios.default.config.json` sont interdits dans l'état courant.
- Le chemin de composition desktop peut être remplacé par `KB_APP_DEMO_DESKTOP_CONFIG_PATH`. `KS_DEVNET_CONFIG_PATH` est uniquement un override facultatif vers une composition explicite pour les scénarios Devnet ; sans cet override, les scénarios utilisent les defaults partagés. `KS_LOGGING_CONFIG_PATH` reste un override explicite du document logging. - 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.
@@ -256,7 +258,13 @@ Aucun renommage massif de modules n'est autorisé sans étape de contrôle dédi
- Les variables possédées par les composants généralistes `ks-*` utilisent obligatoirement `KS_SECRET_*`, `KS_PUBLIC_*` ou `KS_*` ; les variables réellement spécifiques à `kb-app-demo-desktop` ou à de futures crates `kb-*` utilisent `KB_SECRET_*`, `KB_PUBLIC_*` ou `KB_*`. - 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. - 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 bindings TS-RS sont prioritairement une frontière d'application Tauri. Une crate `ks-*` ne conserve une dérivation/export TypeScript que si le type constitue un contrat externe générique explicitement justifié ; sinon l'application définit un DTO/wrapper dédié. - La sensibilité dune valeur composée suit `Secret > Internal > Public` : une URL, un DSN ou toute autre chaîne incorporant un placeholder `KS_SECRET_*`/`KB_SECRET_*` hérite de la sensibilité `Secret`, quel que soit le nom du champ final.
- Une valeur `Secret` ne doit jamais apparaître en clair dans une sérialisation, un `Debug`, une erreur, un log, un diagnostic, un payload Tauri ou lUI. Une valeur interne napparaît que dans un diagnostic explicitement borné et sémantiquement autorisé.
- La section `application` dune composition est opaque pour `ks-config`; le binaire propriétaire définit et valide son propre schéma sous `config/schemas/` avant usage.
- Les bindings TS-RS sont une frontière d'application Tauri. Une crate `ks-*` ne conserve une dérivation/export TypeScript que si le type constitue un contrat externe générique explicitement justifié et audité ; sinon l'application définit un DTO/wrapper dédié.
- Une commande Tauri ne retourne jamais directement un contrat `ks-*` susceptible de contenir des valeurs runtime ; elle retourne un DTO appartenant à lapplication et construit explicitement la projection autorisée.
- Les snapshots/runtime transport contenant une URL résolue restent backend-only : ils ne sont pas sérialisables directement, leur `Debug` est sanitisé, les DTO Tauri ne contiennent pas `endpoint_url`, et les erreurs/logs de transport ne recopient ni corps HTTP non-success ni message JSON-RPC distant susceptible de réinjecter un credential.
- Les chemins dérivés du stockage wallet sont internes ; un payload fonctionnel Tauri ne transporte pas un chemin de fixture. Seule une surface de diagnostic explicitement bornée peut exposer un chemin interne autorisé.
## Ordre de développement cible ## Ordre de développement cible

View File

@@ -1,8 +1,20 @@
<!-- file: kb-app-demo-desktop/CHANGELOG.md --> <!-- file: kb-app-demo-desktop/CHANGELOG.md -->
<!-- version: 47 --> <!-- version: 52 -->
# CHANGELOG — kb-app-demo-desktop # CHANGELOG — kb-app-demo-desktop
## `0.5.1-pre.008`
- Fix: remplace les snapshots `ks-onchain-transport` retournés directement par les commandes Tauri HTTP/WS par des DTO desktop sanitisés, fait supprimer lURL des snapshots backend et supprime `endpointUrl` des payloads dexécution/statut et ajoute des canaris empêchant toute URL résolue de traverser le frontend. Supprime également le chemin local du fixture Token-2022 des payloads fonctionnels et des erreurs affichées.
- Fix: use the crate-root DTO façades inside the configuration projection itself so required `pub(crate)` re-exports are not dead imports.
- Fix: remove the unused desktop application-schema getter; validation continues to use the embedded schema directly.
- remplace l'exposition Tauri de `AppConfig/ProfileConfig` résolus par une projection publique typée et un diagnostic borné construits champ par champ ;
- n'expose plus les URLs RPC/WebSocket, DSN PostgreSQL, chemins SQLite/wallet ni aucune valeur secrète ; les diagnostics correspondants transmettent seulement des états `configured`/`missing` et des paramètres non sensibles explicitement retenus ;
- devient propriétaire du fragment `application` de sa composition et le valide avec `config/schemas/kb-app-demo-desktop.application.config.schema.json` ;
- conserve TS-RS uniquement à la frontière applicative et ajoute des canaris garantissant que les valeurs sensibles injectées dans le runtime ne traversent pas les payloads configuration.
## `0.5.1-pre.007` ## `0.5.1-pre.007`
- consomme les nouveaux documents store/wallet/execution via la composition desktop ; - consomme les nouveaux documents store/wallet/execution via la composition desktop ;

View File

@@ -1,5 +1,5 @@
<!-- file: kb-app-demo-desktop/README.md --> <!-- file: kb-app-demo-desktop/README.md -->
<!-- version: 14 --> <!-- version: 15 -->
# kb-app-demo-desktop # kb-app-demo-desktop
@@ -21,6 +21,10 @@ Les commandes Tauri sont des adaptateurs minces. La logique réutilisable appart
Les campagnes Devnet/Testnet réutilisables appartiennent à `ks-pipeline-demo-scenarios`. Le desktop conserve leurs adaptateurs UI/Tauri, la sélection opérateur, la progression, les payloads TS-RS et la présentation ; `0.5.4` doit retirer les orchestrations réutilisables qui subsistent encore localement. Les campagnes Devnet/Testnet réutilisables appartiennent à `ks-pipeline-demo-scenarios`. Le desktop conserve leurs adaptateurs UI/Tauri, la sélection opérateur, la progression, les payloads TS-RS et la présentation ; `0.5.4` doit retirer les orchestrations réutilisables qui subsistent encore localement.
Depuis `0.5.1-pre.008`, TS-RS appartient à cette frontière applicative : `ks-config` et `ks-lib` ne génèrent plus de bindings TypeScript. Lorsqu'un type généraliste doit être présenté au frontend, le desktop construit un DTO/wrapper dédié.
La fenêtre Configuration ne reçoit plus `ks_config::AppConfig` ou `ProfileConfig`. Elle reçoit une projection publique typée et un diagnostic borné construits champ par champ. Les URLs, DSN et chemins sensibles ne traversent pas Tauri ; le diagnostic n'expose que leur état `configured`/`missing`.
## Exécution Metaplex Token Metadata ## Exécution Metaplex Token Metadata
Le panneau conserve les scénarios synthétiques pour inspecter les contrats, mais son parcours Devnet principal expose désormais 11 campagnes qualifiées de `pre.013` et appelle directement leurs runners spécialisés de `ks-pipeline-demo-scenarios`. Les cinq variantes `Create -> Mint`, collection, Print/Burn, lifecycle pNFT et escrow soumettent uniquement les opérations autorisées ; Maintenance et Use conservent les opérations `unavailable` dans des probes simulation-only. Le desktop ne reconstruit pas les builders ; il conserve encore une partie du dispatch, des critères de complétion et de la projection de preuves, à centraliser en `0.5.4` lorsque cette logique est réutilisable. Le panneau conserve les scénarios synthétiques pour inspecter les contrats, mais son parcours Devnet principal expose désormais 11 campagnes qualifiées de `pre.013` et appelle directement leurs runners spécialisés de `ks-pipeline-demo-scenarios`. Les cinq variantes `Create -> Mint`, collection, Print/Burn, lifecycle pNFT et escrow soumettent uniquement les opérations autorisées ; Maintenance et Use conservent les opérations `unavailable` dans des probes simulation-only. Le desktop ne reconstruit pas les builders ; il conserve encore une partie du dispatch, des critères de complétion et de la projection de preuves, à centraliser en `0.5.4` lorsque cette logique est réutilisable.

View File

@@ -1,5 +1,5 @@
<!-- file: kb-app-demo-desktop/frontend/demo_config.html --> <!-- file: kb-app-demo-desktop/frontend/demo_config.html -->
<!-- version: 5 --> <!-- version: 6 -->
<!DOCTYPE html> <!DOCTYPE html>
<html lang="fr"> <html lang="fr">
@@ -46,7 +46,7 @@
<div class="col-12 col-xl-6"> <div class="col-12 col-xl-6">
<div class="card shadow-sm border-0 h-100"> <div class="card shadow-sm border-0 h-100">
<div class="card-body"> <div class="card-body">
<h2 class="h5 card-title mb-3">Configuration active</h2> <h2 class="h5 card-title mb-3">Configuration publique</h2>
<div id="activeProfileJson" class="app-config-box app-json-viewer mb-0">Chargement...</div> <div id="activeProfileJson" class="app-config-box app-json-viewer mb-0">Chargement...</div>
</div> </div>
</div> </div>
@@ -55,7 +55,7 @@
<div class="col-12 col-xl-6"> <div class="col-12 col-xl-6">
<div class="card shadow-sm border-0 h-100"> <div class="card shadow-sm border-0 h-100">
<div class="card-body"> <div class="card-body">
<h2 class="h5 card-title mb-3">Configuration complète</h2> <h2 class="h5 card-title mb-3">Diagnostic borné</h2>
<div id="fullConfigJson" class="app-config-box app-json-viewer mb-0">Chargement...</div> <div id="fullConfigJson" class="app-config-box app-json-viewer mb-0">Chargement...</div>
</div> </div>
</div> </div>
@@ -64,7 +64,7 @@
<div class="col-12"> <div class="col-12">
<div class="card shadow-sm border-0"> <div class="card shadow-sm border-0">
<div class="card-body"> <div class="card-body">
<h2 class="h5 card-title mb-3">Schéma JSON embarqué</h2> <h2 class="h5 card-title mb-3">Schéma JSON de composition</h2>
<div id="schemaJson" class="app-config-box app-json-viewer mb-0">Chargement...</div> <div id="schemaJson" class="app-config-box app-json-viewer mb-0">Chargement...</div>
</div> </div>
</div> </div>
@@ -84,4 +84,4 @@
<script type="module" src="ts/demo_config.ts" defer></script> <script type="module" src="ts/demo_config.ts" defer></script>
</body> </body>
</html> </html>

View File

@@ -1,5 +1,5 @@
// file: kb-app-demo-desktop/frontend/ts/demo_config.ts // file: kb-app-demo-desktop/frontend/ts/demo_config.ts
// version: 6 // version: 7
import * as bootstrap from "bootstrap"; import * as bootstrap from "bootstrap";
import "simplebar"; import "simplebar";
@@ -55,12 +55,12 @@ function parseSchemaJson(schemaJson: string): unknown {
async function loadDemoConfig(): Promise<void> { async function loadDemoConfig(): Promise<void> {
try { try {
const payload = await invoke<DemoConfigPayload>("load_demo_config"); const payload = await invoke<DemoConfigPayload>("load_demo_config");
setText("#configPath", payload.config_path); setText("#configPath", payload.diagnostic_config.sources.find(source => source.category === "composition")?.path ?? "non disponible");
setText("#activeProfileName", payload.active_profile_name); setText("#activeProfileName", payload.active_profile_name);
setText("#activeEnvironment", payload.environment); setText("#activeEnvironment", payload.environment);
renderJsonViewer("#activeProfileJson", payload.active_profile); renderJsonViewer("#activeProfileJson", payload.public_config);
renderJsonViewer("#fullConfigJson", payload.app_config); renderJsonViewer("#fullConfigJson", payload.diagnostic_config);
renderJsonViewer("#schemaJson", parseSchemaJson(payload.schema_json)); renderJsonViewer("#schemaJson", parseSchemaJson(payload.composition_schema_json));
} catch (caughtError) { } catch (caughtError) {
const message = caughtError instanceof Error ? caughtError.message : String(caughtError); const message = caughtError instanceof Error ? caughtError.message : String(caughtError);
setText("#activeProfileJson", `Erreur pendant le chargement de la configuration : ${message}`); setText("#activeProfileJson", `Erreur pendant le chargement de la configuration : ${message}`);

View File

@@ -1,5 +1,5 @@
// file: kb-app-demo-desktop/frontend/ts/demo_execution_spl.ts // file: kb-app-demo-desktop/frontend/ts/demo_execution_spl.ts
// version: 9 // version: 10
import * as bootstrap from "bootstrap"; import * as bootstrap from "bootstrap";
import "simplebar"; import "simplebar";
@@ -698,7 +698,7 @@ async function loadToken2022Fixture(): Promise<void> {
profileName: element<HTMLSelectElement>("#executionProfileSelect").value, profileName: element<HTMLSelectElement>("#executionProfileSelect").value,
}); });
applyToken2022Fixture(); applyToken2022Fixture();
element<HTMLElement>("#token_2022FixtureHelp").textContent = `Fixture: ${token_2022Fixture.fixturePath} — program=${token_2022Fixture.programId}`; element<HTMLElement>("#token_2022FixtureHelp").textContent = `Fixture Token-2022 chargée — program=${token_2022Fixture.programId}`;
appendLog({ appendLog({
timestamp: new Date().toISOString(), timestamp: new Date().toISOString(),
level: "info", level: "info",

View File

@@ -1,5 +1,5 @@
// file: kb-app-demo-desktop/frontend/ts/demo_http.ts // file: kb-app-demo-desktop/frontend/ts/demo_http.ts
// version: 7 // version: 9
import * as bootstrap from "bootstrap"; import * as bootstrap from "bootstrap";
import "simplebar"; import "simplebar";
@@ -7,6 +7,7 @@ import ResizeObserver from "resize-observer-polyfill";
import { invoke } from "@tauri-apps/api/core"; import { invoke } from "@tauri-apps/api/core";
import { frontendDebug, frontendError, installFrontendConsoleBridge } from "./frontend_log.ts"; import { frontendDebug, frontendError, installFrontendConsoleBridge } from "./frontend_log.ts";
import { renderJsonViewer } from "./json_viewer.ts"; import { renderJsonViewer } from "./json_viewer.ts";
import type { DemoHttpEndpointPayload } from "./bindings/kb_app_demo_desktop/demo_http/DemoHttpEndpointPayload.ts";
import type { DemoHttpExecutionPayload } from "./bindings/kb_app_demo_desktop/demo_http/DemoHttpExecutionPayload.ts"; import type { DemoHttpExecutionPayload } from "./bindings/kb_app_demo_desktop/demo_http/DemoHttpExecutionPayload.ts";
import type { DemoHttpMethodOption } from "./bindings/kb_app_demo_desktop/demo_http/DemoHttpMethodOption.ts"; import type { DemoHttpMethodOption } from "./bindings/kb_app_demo_desktop/demo_http/DemoHttpMethodOption.ts";
import type { DemoHttpOptionsPayload } from "./bindings/kb_app_demo_desktop/demo_http/DemoHttpOptionsPayload.ts"; import type { DemoHttpOptionsPayload } from "./bindings/kb_app_demo_desktop/demo_http/DemoHttpOptionsPayload.ts";
@@ -96,7 +97,6 @@ function formatHttpExecutionPayload(payload: DemoHttpExecutionPayload): unknown
return { return {
endpointName: payload.endpointName, endpointName: payload.endpointName,
provider: payload.provider, provider: payload.provider,
endpointUrl: payload.endpointUrl,
role: payload.role, role: payload.role,
method: payload.method, method: payload.method,
requestKind: payload.requestKind, requestKind: payload.requestKind,
@@ -124,7 +124,7 @@ async function refreshHttpOptions(): Promise<void> {
async function refreshHttpPool(): Promise<void> { async function refreshHttpPool(): Promise<void> {
try { try {
const snapshots = await invoke("demo_http_list_pool_clients"); const snapshots = await invoke<DemoHttpEndpointPayload[]>("demo_http_list_pool_clients");
renderJsonViewer("#httpPoolOutput", snapshots); renderJsonViewer("#httpPoolOutput", snapshots);
} catch (caughtError) { } catch (caughtError) {
const message = caughtError instanceof Error ? caughtError.message : String(caughtError); const message = caughtError instanceof Error ? caughtError.message : String(caughtError);

View File

@@ -1,5 +1,5 @@
// file: kb-app-demo-desktop/frontend/ts/demo_ws.ts // file: kb-app-demo-desktop/frontend/ts/demo_ws.ts
// version: 8 // version: 10
import * as bootstrap from "bootstrap"; import * as bootstrap from "bootstrap";
import "simplebar"; import "simplebar";
@@ -8,6 +8,7 @@ import {invoke} from "@tauri-apps/api/core";
import {listen} from "@tauri-apps/api/event"; import {listen} from "@tauri-apps/api/event";
import {frontendDebug, frontendError, installFrontendConsoleBridge} from "./frontend_log.ts"; import {frontendDebug, frontendError, installFrontendConsoleBridge} from "./frontend_log.ts";
import {renderJsonViewer} from "./json_viewer.ts"; import {renderJsonViewer} from "./json_viewer.ts";
import type {DemoWsEndpointPayload} from "./bindings/kb_app_demo_desktop/demo_ws/DemoWsEndpointPayload.ts";
import type {DemoWsExecutionPayload} from "./bindings/kb_app_demo_desktop/demo_ws/DemoWsExecutionPayload.ts"; import type {DemoWsExecutionPayload} from "./bindings/kb_app_demo_desktop/demo_ws/DemoWsExecutionPayload.ts";
import type {DemoWsMessagePayload} from "./bindings/kb_app_demo_desktop/demo_ws/DemoWsMessagePayload.ts"; import type {DemoWsMessagePayload} from "./bindings/kb_app_demo_desktop/demo_ws/DemoWsMessagePayload.ts";
import type {DemoWsMethodOption} from "./bindings/kb_app_demo_desktop/demo_ws/DemoWsMethodOption.ts"; import type {DemoWsMethodOption} from "./bindings/kb_app_demo_desktop/demo_ws/DemoWsMethodOption.ts";
@@ -177,7 +178,6 @@ function formatWsExecutionPayload(payload: DemoWsExecutionPayload): string {
const header = { const header = {
endpointName: payload.endpointName, endpointName: payload.endpointName,
provider: payload.provider, provider: payload.provider,
endpointUrl: payload.endpointUrl,
role: payload.role, role: payload.role,
method: payload.method, method: payload.method,
requestKind: payload.requestKind, requestKind: payload.requestKind,
@@ -214,7 +214,7 @@ async function refreshWsOptions(): Promise<void> {
async function refreshWsPool(): Promise<void> { async function refreshWsPool(): Promise<void> {
try { try {
const snapshots = await invoke("demo_ws_list_pool_clients"); const snapshots = await invoke<DemoWsEndpointPayload[]>("demo_ws_list_pool_clients");
renderJsonViewer("#wsPoolOutput", snapshots); renderJsonViewer("#wsPoolOutput", snapshots);
} catch (caughtError) { } catch (caughtError) {
const message = caughtError instanceof Error ? caughtError.message : String(caughtError); const message = caughtError instanceof Error ? caughtError.message : String(caughtError);

View File

@@ -1,5 +1,5 @@
// file: kb-app-demo-desktop/src/app_state.rs // file: kb-app-demo-desktop/src/app_state.rs
// version: 15 // version: 16
//! Shared Tauri application state and startup initialization. //! Shared Tauri application state and startup initialization.
@@ -7,6 +7,12 @@
pub(crate) struct AppState { pub(crate) struct AppState {
config_path: std::string::String, config_path: std::string::String,
logging_config_path: std::string::String, logging_config_path: std::string::String,
transport_config_path: std::string::String,
listeners_config_path: std::string::String,
store_config_path: std::string::String,
wallet_config_path: std::string::String,
execution_config_path: std::string::String,
desktop_config: crate::DesktopApplicationConfig,
app_config: ks_config::AppConfig, app_config: ks_config::AppConfig,
active_profile: ks_config::ProfileConfig, active_profile: ks_config::ProfileConfig,
logging_guard: std::sync::Mutex<ks_logging::LoggingGuard>, logging_guard: std::sync::Mutex<ks_logging::LoggingGuard>,
@@ -44,8 +50,17 @@ 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 desktop_config = match crate::desktop_application_config(&composition_profile) {
std::result::Result::Ok(config) => config,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let logging_config_path = let logging_config_path =
resolve_logging_config_path(&workspace_root, &composed.logging_path); resolve_logging_config_path(&workspace_root, &composed.logging_path);
let transport_config_path = composed.transport_path.display().to_string();
let listeners_config_path = composed.listeners_path.display().to_string();
let store_config_path = composed.store_path.display().to_string();
let wallet_config_path = composed.wallet_path.display().to_string();
let execution_config_path = composed.execution_path.display().to_string();
let app_config = composed.app_config; let 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(),
@@ -79,6 +94,12 @@ impl crate::AppState {
return std::result::Result::Ok(crate::AppState { return std::result::Result::Ok(crate::AppState {
config_path: config_path.display().to_string(), config_path: config_path.display().to_string(),
logging_config_path: logging_config_path.display().to_string(), logging_config_path: logging_config_path.display().to_string(),
transport_config_path,
listeners_config_path,
store_config_path,
wallet_config_path,
execution_config_path,
desktop_config,
app_config, app_config,
active_profile, active_profile,
logging_guard: std::sync::Mutex::new(logging_guard), logging_guard: std::sync::Mutex::new(logging_guard),
@@ -108,6 +129,36 @@ impl crate::AppState {
return self.logging_config_path.as_str(); return self.logging_config_path.as_str();
} }
/// Returns the path used to load the transport configuration file.
pub(crate) fn transport_config_path(&self) -> &str {
return self.transport_config_path.as_str();
}
/// Returns the path used to load the listeners configuration file.
pub(crate) fn listeners_config_path(&self) -> &str {
return self.listeners_config_path.as_str();
}
/// Returns the path used to load the store configuration file.
pub(crate) fn store_config_path(&self) -> &str {
return self.store_config_path.as_str();
}
/// Returns the path used to load the wallet configuration file.
pub(crate) fn wallet_config_path(&self) -> &str {
return self.wallet_config_path.as_str();
}
/// Returns the path used to load the execution configuration file.
pub(crate) fn execution_config_path(&self) -> &str {
return self.execution_config_path.as_str();
}
/// Returns desktop-owned settings selected from the active composition profile.
pub(crate) fn desktop_config(&self) -> &crate::DesktopApplicationConfig {
return &self.desktop_config;
}
/// Returns the complete parsed application configuration. /// Returns the complete parsed application configuration.
pub(crate) fn app_config(&self) -> &ks_config::AppConfig { pub(crate) fn app_config(&self) -> &ks_config::AppConfig {
return &self.app_config; return &self.app_config;

View File

@@ -1,7 +1,7 @@
// file: kb-app-demo-desktop/src/demo_config.rs // file: kb-app-demo-desktop/src/demo_config.rs
// version: 3 // version: 5
//! Configuration demo payload and state projection. //! Configuration demo payloads with explicit public and bounded diagnostic projections.
use ts_rs::TS; // rust-rules: derive-import use ts_rs::TS; // rust-rules: derive-import
@@ -12,29 +12,432 @@ use ts_rs::TS; // rust-rules: derive-import
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_config/DemoConfigPayload.ts" export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_config/DemoConfigPayload.ts"
)] )]
pub(crate) struct DemoConfigPayload { pub(crate) struct DemoConfigPayload {
/// Path used to load the configuration file. /// Active composition profile name.
pub(crate) config_path: std::string::String,
/// Active profile name.
pub(crate) active_profile_name: std::string::String, pub(crate) active_profile_name: std::string::String,
/// Active profile environment. /// Desktop-owned environment label.
pub(crate) environment: std::string::String, pub(crate) environment: std::string::String,
/// Entire parsed configuration. /// Explicit public configuration projection.
pub(crate) app_config: ks_config::AppConfig, pub(crate) public_config: crate::DemoConfigPublicPayload,
/// Active profile configuration. /// Explicit bounded diagnostic projection.
pub(crate) active_profile: ks_config::ProfileConfig, pub(crate) diagnostic_config: crate::DemoConfigDiagnosticPayload,
/// Embedded JSON Schema text used during loading. /// Generic binary-composition JSON Schema text.
pub(crate) schema_json: std::string::String, pub(crate) composition_schema_json: std::string::String,
}
/// Public desktop configuration projection containing no secret or internal paths.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_config/DemoConfigPublicPayload.ts"
)]
pub(crate) struct DemoConfigPublicPayload {
/// Desktop application identity.
pub(crate) application_name: std::string::String,
/// Desktop environment label.
pub(crate) environment: std::string::String,
/// Active composed profile.
pub(crate) profile_name: std::string::String,
/// Public HTTP endpoint identities without URLs or credentials.
pub(crate) http_endpoints: std::vec::Vec<crate::DemoConfigPublicEndpointPayload>,
/// Public WebSocket endpoint identities without URLs or credentials.
pub(crate) ws_endpoints: std::vec::Vec<crate::DemoConfigPublicEndpointPayload>,
/// Listener summary without account-specific filters.
pub(crate) listeners: crate::DemoConfigPublicListenersPayload,
/// Wallet cluster only; wallet paths and aliases remain diagnostic/internal.
pub(crate) wallet_cluster: std::string::String,
/// Desktop-owned demo feature flags.
pub(crate) demo: crate::DemoConfigPublicDemoPayload,
}
/// Public endpoint identity with no URL or authentication material.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_config/DemoConfigPublicEndpointPayload.ts"
)]
pub(crate) struct DemoConfigPublicEndpointPayload {
/// Endpoint code.
pub(crate) name: std::string::String,
/// Whether the endpoint is enabled.
pub(crate) enabled: bool,
/// Provider code.
pub(crate) provider: std::string::String,
/// Cluster code.
pub(crate) cluster: std::string::String,
}
/// Public listener summary.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_config/DemoConfigPublicListenersPayload.ts"
)]
pub(crate) struct DemoConfigPublicListenersPayload {
/// Whether listener creation is enabled.
pub(crate) enabled: bool,
/// Default commitment used by subscriptions.
pub(crate) default_commitment: std::string::String,
/// Number of log listener declarations.
pub(crate) log_listener_count: u32,
/// Number of program listener declarations.
pub(crate) program_listener_count: u32,
/// Number of account listener declarations.
pub(crate) account_listener_count: u32,
}
/// Public desktop demo feature flags.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_config/DemoConfigPublicDemoPayload.ts"
)]
pub(crate) struct DemoConfigPublicDemoPayload {
/// Enables live demo pages.
pub(crate) live_demo_enabled: bool,
/// Enables trading demo pages.
pub(crate) trading_demo_enabled: bool,
}
/// Bounded diagnostic projection containing internal configuration metadata but no secret values.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_config/DemoConfigDiagnosticPayload.ts"
)]
pub(crate) struct DemoConfigDiagnosticPayload {
/// Filesystem sources selected by the active binary composition.
pub(crate) sources: std::vec::Vec<crate::DemoConfigSourceDiagnosticPayload>,
/// Endpoint runtime settings without endpoint URLs.
pub(crate) endpoints: std::vec::Vec<crate::DemoConfigEndpointDiagnosticPayload>,
/// Store diagnostics without database URLs or filesystem paths.
pub(crate) store: crate::DemoConfigStoreDiagnosticPayload,
/// Wallet diagnostics without wallet filesystem paths or key material.
pub(crate) wallet: crate::DemoConfigWalletDiagnosticPayload,
/// Execution-policy diagnostics.
pub(crate) execution: crate::DemoConfigExecutionDiagnosticPayload,
}
/// One selected configuration source exposed only through the explicit diagnostic surface.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_config/DemoConfigSourceDiagnosticPayload.ts"
)]
pub(crate) struct DemoConfigSourceDiagnosticPayload {
/// Source category.
pub(crate) category: std::string::String,
/// Resolved filesystem path.
pub(crate) path: std::string::String,
}
/// Endpoint diagnostic without URL content.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_config/DemoConfigEndpointDiagnosticPayload.ts"
)]
pub(crate) struct DemoConfigEndpointDiagnosticPayload {
/// Transport kind, either `http` or `ws`.
pub(crate) kind: std::string::String,
/// Endpoint code.
pub(crate) name: std::string::String,
/// Whether the endpoint is enabled.
pub(crate) enabled: bool,
/// Provider code.
pub(crate) provider: std::string::String,
/// Cluster code.
pub(crate) cluster: std::string::String,
/// URL state marker; the URL itself is never serialized.
pub(crate) url_state: std::string::String,
/// Connection timeout in milliseconds.
#[ts(type = "number")]
pub(crate) connect_timeout_ms: u64,
/// Request timeout in milliseconds.
#[ts(type = "number")]
pub(crate) request_timeout_ms: u64,
/// Optional unsubscribe timeout in milliseconds for WebSocket endpoints.
#[ts(type = "number | null")]
pub(crate) unsubscribe_timeout_ms: std::option::Option<u64>,
/// Optional reconnect policy for WebSocket endpoints.
pub(crate) auto_reconnect: std::option::Option<bool>,
/// Number of configured endpoint roles.
pub(crate) role_count: u32,
}
/// Store diagnostic projection without DSN or SQLite path content.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_config/DemoConfigStoreDiagnosticPayload.ts"
)]
pub(crate) struct DemoConfigStoreDiagnosticPayload {
/// Whether persistence is enabled.
pub(crate) enabled: bool,
/// Selected store backend.
pub(crate) backend: std::string::String,
/// Whether a PostgreSQL URL is configured.
pub(crate) postgres_url_state: std::string::String,
/// PostgreSQL connection pool ceiling.
pub(crate) postgres_max_connections: u32,
/// PostgreSQL connection timeout in milliseconds.
#[ts(type = "number")]
pub(crate) postgres_connect_timeout_ms: u64,
/// Whether PostgreSQL schema initialization is enabled.
pub(crate) postgres_auto_initialize_schema: bool,
/// Whether a SQLite path is configured.
pub(crate) sqlite_path_state: std::string::String,
/// SQLite connection pool ceiling.
pub(crate) sqlite_max_connections: u32,
/// Whether SQLite schema initialization is enabled.
pub(crate) sqlite_auto_initialize_schema: bool,
}
/// Wallet diagnostic projection without paths or secret key material.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_config/DemoConfigWalletDiagnosticPayload.ts"
)]
pub(crate) struct DemoConfigWalletDiagnosticPayload {
/// Whether a wallet directory is configured.
pub(crate) wallet_directory_state: std::string::String,
/// Wallet cluster.
pub(crate) cluster: std::string::String,
/// Whether the managed temporary wallet is enabled.
pub(crate) temporary_wallet_enabled: bool,
/// Temporary wallet alias, which is internal but not secret.
pub(crate) temporary_wallet_alias: std::string::String,
/// Whether the managed temporary wallet persists between restarts.
pub(crate) temporary_wallet_persist: bool,
}
/// Execution-policy diagnostic projection.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_config/DemoConfigExecutionDiagnosticPayload.ts"
)]
pub(crate) struct DemoConfigExecutionDiagnosticPayload {
/// Enables local-validator sends.
pub(crate) localnet_send_enabled: bool,
/// Enables Devnet sends.
pub(crate) devnet_send_enabled: bool,
/// Enables Testnet sends.
pub(crate) testnet_send_enabled: bool,
/// Enables Mainnet sends.
pub(crate) mainnet_send_enabled: bool,
/// Default dry-run policy.
pub(crate) dry_run_default: bool,
/// Requires simulation before send.
pub(crate) require_simulation: bool,
/// Requires explicit operator confirmation.
pub(crate) require_operator_confirmation: bool,
/// Maximum local-validator spend in lamports.
#[ts(type = "number")]
pub(crate) localnet_max_spend_lamports: u64,
/// Maximum Devnet spend in lamports.
#[ts(type = "number")]
pub(crate) devnet_max_spend_lamports: u64,
/// Maximum Testnet spend in lamports.
#[ts(type = "number")]
pub(crate) testnet_max_spend_lamports: u64,
/// Maximum Mainnet spend in lamports.
#[ts(type = "number")]
pub(crate) mainnet_max_spend_lamports: u64,
/// Maximum fee in lamports.
#[ts(type = "number")]
pub(crate) max_fee_lamports: u64,
/// Maximum compute-unit price in micro-lamports.
#[ts(type = "number")]
pub(crate) max_compute_unit_price_micro_lamports: u64,
/// Maximum recent-blockhash age in slots.
#[ts(type = "number")]
pub(crate) recent_blockhash_max_age_slots: u64,
/// Maximum send retries.
pub(crate) send_max_retries: u32,
/// Confirmation poll interval in milliseconds.
#[ts(type = "number")]
pub(crate) confirmation_poll_interval_ms: u64,
/// Maximum confirmation poll attempts.
pub(crate) confirmation_max_attempts: u32,
/// Maximum Devnet faucet airdrop in lamports.
#[ts(type = "number")]
pub(crate) devnet_airdrop_max_lamports: u64,
} }
/// Builds the configuration payload from shared application state. /// Builds the configuration payload from shared application state.
pub(crate) fn demo_config_payload(state: &crate::AppState) -> crate::DemoConfigPayload { pub(crate) fn demo_config_payload(state: &crate::AppState) -> crate::DemoConfigPayload {
let active_profile = state.active_profile();
let desktop = state.desktop_config();
return crate::DemoConfigPayload { return crate::DemoConfigPayload {
config_path: state.config_path().to_owned(), active_profile_name: active_profile.name.clone(),
active_profile_name: state.active_profile().name.clone(), environment: desktop.environment.clone(),
environment: state.active_profile().app.environment.clone(), public_config: public_config_payload(active_profile, desktop),
app_config: state.app_config().clone(), diagnostic_config: diagnostic_config_payload(state),
active_profile: state.active_profile().clone(), composition_schema_json: ks_config::composition_json_schema_text().to_owned(),
schema_json: ks_config::config_json_schema_text().to_owned(), };
}
fn public_config_payload(
profile: &ks_config::ProfileConfig,
desktop: &crate::DesktopApplicationConfig,
) -> DemoConfigPublicPayload {
let http_endpoints = profile
.solana
.http_endpoints
.iter()
.map(|endpoint| {
return DemoConfigPublicEndpointPayload {
name: endpoint.name.clone(),
enabled: endpoint.enabled,
provider: endpoint.provider.clone(),
cluster: endpoint.cluster.clone(),
};
})
.collect::<std::vec::Vec<DemoConfigPublicEndpointPayload>>();
let ws_endpoints = profile
.solana
.ws_endpoints
.iter()
.map(|endpoint| {
return DemoConfigPublicEndpointPayload {
name: endpoint.name.clone(),
enabled: endpoint.enabled,
provider: endpoint.provider.clone(),
cluster: endpoint.cluster.clone(),
};
})
.collect::<std::vec::Vec<DemoConfigPublicEndpointPayload>>();
return DemoConfigPublicPayload {
application_name: desktop.name.clone(),
environment: desktop.environment.clone(),
profile_name: profile.name.clone(),
http_endpoints,
ws_endpoints,
listeners: DemoConfigPublicListenersPayload {
enabled: profile.solana.listeners.enabled,
default_commitment: profile.solana.listeners.default_commitment.clone(),
log_listener_count: bounded_len(profile.solana.listeners.log_listeners.len()),
program_listener_count: bounded_len(profile.solana.listeners.program_listeners.len()),
account_listener_count: bounded_len(profile.solana.listeners.account_listeners.len()),
},
wallet_cluster: profile.wallet.cluster.clone(),
demo: DemoConfigPublicDemoPayload {
live_demo_enabled: desktop.demo.live_demo_enabled,
trading_demo_enabled: desktop.demo.trading_demo_enabled,
},
};
}
fn diagnostic_config_payload(state: &crate::AppState) -> DemoConfigDiagnosticPayload {
let profile = state.active_profile();
let mut endpoints = std::vec::Vec::<DemoConfigEndpointDiagnosticPayload>::new();
for endpoint in &profile.solana.http_endpoints {
endpoints.push(DemoConfigEndpointDiagnosticPayload {
kind: "http".to_string(),
name: endpoint.name.clone(),
enabled: endpoint.enabled,
provider: endpoint.provider.clone(),
cluster: endpoint.cluster.clone(),
url_state: configured_state(endpoint.url.as_str()),
connect_timeout_ms: endpoint.connect_timeout_ms,
request_timeout_ms: endpoint.request_timeout_ms,
unsubscribe_timeout_ms: std::option::Option::None,
auto_reconnect: std::option::Option::None,
role_count: bounded_len(endpoint.roles.len()),
});
}
for endpoint in &profile.solana.ws_endpoints {
endpoints.push(DemoConfigEndpointDiagnosticPayload {
kind: "ws".to_string(),
name: endpoint.name.clone(),
enabled: endpoint.enabled,
provider: endpoint.provider.clone(),
cluster: endpoint.cluster.clone(),
url_state: configured_state(endpoint.url.as_str()),
connect_timeout_ms: endpoint.connect_timeout_ms,
request_timeout_ms: endpoint.request_timeout_ms,
unsubscribe_timeout_ms: std::option::Option::Some(endpoint.unsubscribe_timeout_ms),
auto_reconnect: std::option::Option::Some(endpoint.auto_reconnect),
role_count: bounded_len(endpoint.roles.len()),
});
}
return DemoConfigDiagnosticPayload {
sources: std::vec![
source_diagnostic("composition", state.config_path()),
source_diagnostic("logging", state.logging_config_path()),
source_diagnostic("transport", state.transport_config_path()),
source_diagnostic("listeners", state.listeners_config_path()),
source_diagnostic("store", state.store_config_path()),
source_diagnostic("wallet", state.wallet_config_path()),
source_diagnostic("execution", state.execution_config_path()),
],
endpoints,
store: DemoConfigStoreDiagnosticPayload {
enabled: profile.database.enabled,
backend: profile.database.backend.clone(),
postgres_url_state: configured_state(profile.database.postgres.url.as_str()),
postgres_max_connections: profile.database.postgres.max_connections,
postgres_connect_timeout_ms: profile.database.postgres.connect_timeout_ms,
postgres_auto_initialize_schema: profile.database.postgres.auto_initialize_schema,
sqlite_path_state: configured_state(profile.database.sqlite.path.as_str()),
sqlite_max_connections: profile.database.sqlite.max_connections,
sqlite_auto_initialize_schema: profile.database.sqlite.auto_initialize_schema,
},
wallet: DemoConfigWalletDiagnosticPayload {
wallet_directory_state: configured_state(profile.wallet.wallet_dir.as_str()),
cluster: profile.wallet.cluster.clone(),
temporary_wallet_enabled: profile.wallet.temporary_wallet_enabled,
temporary_wallet_alias: profile.wallet.temporary_wallet_alias.clone(),
temporary_wallet_persist: profile.wallet.temporary_wallet_persist,
},
execution: execution_diagnostic(&profile.execution),
};
}
fn source_diagnostic(category: &str, path: &str) -> DemoConfigSourceDiagnosticPayload {
return DemoConfigSourceDiagnosticPayload {
category: category.to_string(),
path: path.to_string(),
};
}
fn configured_state(value: &str) -> std::string::String {
if value.trim().is_empty() {
return "missing".to_string();
}
return "configured".to_string();
}
fn bounded_len(value: usize) -> u32 {
return match u32::try_from(value) {
std::result::Result::Ok(count) => count,
std::result::Result::Err(_) => u32::MAX,
};
}
fn execution_diagnostic(
execution: &ks_config::ExecutionConfig,
) -> DemoConfigExecutionDiagnosticPayload {
return DemoConfigExecutionDiagnosticPayload {
localnet_send_enabled: execution.localnet_send_enabled,
devnet_send_enabled: execution.devnet_send_enabled,
testnet_send_enabled: execution.testnet_send_enabled,
mainnet_send_enabled: execution.mainnet_send_enabled,
dry_run_default: execution.dry_run_default,
require_simulation: execution.require_simulation,
require_operator_confirmation: execution.require_operator_confirmation,
localnet_max_spend_lamports: execution.localnet_max_spend_lamports,
devnet_max_spend_lamports: execution.devnet_max_spend_lamports,
testnet_max_spend_lamports: execution.testnet_max_spend_lamports,
mainnet_max_spend_lamports: execution.mainnet_max_spend_lamports,
max_fee_lamports: execution.max_fee_lamports,
max_compute_unit_price_micro_lamports: execution.max_compute_unit_price_micro_lamports,
recent_blockhash_max_age_slots: execution.recent_blockhash_max_age_slots,
send_max_retries: execution.send_max_retries,
confirmation_poll_interval_ms: execution.confirmation_poll_interval_ms,
confirmation_max_attempts: execution.confirmation_max_attempts,
devnet_airdrop_max_lamports: execution.devnet_airdrop_max_lamports,
}; };
} }
@@ -42,10 +445,126 @@ pub(crate) fn demo_config_payload(state: &crate::AppState) -> crate::DemoConfigP
mod tests { mod tests {
use ts_rs::TS; // rust-rules: trait-import use ts_rs::TS; // rust-rules: trait-import
fn active_fixture_profile() -> ks_config::ProfileConfig {
let parsed = ks_config::parse_config_json(include_str!(
"../../test-fixtures/config/resolved.app.config.json"
));
let config = match parsed {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("resolved fixture must parse: {error}"),
};
return match ks_config::active_profile(&config) {
std::result::Result::Ok(value) => value.clone(),
std::result::Result::Err(error) => {
panic!("resolved fixture active profile must exist: {error}")
},
};
}
fn desktop_fixture() -> crate::DesktopApplicationConfig {
return crate::DesktopApplicationConfig {
name: "kb-app-demo-desktop".to_string(),
environment: "test".to_string(),
demo: crate::DesktopDemoConfig {
live_demo_enabled: true,
trading_demo_enabled: false,
},
};
}
#[test] #[test]
fn payload_binding_uses_desktop_path() { fn payload_binding_uses_desktop_path() {
let config = ts_rs::Config::default(); let config = ts_rs::Config::default();
let declaration = <crate::DemoConfigPayload as TS>::decl(&config); let declaration = <crate::DemoConfigPayload as TS>::decl(&config);
assert!(declaration.contains("DemoConfigPayload")); assert!(declaration.contains("DemoConfigPayload"));
assert!(declaration.contains("DemoConfigPublicPayload"));
assert!(declaration.contains("DemoConfigDiagnosticPayload"));
assert!(!declaration.contains("AppConfig"));
assert!(!declaration.contains("ProfileConfig"));
}
#[test]
fn public_projection_excludes_internal_execution_and_storage_fields() {
let profile = active_fixture_profile();
let payload = super::public_config_payload(&profile, &desktop_fixture());
let serialized = match serde_json::to_string(&payload) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("public payload must serialize: {error}"),
};
assert!(!serialized.contains("mainnet_send_enabled"));
assert!(!serialized.contains("postgres"));
assert!(!serialized.contains("wallet_dir"));
assert!(!serialized.contains("url"));
}
#[test]
fn public_and_diagnostic_projections_never_serialize_secret_canaries() {
let mut profile = active_fixture_profile();
profile.database.postgres.url =
"postgres://operator:POSTGRES-SECRET-CANARY@localhost/solana".to_string();
profile.database.sqlite.path = "/private/SQLITE-PATH-CANARY.sqlite".to_string();
profile.wallet.wallet_dir = "/private/WALLET-PATH-CANARY".to_string();
if let std::option::Option::Some(endpoint) = profile.solana.http_endpoints.first_mut() {
endpoint.url = "https://provider.invalid/?api-key=HELIUS-SECRET-CANARY".to_string();
}
if let std::option::Option::Some(endpoint) = profile.solana.ws_endpoints.first_mut() {
endpoint.url = "wss://provider.invalid/WS-SECRET-CANARY".to_string();
}
let public = super::public_config_payload(&profile, &desktop_fixture());
let diagnostics = super::DemoConfigDiagnosticPayload {
sources: std::vec![],
endpoints: profile
.solana
.http_endpoints
.iter()
.map(|endpoint| {
return super::DemoConfigEndpointDiagnosticPayload {
kind: "http".to_string(),
name: endpoint.name.clone(),
enabled: endpoint.enabled,
provider: endpoint.provider.clone(),
cluster: endpoint.cluster.clone(),
url_state: super::configured_state(endpoint.url.as_str()),
connect_timeout_ms: endpoint.connect_timeout_ms,
request_timeout_ms: endpoint.request_timeout_ms,
unsubscribe_timeout_ms: std::option::Option::None,
auto_reconnect: std::option::Option::None,
role_count: super::bounded_len(endpoint.roles.len()),
};
})
.collect::<std::vec::Vec<super::DemoConfigEndpointDiagnosticPayload>>(),
store: super::DemoConfigStoreDiagnosticPayload {
enabled: profile.database.enabled,
backend: profile.database.backend.clone(),
postgres_url_state: super::configured_state(profile.database.postgres.url.as_str()),
postgres_max_connections: profile.database.postgres.max_connections,
postgres_connect_timeout_ms: profile.database.postgres.connect_timeout_ms,
postgres_auto_initialize_schema: profile.database.postgres.auto_initialize_schema,
sqlite_path_state: super::configured_state(profile.database.sqlite.path.as_str()),
sqlite_max_connections: profile.database.sqlite.max_connections,
sqlite_auto_initialize_schema: profile.database.sqlite.auto_initialize_schema,
},
wallet: super::DemoConfigWalletDiagnosticPayload {
wallet_directory_state: super::configured_state(profile.wallet.wallet_dir.as_str()),
cluster: profile.wallet.cluster.clone(),
temporary_wallet_enabled: profile.wallet.temporary_wallet_enabled,
temporary_wallet_alias: profile.wallet.temporary_wallet_alias.clone(),
temporary_wallet_persist: profile.wallet.temporary_wallet_persist,
},
execution: super::execution_diagnostic(&profile.execution),
};
let serialized = match serde_json::to_string(&(public, diagnostics)) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("safe projections must serialize: {error}"),
};
for secret in [
"POSTGRES-SECRET-CANARY",
"SQLITE-PATH-CANARY",
"WALLET-PATH-CANARY",
"HELIUS-SECRET-CANARY",
"WS-SECRET-CANARY",
] {
assert!(!serialized.contains(secret));
}
} }
} }

View File

@@ -1,5 +1,5 @@
// file: kb-app-demo-desktop/src/demo_http.rs // file: kb-app-demo-desktop/src/demo_http.rs
// version: 14 // version: 16
//! HTTP JSON-RPC demo commands. //! HTTP JSON-RPC demo commands.
@@ -53,6 +53,24 @@ pub(crate) struct DemoHttpOptionsPayload {
pub(crate) methods: std::vec::Vec<crate::DemoHttpMethodOption>, pub(crate) methods: std::vec::Vec<crate::DemoHttpMethodOption>,
} }
/// UI-safe snapshot of one configured HTTP endpoint.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[serde(rename_all = "camelCase")]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_http/DemoHttpEndpointPayload.ts"
)]
pub(crate) struct DemoHttpEndpointPayload {
/// Logical endpoint name.
pub(crate) endpoint_name: std::string::String,
/// Provider name.
pub(crate) provider: std::string::String,
/// UI-safe role and local-limit snapshots.
pub(crate) roles: std::vec::Vec<crate::DemoEndpointRolePayload>,
/// Local endpoint status.
pub(crate) status: std::string::String,
}
/// Request payload for one HTTP JSON-RPC demo execution. /// Request payload for one HTTP JSON-RPC demo execution.
#[derive(Clone, Debug, serde::Deserialize, TS)] #[derive(Clone, Debug, serde::Deserialize, TS)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
@@ -85,8 +103,6 @@ pub(crate) struct DemoHttpExecutionPayload {
pub(crate) endpoint_name: std::string::String, pub(crate) endpoint_name: std::string::String,
/// Selected provider name. /// Selected provider name.
pub(crate) provider: std::string::String, pub(crate) provider: std::string::String,
/// Selected endpoint URL.
pub(crate) endpoint_url: std::string::String,
/// Required role used by the selection. /// Required role used by the selection.
pub(crate) role: std::string::String, pub(crate) role: std::string::String,
/// JSON-RPC method name. /// JSON-RPC method name.
@@ -146,7 +162,6 @@ pub(crate) async fn demo_http_execute_request(
return std::result::Result::Ok(crate::DemoHttpExecutionPayload { return std::result::Result::Ok(crate::DemoHttpExecutionPayload {
endpoint_name: selected_client.endpoint_name().to_string(), endpoint_name: selected_client.endpoint_name().to_string(),
provider: selected_client.provider().to_string(), provider: selected_client.provider().to_string(),
endpoint_url: selected_client.endpoint_url().to_string(),
role, role,
request_kind: ks_onchain_transport::request_kind_from_method(&method), request_kind: ks_onchain_transport::request_kind_from_method(&method),
method, method,
@@ -157,8 +172,13 @@ pub(crate) async fn demo_http_execute_request(
pub(crate) fn demo_http_list_pool_clients( pub(crate) fn demo_http_list_pool_clients(
state: tauri::State<'_, crate::AppState>, state: tauri::State<'_, crate::AppState>,
) -> std::vec::Vec<ks_onchain_transport::HttpPoolClientSnapshot> { ) -> std::vec::Vec<crate::DemoHttpEndpointPayload> {
return state.http_pool().snapshot(); let snapshots = state.http_pool().snapshot();
let mut endpoints = std::vec::Vec::new();
for snapshot in snapshots {
endpoints.push(http_endpoint_payload(snapshot));
}
return endpoints;
} }
pub(crate) fn demo_http_options( pub(crate) fn demo_http_options(
@@ -170,6 +190,21 @@ pub(crate) fn demo_http_options(
}; };
} }
fn http_endpoint_payload(
snapshot: ks_onchain_transport::HttpPoolClientSnapshot,
) -> crate::DemoHttpEndpointPayload {
let mut roles = std::vec::Vec::new();
for role in snapshot.roles {
roles.push(crate::DemoEndpointRolePayload::from_snapshot(role));
}
return crate::DemoHttpEndpointPayload {
endpoint_name: snapshot.endpoint_name,
provider: snapshot.provider,
roles,
status: snapshot.status,
};
}
fn build_http_role_options( fn build_http_role_options(
snapshots: std::vec::Vec<ks_onchain_transport::HttpPoolClientSnapshot>, snapshots: std::vec::Vec<ks_onchain_transport::HttpPoolClientSnapshot>,
) -> std::vec::Vec<crate::DemoHttpRoleOption> { ) -> std::vec::Vec<crate::DemoHttpRoleOption> {
@@ -412,4 +447,23 @@ mod tests {
} }
assert!(!options.is_empty()); assert!(!options.is_empty());
} }
#[test]
fn endpoint_payload_never_serializes_resolved_url() {
let snapshot = ks_onchain_transport::HttpPoolClientSnapshot {
endpoint_name: "helius".to_string(),
provider: "helius".to_string(),
roles: std::vec::Vec::new(),
status: "idle".to_string(),
};
let payload = super::http_endpoint_payload(snapshot);
let serialized = match serde_json::to_string(&payload) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
panic!("cannot serialize HTTP endpoint payload: {error}")
},
};
assert!(!serialized.contains("HTTP-SECRET-CANARY"));
assert!(!serialized.contains("endpointUrl"));
}
} }

View File

@@ -1,5 +1,5 @@
// file: kb-app-demo-desktop/src/demo_spl_token_2022.rs // file: kb-app-demo-desktop/src/demo_spl_token_2022.rs
// version: 14 // version: 15
//! Thin Tauri adapter for independent public Token-2022 Devnet validation scenarios. //! Thin Tauri adapter for independent public Token-2022 Devnet validation scenarios.
@@ -13,8 +13,6 @@ use ts_rs::TS; // rust-rules: derive-import
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_spl_token_2022/DemoSplToken2022FixturePayload.ts" export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_spl_token_2022/DemoSplToken2022FixturePayload.ts"
)] )]
pub(crate) struct DemoSplToken2022FixturePayload { pub(crate) struct DemoSplToken2022FixturePayload {
/// Fixture file used by the application.
pub(crate) fixture_path: std::string::String,
/// Token-2022 program ID. /// Token-2022 program ID.
pub(crate) program_id: std::string::String, pub(crate) program_id: std::string::String,
/// Mint account. /// Mint account.
@@ -231,11 +229,10 @@ pub(crate) fn demo_spl_token_2022_fixture(
let fixture_path = wallet_dir.join("spl_token_2022_validation").join("fixture.env"); let fixture_path = wallet_dir.join("spl_token_2022_validation").join("fixture.env");
let contents = match std::fs::read_to_string(&fixture_path) { let contents = match std::fs::read_to_string(&fixture_path) {
std::result::Result::Ok(value) => value, std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => { std::result::Result::Err(_) => {
return std::result::Result::Err(format!( return std::result::Result::Err(
"unable to read Token-2022 fixture {}: {error}", "unable to read configured Token-2022 fixture".to_string(),
fixture_path.display() );
));
}, },
}; };
let values = parse_fixture(contents.as_str()); let values = parse_fixture(contents.as_str());
@@ -292,7 +289,6 @@ pub(crate) fn demo_spl_token_2022_fixture(
}, },
}; };
return std::result::Result::Ok(crate::DemoSplToken2022FixturePayload { return std::result::Result::Ok(crate::DemoSplToken2022FixturePayload {
fixture_path: fixture_path.display().to_string(),
program_id, program_id,
mint, mint,
source, source,
@@ -581,6 +577,31 @@ mod tests {
}; };
} }
#[test]
fn fixture_payload_never_serializes_wallet_path() {
let payload = crate::DemoSplToken2022FixturePayload {
program_id: "program".to_string(),
mint: "mint".to_string(),
source: "source".to_string(),
destination: "destination".to_string(),
close_account: "close".to_string(),
delegate: "delegate".to_string(),
authority: "authority".to_string(),
freeze_authority: "freeze".to_string(),
decimals: 9,
mint_amount_raw: "1".to_string(),
transfer_amount_raw: "1".to_string(),
approve_amount_raw: "1".to_string(),
burn_amount_raw: "1".to_string(),
};
let serialized = serde_json::to_string(&payload);
assert!(serialized.is_ok());
if let std::result::Result::Ok(json) = serialized {
assert!(!json.contains("fixturePath"));
assert!(!json.contains("WALLET-PATH-CANARY"));
}
}
#[test] #[test]
fn every_public_scenario_builds_one_typed_operation() { fn every_public_scenario_builds_one_typed_operation() {
for scenario in [ for scenario in [

View File

@@ -0,0 +1,52 @@
// file: kb-app-demo-desktop/src/demo_transport.rs
// version: 1
//! UI-safe transport DTOs shared by HTTP and WebSocket demo surfaces.
use ts_rs::TS; // rust-rules: derive-import
/// UI-safe endpoint role and local routing limits.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[serde(rename_all = "camelCase")]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_transport/DemoEndpointRolePayload.ts"
)]
pub(crate) struct DemoEndpointRolePayload {
/// Role code used by endpoint pools.
pub(crate) role: std::string::String,
/// Whether the role is enabled.
pub(crate) enabled: bool,
/// Request or subscription kinds handled by this role.
pub(crate) request_kinds: std::vec::Vec<std::string::String>,
/// Role priority where lower values are preferred.
pub(crate) priority: u32,
/// Requests per second allowed for this role.
pub(crate) requests_per_second: u32,
/// Burst capacity allowed for this role.
pub(crate) burst_capacity: u32,
/// Maximum concurrent requests allowed for this role.
pub(crate) max_concurrent_requests: u32,
/// Maximum subscriptions allowed for this role.
pub(crate) max_subscriptions: u32,
/// Pause after a rate limit response in milliseconds.
#[ts(type = "number")]
pub(crate) pause_after_rate_limit_ms: u64,
}
impl crate::DemoEndpointRolePayload {
/// Converts one backend transport role snapshot into the UI-safe application contract.
pub(crate) fn from_snapshot(snapshot: ks_onchain_transport::EndpointRoleSnapshot) -> Self {
return Self {
role: snapshot.role,
enabled: snapshot.enabled,
request_kinds: snapshot.request_kinds,
priority: snapshot.priority,
requests_per_second: snapshot.requests_per_second,
burst_capacity: snapshot.burst_capacity,
max_concurrent_requests: snapshot.max_concurrent_requests,
max_subscriptions: snapshot.max_subscriptions,
pause_after_rate_limit_ms: snapshot.pause_after_rate_limit_ms,
};
}
}

View File

@@ -1,5 +1,5 @@
// file: kb-app-demo-desktop/src/demo_ws.rs // file: kb-app-demo-desktop/src/demo_ws.rs
// version: 20 // version: 23
//! Standard Solana WebSocket demo commands backed by `ks_onchain_transport::WsSession`. //! Standard Solana WebSocket demo commands backed by `ks_onchain_transport::WsSession`.
@@ -59,6 +59,24 @@ pub(crate) struct DemoWsOptionsPayload {
pub(crate) methods: std::vec::Vec<crate::DemoWsMethodOption>, pub(crate) methods: std::vec::Vec<crate::DemoWsMethodOption>,
} }
/// UI-safe snapshot of one configured WebSocket endpoint.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[serde(rename_all = "camelCase")]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_ws/DemoWsEndpointPayload.ts"
)]
pub(crate) struct DemoWsEndpointPayload {
/// Logical endpoint name.
pub(crate) endpoint_name: std::string::String,
/// Provider name.
pub(crate) provider: std::string::String,
/// UI-safe role and local-limit snapshots.
pub(crate) roles: std::vec::Vec<crate::DemoEndpointRolePayload>,
/// Local endpoint status.
pub(crate) status: std::string::String,
}
/// Request payload for one WebSocket subscription demo command. /// Request payload for one WebSocket subscription demo command.
#[derive(Clone, Debug, serde::Deserialize, TS)] #[derive(Clone, Debug, serde::Deserialize, TS)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
@@ -91,8 +109,6 @@ pub(crate) struct DemoWsExecutionPayload {
pub(crate) endpoint_name: std::string::String, pub(crate) endpoint_name: std::string::String,
/// Selected provider name. /// Selected provider name.
pub(crate) provider: std::string::String, pub(crate) provider: std::string::String,
/// Selected endpoint URL.
pub(crate) endpoint_url: std::string::String,
/// Required role used by the selection. /// Required role used by the selection.
pub(crate) role: std::string::String, pub(crate) role: std::string::String,
/// JSON-RPC method name. /// JSON-RPC method name.
@@ -120,8 +136,6 @@ pub(crate) struct DemoWsStatusPayload {
pub(crate) connected: bool, pub(crate) connected: bool,
/// Selected endpoint name. /// Selected endpoint name.
pub(crate) endpoint_name: std::option::Option<std::string::String>, pub(crate) endpoint_name: std::option::Option<std::string::String>,
/// Selected endpoint URL.
pub(crate) endpoint_url: std::option::Option<std::string::String>,
/// Last subscription method. /// Last subscription method.
pub(crate) method: std::option::Option<std::string::String>, pub(crate) method: std::option::Option<std::string::String>,
/// Last unsubscribe method. /// Last unsubscribe method.
@@ -230,7 +244,6 @@ pub(crate) async fn demo_ws_connect(
return std::result::Result::Ok(crate::DemoWsExecutionPayload { return std::result::Result::Ok(crate::DemoWsExecutionPayload {
endpoint_name: selected_client.endpoint_name().to_string(), endpoint_name: selected_client.endpoint_name().to_string(),
provider: selected_client.provider().to_string(), provider: selected_client.provider().to_string(),
endpoint_url: selected_client.endpoint_url().to_string(),
role, role,
method: method.clone(), method: method.clone(),
request_kind: ks_onchain_transport::request_kind_from_method(&method), request_kind: ks_onchain_transport::request_kind_from_method(&method),
@@ -259,15 +272,17 @@ pub(crate) async fn demo_ws_status(
/// Lists WebSocket endpoints available through the configured pool. /// Lists WebSocket endpoints available through the configured pool.
pub(crate) fn demo_ws_list_pool_clients( pub(crate) fn demo_ws_list_pool_clients(
state: tauri::State<'_, crate::AppState>, state: tauri::State<'_, crate::AppState>,
) -> std::result::Result< ) -> std::result::Result<std::vec::Vec<crate::DemoWsEndpointPayload>, std::string::String> {
std::vec::Vec<ks_onchain_transport::WsPoolClientSnapshot>,
std::string::String,
> {
let pool = match state.demo_ws_pool() { let pool = match state.demo_ws_pool() {
std::result::Result::Ok(value) => value, std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()), std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
}; };
return std::result::Result::Ok(pool.snapshot()); let snapshots = pool.snapshot();
let mut endpoints = std::vec::Vec::new();
for snapshot in snapshots {
endpoints.push(ws_endpoint_payload(snapshot));
}
return std::result::Result::Ok(endpoints);
} }
/// Lists selectable WebSocket roles and methods for the demo UI. /// Lists selectable WebSocket roles and methods for the demo UI.
@@ -393,7 +408,7 @@ async fn ensure_session(
if let std::option::Option::Some(session) = existing { if let std::option::Option::Some(session) = existing {
let snapshot = session.snapshot().await; let snapshot = session.snapshot().await;
if snapshot.state != ks_onchain_transport::WsSessionState::Disconnected { if snapshot.state != ks_onchain_transport::WsSessionState::Disconnected {
if snapshot.endpoint_url != selected_client.endpoint_url() { if snapshot.endpoint_name.as_str() != selected_client.endpoint_name() {
return std::result::Result::Err(format!( return std::result::Result::Err(format!(
"demo WebSocket session already uses endpoint '{}'; disconnect before selecting '{}'", "demo WebSocket session already uses endpoint '{}'; disconnect before selecting '{}'",
snapshot.endpoint_name, snapshot.endpoint_name,
@@ -569,7 +584,6 @@ fn status_from_snapshot(
return crate::DemoWsStatusPayload { return crate::DemoWsStatusPayload {
connected: snapshot.state != ks_onchain_transport::WsSessionState::Disconnected, connected: snapshot.state != ks_onchain_transport::WsSessionState::Disconnected,
endpoint_name: std::option::Option::Some(snapshot.endpoint_name), endpoint_name: std::option::Option::Some(snapshot.endpoint_name),
endpoint_url: std::option::Option::Some(snapshot.endpoint_url),
method, method,
unsubscribe_method, unsubscribe_method,
subscription_id, subscription_id,
@@ -585,7 +599,6 @@ fn disconnected_status() -> crate::DemoWsStatusPayload {
return crate::DemoWsStatusPayload { return crate::DemoWsStatusPayload {
connected: false, connected: false,
endpoint_name: std::option::Option::None, endpoint_name: std::option::Option::None,
endpoint_url: std::option::Option::None,
method: std::option::Option::None, method: std::option::Option::None,
unsubscribe_method: std::option::Option::None, unsubscribe_method: std::option::Option::None,
subscription_id: std::option::Option::None, subscription_id: std::option::Option::None,
@@ -594,6 +607,21 @@ fn disconnected_status() -> crate::DemoWsStatusPayload {
}; };
} }
fn ws_endpoint_payload(
snapshot: ks_onchain_transport::WsPoolClientSnapshot,
) -> crate::DemoWsEndpointPayload {
let mut roles = std::vec::Vec::new();
for role in snapshot.roles {
roles.push(crate::DemoEndpointRolePayload::from_snapshot(role));
}
return crate::DemoWsEndpointPayload {
endpoint_name: snapshot.endpoint_name,
provider: snapshot.provider,
roles,
status: snapshot.status,
};
}
fn build_ws_role_options( fn build_ws_role_options(
snapshots: std::vec::Vec<ks_onchain_transport::WsPoolClientSnapshot>, snapshots: std::vec::Vec<ks_onchain_transport::WsPoolClientSnapshot>,
) -> std::vec::Vec<crate::DemoWsRoleOption> { ) -> std::vec::Vec<crate::DemoWsRoleOption> {
@@ -957,10 +985,46 @@ mod tests {
); );
} }
#[test]
fn endpoint_and_status_payloads_never_serialize_resolved_url() {
let endpoint_snapshot = ks_onchain_transport::WsPoolClientSnapshot {
endpoint_name: "helius".to_string(),
provider: "helius".to_string(),
roles: std::vec::Vec::new(),
status: "idle".to_string(),
};
let endpoint_payload = super::ws_endpoint_payload(endpoint_snapshot);
let endpoint_json = match serde_json::to_string(&endpoint_payload) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
panic!("cannot serialize WebSocket endpoint payload: {error}")
},
};
assert!(!endpoint_json.contains("WS-SECRET-CANARY"));
assert!(!endpoint_json.contains("endpointUrl"));
let status = super::status_from_snapshot(ks_onchain_transport::WsSessionSnapshot {
endpoint_name: "helius".to_string(),
provider: "helius".to_string(),
state: ks_onchain_transport::WsSessionState::Connected,
reconnect_count: 0,
capabilities: ks_onchain_transport::StandardWsCapabilities::default(),
subscriptions: std::vec::Vec::new(),
});
let status_json = match serde_json::to_string(&status) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
panic!("cannot serialize WebSocket status payload: {error}")
},
};
assert!(!status_json.contains("WS-SECRET-CANARY"));
assert!(!status_json.contains("endpointUrl"));
}
#[test] #[test]
fn tauri_numeric_bindings_use_json_compatible_numbers() { fn tauri_numeric_bindings_use_json_compatible_numbers() {
let config = ts_rs::Config::default(); let config = ts_rs::Config::default();
let declarations = [ let declarations = [
<crate::DemoEndpointRolePayload as TS>::decl(&config),
<crate::DemoWsExecutionPayload as TS>::decl(&config), <crate::DemoWsExecutionPayload as TS>::decl(&config),
<crate::DemoWsStatusPayload as TS>::decl(&config), <crate::DemoWsStatusPayload as TS>::decl(&config),
<crate::DemoWsSubscriptionStatusPayload as TS>::decl(&config), <crate::DemoWsSubscriptionStatusPayload as TS>::decl(&config),

View File

@@ -0,0 +1,96 @@
// file: kb-app-demo-desktop/src/desktop_config.rs
// version: 2
//! Desktop-owned application settings embedded in one generic binary composition profile.
const DESKTOP_APPLICATION_SCHEMA: &str =
include_str!("../../config/schemas/kb-app-demo-desktop.application.config.schema.json");
/// Desktop-owned settings for one selected composition profile.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
pub(crate) struct DesktopApplicationConfig {
/// Application display/name identity.
pub(crate) name: std::string::String,
/// Human-readable environment classification.
pub(crate) environment: std::string::String,
/// Desktop demo feature flags.
pub(crate) demo: crate::DesktopDemoConfig,
}
/// Desktop-owned demo feature flags.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
pub(crate) struct DesktopDemoConfig {
/// Enables live demo pages.
pub(crate) live_demo_enabled: bool,
/// Enables trading demo pages.
pub(crate) trading_demo_enabled: bool,
}
/// Parses and validates desktop-owned settings from one generic composition profile.
pub(crate) fn desktop_application_config(
profile: &ks_config::CompositionProfileConfig,
) -> ks_core::Result<DesktopApplicationConfig> {
let application = match profile.application.as_ref() {
std::option::Option::Some(value) => value,
std::option::Option::None => {
return std::result::Result::Err(ks_core::Error::new(
"desktop_application_config_missing",
profile.name.clone(),
));
},
};
match ks_config::validate_json_value_against_schema(
DESKTOP_APPLICATION_SCHEMA,
application,
"desktop_application_config_schema_validation_failed",
) {
std::result::Result::Ok(()) => (),
std::result::Result::Err(error) => return std::result::Result::Err(error),
}
return match serde_json::from_value::<DesktopApplicationConfig>(application.clone()) {
std::result::Result::Ok(value) => std::result::Result::Ok(value),
std::result::Result::Err(_) => std::result::Result::Err(ks_core::Error::new(
"desktop_application_config_decode_failed",
"desktop application configuration could not be decoded",
)),
};
}
#[cfg(test)]
mod tests {
#[test]
fn default_composition_contains_valid_desktop_application_fragments() {
let raw = include_str!("../../config/kb-app-demo-desktop.default.config.json");
let composition = match ks_config::parse_composition_json(raw) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("default composition must parse: {error}"),
};
for profile in &composition.profiles {
let result = super::desktop_application_config(profile);
assert!(result.is_ok());
}
}
#[test]
fn application_fragment_rejects_unknown_or_missing_fields() {
let profile = ks_config::CompositionProfileConfig {
name: "test".to_string(),
application: std::option::Option::Some(serde_json::json!({
"name": "kb-app-demo-desktop",
"environment": "test",
"demo": {
"live_demo_enabled": true,
"trading_demo_enabled": false
},
"secret": "must-not-be-accepted"
})),
logging_profile: std::option::Option::None,
transport_profile: std::option::Option::None,
listeners_profile: std::option::Option::None,
store_profile: std::option::Option::None,
wallet_profile: std::option::Option::None,
execution_profile: std::option::Option::None,
};
assert!(super::desktop_application_config(&profile).is_err());
}
}

View File

@@ -1,5 +1,5 @@
// file: kb-app-demo-desktop/src/lib.rs // file: kb-app-demo-desktop/src/lib.rs
// version: 34 // version: 37
//! Tauri desktop demo application for `khadhroony-bot3`. //! Tauri desktop demo application for `khadhroony-bot3`.
@@ -29,7 +29,9 @@ mod demo_sql_diag;
mod demo_sql_pg_core; mod demo_sql_pg_core;
mod demo_sql_pg_raw; mod demo_sql_pg_raw;
mod demo_sql_replay_candidates; mod demo_sql_replay_candidates;
mod demo_transport;
mod demo_ws; mod demo_ws;
mod desktop_config;
mod frontend_log; mod frontend_log;
mod main_window; mod main_window;
mod splash; mod splash;
@@ -64,8 +66,28 @@ pub(crate) use self::demo_backfill::demo_backfill_cancel;
pub(crate) use self::demo_backfill::demo_backfill_execute; pub(crate) use self::demo_backfill::demo_backfill_execute;
/// Returns endpoint roles, known programs and conservative backfill defaults. /// Returns endpoint roles, known programs and conservative backfill defaults.
pub(crate) use self::demo_backfill::demo_backfill_options; pub(crate) use self::demo_backfill::demo_backfill_options;
/// Bounded internal configuration diagnostics shown by the configuration window.
pub(crate) use self::demo_config::DemoConfigDiagnosticPayload;
/// Bounded endpoint diagnostics shown by the configuration window.
pub(crate) use self::demo_config::DemoConfigEndpointDiagnosticPayload;
/// Bounded execution diagnostics shown by the configuration window.
pub(crate) use self::demo_config::DemoConfigExecutionDiagnosticPayload;
/// Configuration payload shown by the configuration window. /// Configuration payload shown by the configuration window.
pub(crate) use self::demo_config::DemoConfigPayload; pub(crate) use self::demo_config::DemoConfigPayload;
/// Public desktop demo feature flags.
pub(crate) use self::demo_config::DemoConfigPublicDemoPayload;
/// Public endpoint identity shown by the configuration window.
pub(crate) use self::demo_config::DemoConfigPublicEndpointPayload;
/// Public listener summary shown by the configuration window.
pub(crate) use self::demo_config::DemoConfigPublicListenersPayload;
/// Explicit public configuration projection shown by the configuration window.
pub(crate) use self::demo_config::DemoConfigPublicPayload;
/// Bounded source-path diagnostics shown by the configuration window.
pub(crate) use self::demo_config::DemoConfigSourceDiagnosticPayload;
/// Bounded store diagnostics shown by the configuration window.
pub(crate) use self::demo_config::DemoConfigStoreDiagnosticPayload;
/// Bounded wallet diagnostics shown by the configuration window.
pub(crate) use self::demo_config::DemoConfigWalletDiagnosticPayload;
/// Builds the configuration payload from shared application state. /// Builds the configuration payload from shared application state.
pub(crate) use self::demo_config::demo_config_payload; pub(crate) use self::demo_config::demo_config_payload;
/// Internal demo core extraction item. /// Internal demo core extraction item.
@@ -228,6 +250,8 @@ pub(crate) use self::demo_execution_solana_core::select_devnet_profile;
pub(crate) use self::demo_execution_spl::DevnetSplValidationScenarioPayload; pub(crate) use self::demo_execution_spl::DevnetSplValidationScenarioPayload;
/// Returns the complete ordered Devnet SPL validation inventory for milestone 0.4.6. /// Returns the complete ordered Devnet SPL validation inventory for milestone 0.4.6.
pub(crate) use self::demo_execution_spl::demo_execution_spl_validation_scenarios; pub(crate) use self::demo_execution_spl::demo_execution_spl_validation_scenarios;
/// UI-safe HTTP endpoint snapshot.
pub(crate) use self::demo_http::DemoHttpEndpointPayload;
/// HTTP demo response payload. /// HTTP demo response payload.
pub(crate) use self::demo_http::DemoHttpExecutionPayload; pub(crate) use self::demo_http::DemoHttpExecutionPayload;
/// One selectable HTTP JSON-RPC method. /// One selectable HTTP JSON-RPC method.
@@ -336,6 +360,10 @@ pub(crate) use self::demo_sql_replay_candidates::load_demo_sql_replay_entities;
pub(crate) use self::demo_sql_replay_candidates::load_demo_sql_replay_programs; pub(crate) use self::demo_sql_replay_candidates::load_demo_sql_replay_programs;
/// Loads bounded transaction replay candidates from PostgreSQL. /// Loads bounded transaction replay candidates from PostgreSQL.
pub(crate) use self::demo_sql_replay_candidates::load_demo_sql_replay_transactions; pub(crate) use self::demo_sql_replay_candidates::load_demo_sql_replay_transactions;
/// UI-safe endpoint role shared by HTTP and WebSocket diagnostics.
pub(crate) use self::demo_transport::DemoEndpointRolePayload;
/// UI-safe WebSocket endpoint snapshot.
pub(crate) use self::demo_ws::DemoWsEndpointPayload;
/// WebSocket execution response payload. /// WebSocket execution response payload.
pub(crate) use self::demo_ws::DemoWsExecutionPayload; pub(crate) use self::demo_ws::DemoWsExecutionPayload;
/// WebSocket message emitted to the frontend. /// WebSocket message emitted to the frontend.
@@ -368,6 +396,12 @@ pub(crate) use self::demo_ws::demo_ws_status;
pub(crate) use self::demo_ws::demo_ws_unsubscribe; pub(crate) use self::demo_ws::demo_ws_unsubscribe;
/// Disconnects the persistent WebSocket session during application shutdown. /// Disconnects the persistent WebSocket session during application shutdown.
pub(crate) use self::demo_ws::disconnect_demo_ws_app_state; pub(crate) use self::demo_ws::disconnect_demo_ws_app_state;
/// Desktop-owned application settings selected from the active composition profile.
pub(crate) use self::desktop_config::DesktopApplicationConfig;
/// Desktop-owned demo feature flags.
pub(crate) use self::desktop_config::DesktopDemoConfig;
/// Parses desktop-owned settings from one composition profile.
pub(crate) use self::desktop_config::desktop_application_config;
/// Frontend logging payload. /// Frontend logging payload.
pub(crate) use self::frontend_log::FrontendLogPayload; pub(crate) use self::frontend_log::FrontendLogPayload;
/// Emits one normalized frontend log event. /// Emits one normalized frontend log event.

View File

@@ -1,5 +1,5 @@
// file: kb-app-demo-desktop/src/tauri.rs // file: kb-app-demo-desktop/src/tauri.rs
// version: 37 // version: 38
//! Tauri runtime assembly and private command wrappers. //! Tauri runtime assembly and private command wrappers.
@@ -257,7 +257,7 @@ fn open_demo_http_window(
#[tauri::command] #[tauri::command]
fn demo_http_list_pool_clients( fn demo_http_list_pool_clients(
state: tauri::State<'_, crate::AppState>, state: tauri::State<'_, crate::AppState>,
) -> std::vec::Vec<ks_onchain_transport::HttpPoolClientSnapshot> { ) -> std::vec::Vec<crate::DemoHttpEndpointPayload> {
return crate::demo_http_list_pool_clients(state); return crate::demo_http_list_pool_clients(state);
} }
@@ -284,10 +284,7 @@ fn open_demo_ws_window(
#[tauri::command] #[tauri::command]
fn demo_ws_list_pool_clients( fn demo_ws_list_pool_clients(
state: tauri::State<'_, crate::AppState>, state: tauri::State<'_, crate::AppState>,
) -> std::result::Result< ) -> std::result::Result<std::vec::Vec<crate::DemoWsEndpointPayload>, std::string::String> {
std::vec::Vec<ks_onchain_transport::WsPoolClientSnapshot>,
std::string::String,
> {
return crate::demo_ws_list_pool_clients(state); return crate::demo_ws_list_pool_clients(state);
} }

View File

@@ -1,8 +1,19 @@
<!-- file: ks-config/CHANGELOG.md --> <!-- file: ks-config/CHANGELOG.md -->
<!-- version: 17 --> <!-- version: 19 -->
# CHANGELOG — ks-config # CHANGELOG — ks-config
## `0.5.1-pre.008`
- Fix: use `Result::is_err()` for execution and wallet schema validation checks, removing the Clippy redundant-pattern warnings without changing fail-closed behavior.
- retire `ts-rs` de `ks-config`, supprime ses bindings TypeScript générés et réserve les projections frontend aux DTO des applications propriétaires ;
- rend les contrats source/runtime susceptibles de contenir des valeurs résolues non sérialisables et non `Debug` par défaut, et retire les sérialiseurs publics de `AppConfig` ;
- ajoute la classification `Secret > Internal > Public`, y compris pour les chaînes composées contenant plusieurs placeholders d'environnement, ainsi qu'une représentation diagnostic générique qui ne révèle jamais les valeurs secret/internal ;
- rend la section `application` des compositions opaque pour `ks-config` et fournit un helper générique de validation JSON Schema pour les fragments possédés par les binaires ;
- durcit les erreurs de validation/décodage afin de ne plus recopier les valeurs rejetées, notamment pour les URLs et fragments contenant potentiellement des secrets ;
- ajoute un test d'API externe et des canaris de non-divulgation pour les helpers de sensibilité.
## `0.5.1-pre.007` ## `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` ; - 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` ;

View File

@@ -1,5 +1,5 @@
# file: ks-config/Cargo.toml # file: ks-config/Cargo.toml
# version: 4 # version: 5
[package] [package]
name = "ks-config" name = "ks-config"
@@ -14,7 +14,6 @@ ks-core = { path = "../ks-core" }
jsonschema.workspace = true jsonschema.workspace = true
serde.workspace = true serde.workspace = true
serde_json.workspace = true serde_json.workspace = true
ts-rs.workspace = true
[lints] [lints]
workspace = true workspace = true

View File

@@ -1,5 +1,5 @@
<!-- file: ks-config/README.md --> <!-- file: ks-config/README.md -->
<!-- version: 10 --> <!-- version: 11 -->
# ks-config # ks-config
@@ -46,7 +46,9 @@ Les valeurs globales telles que `logs_directory` et `wallets_directory` ne sont
- `read_wallet_json_file_with_environment`, `wallet_profile`, `default_wallet_profile` ; - `read_wallet_json_file_with_environment`, `wallet_profile`, `default_wallet_profile` ;
- `read_execution_json_file_with_environment`, `execution_profile`, `default_execution_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é. - `classify_environment_variable`, `classify_environment_template`, `diagnostic_environment_value` ;
- `validate_json_value_against_schema` pour les fragments possédés par les binaires ;
- `AppConfig`, `ProfileConfig` pendant la phase de compatibilité backend-only.
## Frontières ## Frontières
@@ -54,7 +56,9 @@ Les valeurs globales telles que `logs_directory` et `wallets_directory` ne sont
`ks-onchain-transport` peut consommer directement `TransportProfileConfig`. `ks-onchain-transport` peut consommer directement `TransportProfileConfig`.
Les dérivations TS-RS des types de `ks-config` sont encore présentes pendant `pre.007`, mais elles ne sont pas considérées comme une frontière durable. L'audit montre que le frontend desktop n'importe pas directement ces bindings ; leur suppression ou réduction est prévue avec les DTO publics sûrs de la prochaine prerelease. Depuis `0.5.1-pre.008`, `ks-config` ne dépend plus de TS-RS et ne produit plus de bindings TypeScript. Ses contrats source/runtime peuvent contenir des secrets résolus : ils ne dérivent donc ni `serde::Serialize` ni `Debug`. Les applications propriétaires construisent leurs DTO publics et diagnostics bornés champ par champ.
La section `application` d'une composition est volontairement opaque pour `ks-config`. Chaque binaire propriétaire définit son schéma et le valide avec `validate_json_value_against_schema`; le desktop utilise `config/schemas/kb-app-demo-desktop.application.config.schema.json`.
## Documents ## Documents

View File

@@ -1,16 +1,9 @@
<!-- file: ks-config/TODO.md --> <!-- file: ks-config/TODO.md -->
<!-- version: 9 --> <!-- version: 10 -->
# TODO — ks-config # TODO — ks-config
## Série `0.5.x` ## Série `0.5.x`
- [ ] `0.5.1` - appliquer à lexécution les classes `KS_SECRET_*` / `KB_SECRET_*`, `KS_PUBLIC_*` / `KB_PUBLIC_*` et internes, avec propagation de sensibilité aux valeurs composées. - [ ] `0.5.1-pre.009` - réconcilier une dernière fois schémas, exemples, guide de configuration et contrat d'API externe avant archivage du plan.
- [ ] `0.5.1` - séparer les représentations source, runtime résolue, publique et diagnostic.
- [ ] `0.5.1` - interdire quun secret résolu soit sérialisé, loggé, inclus dans une erreur ou transmis via Tauri.
- [ ] `0.5.1` - supprimer lexposition 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 lidentité entre chaque schéma embarqué et son fichier sous `config/schemas/`. - [ ] Contrat - maintenir lidentité entre chaque schéma embarqué et son fichier sous `config/schemas/`.
- [ ] Tests - ajouter les tests dAPI externe et les canaris de non-divulgation.
- [ ] Documentation - maintenir guides, exemples et `.env.example` avec limplémentation correspondante.
- [ ] Intégration - coordonner les surfaces publiques sûres avec logging, transports, pipeline, scénarios et applications.

View File

@@ -1,5 +1,5 @@
<!-- file: ks-config/USAGE.md --> <!-- file: ks-config/USAGE.md -->
<!-- version: 9 --> <!-- version: 10 -->
# Utilisation de ks-config # Utilisation de ks-config
@@ -108,6 +108,14 @@ KS_WALLETS_DIRECTORY
Le schéma de ce contrat est `config/schemas/resolved.app.config.schema.json`. Les fixtures correspondantes sont sous `test-fixtures/config/`. Le schéma de ce contrat est `config/schemas/resolved.app.config.schema.json`. Les fixtures correspondantes sont sous `test-fixtures/config/`.
## Frontière source/runtime/public/diagnostic
`AppConfig`, `ProfileConfig` et les documents spécialisés chargés avec résolution d'environnement sont des contrats backend-only. Ils peuvent contenir des URLs, DSN, chemins ou autres valeurs sensibles et ne dérivent ni `serde::Serialize` ni `Debug`. `ks-config` n'offre plus de sérialiseur du runtime résolu.
La sensibilité des variables suit `Secret > Internal > Public`. `classify_environment_template` applique cette règle aux chaînes composées : une URL contenant `${KS_SECRET_HELIUS_API_KEY}` est secrète même si le champ s'appelle simplement `url`. `diagnostic_environment_value` ne retourne jamais une valeur secrète ou interne en clair.
Les fragments propres à un binaire restent opaques dans `CompositionProfileConfig.application`. Le propriétaire les valide avec `validate_json_value_against_schema` et son schéma dédié.
## Frontière TypeScript ## Frontière TypeScript
Les bindings TS-RS générés depuis `ks-config` ne sont pas importés directement par le frontend desktop actuel. Ils sont donc considérés comme transitoires jusqu'à la prochaine prerelease, qui introduira les DTO publics/diagnostiques et supprimera les exports TypeScript non justifiés des crates généralistes. `ks-config` ne dépend plus de TS-RS et ne génère aucun binding TypeScript. Les types traversant Tauri sont définis comme DTO/wrappers dans l'application propriétaire ; `kb-app-demo-desktop` construit notamment ses projections de configuration publique et diagnostic sans sérialiser `AppConfig/ProfileConfig`.

View File

@@ -1,5 +1,5 @@
// file: ks-config/src/composition.rs // file: ks-config/src/composition.rs
// version: 2 // version: 3
//! Binary composition configuration and resolution into the shared runtime profile contract. //! Binary composition configuration and resolution into the shared runtime profile contract.
@@ -13,7 +13,7 @@ const DEFAULT_WALLET_CONFIG_PATH: &str = "config/wallet.config.json";
const DEFAULT_EXECUTION_CONFIG_PATH: &str = "config/execution.config.json"; 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, serde::Deserialize, Eq, PartialEq)]
pub struct CompositionConfigDocument { pub struct CompositionConfigDocument {
/// Active binary composition profile name. /// Active binary composition profile name.
pub active_profile: std::string::String, pub active_profile: std::string::String,
@@ -24,7 +24,7 @@ pub struct CompositionConfigDocument {
} }
/// Paths of shared configuration documents consumed by one binary composition. /// Paths of shared configuration documents consumed by one binary composition.
#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize)] #[derive(Clone, serde::Deserialize, Eq, PartialEq)]
pub struct CompositionConfigSources { pub struct CompositionConfigSources {
/// Logging configuration document path relative to the workspace root unless absolute. /// Logging configuration document path relative to the workspace root unless absolute.
pub logging: std::string::String, pub logging: std::string::String,
@@ -41,12 +41,12 @@ pub struct CompositionConfigSources {
} }
/// Named composition profile with optional overrides of each shared document default. /// Named composition profile with optional overrides of each shared document default.
#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize)] #[derive(Clone, serde::Deserialize, Eq, PartialEq)]
pub struct CompositionProfileConfig { pub struct CompositionProfileConfig {
/// Composition profile code. /// Composition profile code.
pub name: std::string::String, pub name: std::string::String,
/// Application metadata retained until the application-specific split is completed. /// Optional binary-owned application settings opaque to the shared composition layer.
pub app: crate::AppSectionConfig, pub application: std::option::Option<serde_json::Value>,
/// Optional logging profile override. /// Optional logging profile override.
pub logging_profile: std::option::Option<std::string::String>, pub logging_profile: std::option::Option<std::string::String>,
/// Optional transport profile override. /// Optional transport profile override.
@@ -59,12 +59,10 @@ pub struct CompositionProfileConfig {
pub wallet_profile: std::option::Option<std::string::String>, pub wallet_profile: std::option::Option<std::string::String>,
/// Optional execution profile override. /// Optional execution profile override.
pub execution_profile: std::option::Option<std::string::String>, pub execution_profile: std::option::Option<std::string::String>,
/// Desktop demo flags retained until the application-specific split is completed.
pub demo: crate::DemoConfig,
} }
/// Resolved application configuration and the source documents needed by a binary. /// Resolved application configuration and the source documents needed by a binary.
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Clone, Eq, PartialEq)]
pub struct ComposedAppConfig { pub struct ComposedAppConfig {
/// Parsed binary composition document. /// Parsed binary composition document.
pub composition: CompositionConfigDocument, pub composition: CompositionConfigDocument,
@@ -127,9 +125,9 @@ pub fn validate_composition_json_schema(raw_json: &str) -> ks_core::Result<()> {
}; };
return match validator.validate(&instance) { return match validator.validate(&instance) {
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(ks_core::Error::new( std::result::Result::Err(_) => std::result::Result::Err(ks_core::Error::new(
"composition_config_schema_validation_failed", "composition_config_schema_validation_failed",
error.to_string(), "composition configuration does not satisfy its schema",
)), )),
}; };
} }
@@ -142,10 +140,10 @@ pub fn parse_composition_json(raw_json: &str) -> ks_core::Result<CompositionConf
} }
let document = match serde_json::from_str::<CompositionConfigDocument>(raw_json) { let document = match serde_json::from_str::<CompositionConfigDocument>(raw_json) {
std::result::Result::Ok(value) => value, std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => { std::result::Result::Err(_) => {
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",
error.to_string(), "composition configuration could not be decoded",
)); ));
}, },
}; };
@@ -291,7 +289,6 @@ pub fn compose_app_config(
}; };
profiles.push(crate::ProfileConfig { profiles.push(crate::ProfileConfig {
name: profile.name.clone(), name: profile.name.clone(),
app: profile.app.clone(),
database: store_profile.database.clone(), database: store_profile.database.clone(),
solana: crate::SolanaConfig { solana: crate::SolanaConfig {
http_endpoints: transport_profile.http_endpoints, http_endpoints: transport_profile.http_endpoints,
@@ -300,7 +297,6 @@ pub fn compose_app_config(
}, },
wallet: wallet_profile, wallet: wallet_profile,
execution: execution_profile.config.clone(), execution: execution_profile.config.clone(),
demo: profile.demo.clone(),
}); });
} }
let config = crate::AppConfig { let config = crate::AppConfig {
@@ -437,20 +433,13 @@ pub fn read_default_shared_app_config_with_environment(
}, },
profiles: vec![CompositionProfileConfig { profiles: vec![CompositionProfileConfig {
name: profile_name.clone(), name: profile_name.clone(),
app: crate::AppSectionConfig { application: std::option::Option::None,
name: "khadhroony-solana".to_string(),
environment: profile_name,
},
logging_profile: std::option::Option::None, logging_profile: std::option::Option::None,
transport_profile: std::option::Option::None, transport_profile: std::option::Option::None,
listeners_profile: std::option::Option::None, listeners_profile: std::option::Option::None,
store_profile: std::option::Option::None, store_profile: std::option::Option::None,
wallet_profile: std::option::Option::None, wallet_profile: std::option::Option::None,
execution_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); return compose_app_config(&composition, &transport, &listeners, &store, &wallet, &execution);
@@ -593,7 +582,7 @@ 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/exemples/example.kb-app-demo-desktop.default.config.json");
#[test] #[test]
fn default_and_example_compositions_validate() { fn default_and_example_compositions_validate() {
@@ -613,6 +602,7 @@ mod tests {
panic!("local Devnet composition must resolve: {error}") panic!("local Devnet composition must resolve: {error}")
}, },
}; };
assert!(profile.application.is_some());
assert!(profile.logging_profile.is_none()); assert!(profile.logging_profile.is_none());
assert!(profile.transport_profile.is_none()); assert!(profile.transport_profile.is_none());
assert!(profile.store_profile.is_none()); assert!(profile.store_profile.is_none());

View File

@@ -1,5 +1,5 @@
// file: ks-config/src/environment.rs // file: ks-config/src/environment.rs
// version: 5 // version: 6
//! Environment-file loading and configuration placeholder resolution. //! Environment-file loading and configuration placeholder resolution.
@@ -32,9 +32,9 @@ pub fn load_workspace_environment(
std::result::Result::Ok(()) => std::result::Result::Ok(EnvironmentLoadReport { std::result::Result::Ok(()) => std::result::Result::Ok(EnvironmentLoadReport {
loaded_path: std::option::Option::Some(selected_path), loaded_path: std::option::Option::Some(selected_path),
}), }),
std::result::Result::Err(error) => std::result::Result::Err(ks_core::Error::new( std::result::Result::Err(_) => std::result::Result::Err(ks_core::Error::new(
"config_env_file_load_failed", "config_env_file_load_failed",
format!("{}: {error}", selected_path.display()), format!("failed to load environment file {}", selected_path.display()),
)), )),
}; };
} }

View File

@@ -1,5 +1,5 @@
// file: ks-config/src/execution.rs // file: ks-config/src/execution.rs
// version: 1 // version: 3
//! Independent execution policy configuration document loading and validation. //! Independent execution policy configuration document loading and validation.
@@ -7,7 +7,7 @@ const EXECUTION_JSON_SCHEMA: &str =
include_str!("../../config/schemas/execution.config.schema.json"); include_str!("../../config/schemas/execution.config.schema.json");
/// Root execution policy document with one default profile and named alternatives. /// Root execution policy document with one default profile and named alternatives.
#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize)] #[derive(Clone, serde::Deserialize, Eq, PartialEq)]
pub struct ExecutionConfigDocument { pub struct ExecutionConfigDocument {
/// Default execution profile used when a composition does not override it. /// Default execution profile used when a composition does not override it.
pub default_profile: std::string::String, pub default_profile: std::string::String,
@@ -16,7 +16,7 @@ pub struct ExecutionConfigDocument {
} }
/// Named execution policy profile. /// Named execution policy profile.
#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize)] #[derive(Clone, serde::Deserialize, Eq, PartialEq)]
pub struct ExecutionProfileConfig { pub struct ExecutionProfileConfig {
/// Profile code. /// Profile code.
pub name: std::string::String, pub name: std::string::String,
@@ -59,18 +59,18 @@ pub fn parse_execution_json(raw_json: &str) -> ks_core::Result<ExecutionConfigDo
)); ));
}, },
}; };
if let std::result::Result::Err(error) = validator.validate(&instance) { if validator.validate(&instance).is_err() {
return std::result::Result::Err(ks_core::Error::new( return std::result::Result::Err(ks_core::Error::new(
"execution_config_schema_validation_failed", "execution_config_schema_validation_failed",
error.to_string(), "execution configuration does not satisfy its schema",
)); ));
} }
let document = match serde_json::from_str::<ExecutionConfigDocument>(raw_json) { let document = match serde_json::from_str::<ExecutionConfigDocument>(raw_json) {
std::result::Result::Ok(value) => value, std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => { std::result::Result::Err(_) => {
return std::result::Result::Err(ks_core::Error::new( return std::result::Result::Err(ks_core::Error::new(
"execution_config_json_decode_failed", "execution_config_json_decode_failed",
error.to_string(), "execution configuration could not be decoded",
)); ));
}, },
}; };
@@ -162,7 +162,8 @@ pub fn validate_execution_document(document: &ExecutionConfigDocument) -> ks_cor
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
const DEFAULT_EXECUTION: &str = include_str!("../../config/execution.config.json"); const DEFAULT_EXECUTION: &str = include_str!("../../config/execution.config.json");
const EXAMPLE_EXECUTION: &str = include_str!("../../config/example.execution.config.json"); const EXAMPLE_EXECUTION: &str =
include_str!("../../config/exemples/example.execution.config.json");
#[test] #[test]
fn default_and_example_execution_configs_validate() { fn default_and_example_execution_configs_validate() {

View File

@@ -1,5 +1,5 @@
// file: ks-config/src/lib.rs // file: ks-config/src/lib.rs
// version: 8 // version: 9
//! Khadhroony Solana shared configuration contracts and composition helpers. //! Khadhroony Solana shared configuration contracts and composition helpers.
#![warn(missing_docs)] #![warn(missing_docs)]
@@ -10,6 +10,8 @@ mod composition;
mod environment; mod environment;
mod execution; mod execution;
mod listeners; mod listeners;
mod schema;
mod sensitivity;
mod settings; mod settings;
mod store; mod store;
mod transport; mod transport;
@@ -97,16 +99,22 @@ pub use self::listeners::resolved_listener_config;
pub use self::listeners::validate_listeners_document; pub use self::listeners::validate_listeners_document;
/// Exposes listener JSON Schema validation. /// Exposes listener JSON Schema validation.
pub use self::listeners::validate_listeners_json_schema; pub use self::listeners::validate_listeners_json_schema;
/// Exposes generic caller-owned JSON Schema validation.
pub use self::schema::validate_json_value_against_schema;
/// Exposes Khadhroony environment-value sensitivity classification.
pub use self::sensitivity::EnvironmentValueSensitivity;
/// Exposes composed-value environment-placeholder sensitivity classification.
pub use self::sensitivity::classify_environment_template;
/// Exposes Khadhroony environment-variable namespace classification.
pub use self::sensitivity::classify_environment_variable;
/// Exposes the bounded generic diagnostic representation for an environment value.
pub use self::sensitivity::diagnostic_environment_value;
/// Exposes the account listener configuration type. /// Exposes the account listener configuration type.
pub use self::settings::AccountListenerConfig; pub use self::settings::AccountListenerConfig;
/// Exposes the root resolved runtime configuration type. /// Exposes the root resolved runtime configuration type.
pub use self::settings::AppConfig; pub use self::settings::AppConfig;
/// Exposes the application metadata configuration type.
pub use self::settings::AppSectionConfig;
/// Exposes the database configuration type. /// Exposes the database configuration type.
pub use self::settings::DatabaseConfig; pub use self::settings::DatabaseConfig;
/// Exposes the temporary demo application configuration type.
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;
/// Exposes the execution configuration type. /// Exposes the execution configuration type.
@@ -143,10 +151,6 @@ pub use self::settings::parse_config_json;
pub use self::settings::read_config_json_file; pub use self::settings::read_config_json_file;
/// Exposes resolved runtime snapshot 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.
pub use self::settings::serialize_config_json;
/// Exposes the pretty JSON serializer for resolved runtime configuration values.
pub use self::settings::serialize_config_json_pretty;
/// Exposes the typed resolved runtime configuration validator. /// Exposes the typed resolved runtime configuration validator.
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.

View File

@@ -1,5 +1,5 @@
// file: ks-config/src/listeners.rs // file: ks-config/src/listeners.rs
// version: 2 // version: 3
//! Independent Solana listener configuration document loading and validation. //! Independent Solana listener configuration document loading and validation.
@@ -7,7 +7,7 @@ const LISTENERS_JSON_SCHEMA: &str =
include_str!("../../config/schemas/listeners.config.schema.json"); include_str!("../../config/schemas/listeners.config.schema.json");
/// 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, serde::Deserialize, Eq, PartialEq)]
pub struct ListenersConfigDocument { pub struct ListenersConfigDocument {
/// Default 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 default_profile: std::string::String, pub default_profile: std::string::String,
@@ -16,7 +16,7 @@ pub struct ListenersConfigDocument {
} }
/// Named listener profile. /// Named listener profile.
#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize)] #[derive(Clone, serde::Deserialize, Eq, PartialEq)]
pub struct ListenersProfileConfig { pub struct ListenersProfileConfig {
/// Profile code. /// Profile code.
pub name: std::string::String, pub name: std::string::String,
@@ -75,9 +75,9 @@ pub fn validate_listeners_json_schema(raw_json: &str) -> ks_core::Result<()> {
}; };
return match validator.validate(&instance) { return match validator.validate(&instance) {
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(ks_core::Error::new( std::result::Result::Err(_) => std::result::Result::Err(ks_core::Error::new(
"listeners_config_schema_validation_failed", "listeners_config_schema_validation_failed",
error.to_string(), "listeners configuration does not satisfy its schema",
)), )),
}; };
} }
@@ -90,10 +90,10 @@ pub fn parse_listeners_json(raw_json: &str) -> ks_core::Result<ListenersConfigDo
} }
let document = match serde_json::from_str::<ListenersConfigDocument>(raw_json) { let document = match serde_json::from_str::<ListenersConfigDocument>(raw_json) {
std::result::Result::Ok(document) => document, std::result::Result::Ok(document) => document,
std::result::Result::Err(error) => { std::result::Result::Err(_) => {
return std::result::Result::Err(ks_core::Error::new( return std::result::Result::Err(ks_core::Error::new(
"listeners_config_json_decode_failed", "listeners_config_json_decode_failed",
error.to_string(), "listeners configuration could not be decoded",
)); ));
}, },
}; };
@@ -214,7 +214,8 @@ pub fn validate_listeners_document(document: &ListenersConfigDocument) -> ks_cor
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
const DEFAULT_LISTENERS: &str = include_str!("../../config/listeners.config.json"); const DEFAULT_LISTENERS: &str = include_str!("../../config/listeners.config.json");
const EXAMPLE_LISTENERS: &str = include_str!("../../config/example.listeners.config.json"); const EXAMPLE_LISTENERS: &str =
include_str!("../../config/exemples/example.listeners.config.json");
#[test] #[test]
fn default_and_example_listener_configs_validate() { fn default_and_example_listener_configs_validate() {

56
ks-config/src/schema.rs Normal file
View File

@@ -0,0 +1,56 @@
// file: ks-config/src/schema.rs
// version: 1
//! Generic JSON Schema validation helpers for binary-owned configuration fragments.
/// Validates one JSON value against caller-owned JSON Schema text without echoing instance values.
pub fn validate_json_value_against_schema(
schema_json: &str,
instance: &serde_json::Value,
validation_error_code: &str,
) -> ks_core::Result<()> {
let schema = match serde_json::from_str::<serde_json::Value>(schema_json) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
return std::result::Result::Err(ks_core::Error::new(
"config_fragment_schema_parse_failed",
error.to_string(),
));
},
};
let validator = match jsonschema::validator_for(&schema) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
return std::result::Result::Err(ks_core::Error::new(
"config_fragment_schema_compile_failed",
error.to_string(),
));
},
};
return match validator.validate(instance) {
std::result::Result::Ok(()) => std::result::Result::Ok(()),
std::result::Result::Err(_) => std::result::Result::Err(ks_core::Error::new(
validation_error_code,
"configuration fragment does not satisfy its schema",
)),
};
}
#[cfg(test)]
mod tests {
#[test]
fn validation_error_never_echoes_instance_secret() {
let schema = r#"{"type":"object","additionalProperties":false,"required":["value"],"properties":{"value":{"type":"integer"}}}"#;
let instance = serde_json::json!({"value": "SECRET-CANARY"});
let result = super::validate_json_value_against_schema(
schema,
&instance,
"test_fragment_validation_failed",
);
let error = match result {
std::result::Result::Ok(()) => panic!("invalid fragment must fail"),
std::result::Result::Err(error) => error,
};
assert!(!error.to_string().contains("SECRET-CANARY"));
}
}

View File

@@ -0,0 +1,160 @@
// file: ks-config/src/sensitivity.rs
// version: 1
//! Application environment-variable sensitivity classification.
/// Sensitivity assigned to one Khadhroony-owned environment value.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum EnvironmentValueSensitivity {
/// Secret value that must never cross logs, diagnostics, serialization or UI boundaries.
Secret,
/// Internal value that is hidden by default and may appear only through an explicit bounded diagnostic.
Internal,
/// Value explicitly classified as eligible for a public DTO.
Public,
}
/// Classifies every Khadhroony environment placeholder contained in arbitrary text.
///
/// The returned sensitivity follows `Secret > Internal > Public`, so an endpoint URL that embeds
/// one `KS_SECRET_*` placeholder is classified as secret even when the surrounding URL is public.
pub fn classify_environment_template(
raw: &str,
) -> std::option::Option<EnvironmentValueSensitivity> {
let bytes = raw.as_bytes();
let mut sensitivity = std::option::Option::None;
let mut index = 0_usize;
while index < bytes.len() {
if bytes[index] == b'$' && index + 1 < bytes.len() && bytes[index + 1] == b'{' {
let mut end = index + 2;
while end < bytes.len() && bytes[end] != b'}' {
end += 1;
}
if end < bytes.len() {
let expression = &raw[index + 2..end];
let name = match expression.split_once(":-") {
std::option::Option::Some((name, _)) => name,
std::option::Option::None => expression,
};
sensitivity = merge_sensitivity(sensitivity, classify_environment_variable(name));
index = end + 1;
continue;
}
}
index += 1;
}
return sensitivity;
}
/// Classifies a Khadhroony-owned environment variable by its namespace.
pub fn classify_environment_variable(
name: &str,
) -> std::option::Option<EnvironmentValueSensitivity> {
if name.starts_with("KS_SECRET_") || name.starts_with("KB_SECRET_") {
return std::option::Option::Some(EnvironmentValueSensitivity::Secret);
}
if name.starts_with("KS_PUBLIC_") || name.starts_with("KB_PUBLIC_") {
return std::option::Option::Some(EnvironmentValueSensitivity::Public);
}
if name.starts_with("KS_") || name.starts_with("KB_") {
return std::option::Option::Some(EnvironmentValueSensitivity::Internal);
}
return std::option::Option::None;
}
/// Produces the generic diagnostic representation allowed for one environment value.
pub fn diagnostic_environment_value(
name: &str,
value: &str,
) -> std::option::Option<std::string::String> {
return match classify_environment_variable(name) {
std::option::Option::Some(EnvironmentValueSensitivity::Secret) => {
std::option::Option::Some("<redacted>".to_string())
},
std::option::Option::Some(EnvironmentValueSensitivity::Internal) => {
std::option::Option::Some("<configured>".to_string())
},
std::option::Option::Some(EnvironmentValueSensitivity::Public) => {
std::option::Option::Some(value.to_string())
},
std::option::Option::None => std::option::Option::None,
};
}
fn merge_sensitivity(
left: std::option::Option<EnvironmentValueSensitivity>,
right: std::option::Option<EnvironmentValueSensitivity>,
) -> std::option::Option<EnvironmentValueSensitivity> {
if left == std::option::Option::Some(EnvironmentValueSensitivity::Secret)
|| right == std::option::Option::Some(EnvironmentValueSensitivity::Secret)
{
return std::option::Option::Some(EnvironmentValueSensitivity::Secret);
}
if left == std::option::Option::Some(EnvironmentValueSensitivity::Internal)
|| right == std::option::Option::Some(EnvironmentValueSensitivity::Internal)
{
return std::option::Option::Some(EnvironmentValueSensitivity::Internal);
}
if left == std::option::Option::Some(EnvironmentValueSensitivity::Public)
|| right == std::option::Option::Some(EnvironmentValueSensitivity::Public)
{
return std::option::Option::Some(EnvironmentValueSensitivity::Public);
}
return std::option::Option::None;
}
#[cfg(test)]
mod tests {
#[test]
fn composed_values_inherit_the_strongest_placeholder_sensitivity() {
assert_eq!(
super::classify_environment_template(
"https://rpc.invalid/?api-key=${KS_SECRET_HELIUS_API_KEY}&mint=${KS_PUBLIC_DEVNET_TOKEN_2022_MINT}"
),
std::option::Option::Some(super::EnvironmentValueSensitivity::Secret)
);
assert_eq!(
super::classify_environment_template(
"${KS_LOGS_DIRECTORY:-logs}/${KS_PUBLIC_SPL_TOKEN_PROGRAM_ID}"
),
std::option::Option::Some(super::EnvironmentValueSensitivity::Internal)
);
assert_eq!(
super::classify_environment_template("${KS_PUBLIC_SPL_TOKEN_PROGRAM_ID}"),
std::option::Option::Some(super::EnvironmentValueSensitivity::Public)
);
}
#[test]
fn generic_diagnostic_never_reveals_secret_or_internal_values() {
assert_eq!(
super::diagnostic_environment_value("KS_SECRET_HELIUS_API_KEY", "SECRET-CANARY"),
std::option::Option::Some("<redacted>".to_string())
);
assert_eq!(
super::diagnostic_environment_value("KS_LOGS_DIRECTORY", "/private/logs"),
std::option::Option::Some("<configured>".to_string())
);
assert_eq!(
super::diagnostic_environment_value("KS_PUBLIC_SPL_TOKEN_PROGRAM_ID", "Token111"),
std::option::Option::Some("Token111".to_string())
);
}
#[test]
fn namespaces_map_to_secret_internal_and_public() {
assert_eq!(
super::classify_environment_variable("KS_SECRET_HELIUS_API_KEY"),
std::option::Option::Some(super::EnvironmentValueSensitivity::Secret)
);
assert_eq!(
super::classify_environment_variable("KS_LOGS_DIRECTORY"),
std::option::Option::Some(super::EnvironmentValueSensitivity::Internal)
);
assert_eq!(
super::classify_environment_variable("KB_PUBLIC_DEMO_VALUE"),
std::option::Option::Some(super::EnvironmentValueSensitivity::Public)
);
assert_eq!(super::classify_environment_variable("HOME"), std::option::Option::None);
}
}

View File

@@ -1,16 +1,13 @@
// file: ks-config/src/settings.rs // file: ks-config/src/settings.rs
// version: 22 // version: 23
//! 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.
use ts_rs::TS; // rust-rules: derive-import
const CONFIG_JSON_SCHEMA: &str = const CONFIG_JSON_SCHEMA: &str =
include_str!("../../config/schemas/resolved.app.config.schema.json"); include_str!("../../config/schemas/resolved.app.config.schema.json");
/// Resolved runtime configuration containing every composed named profile. /// Resolved runtime configuration containing every composed named profile.
#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize, TS)] #[derive(Clone, serde::Deserialize, Eq, PartialEq)]
#[ts(export, export_to = "../frontend/ts/bindings/ks_config/settings/AppConfig.ts")]
pub struct AppConfig { pub struct AppConfig {
/// Active profile name. /// Active profile name.
pub active_profile: std::string::String, pub active_profile: std::string::String,
@@ -19,16 +16,10 @@ pub struct AppConfig {
} }
/// Resolved runtime profile selected by the root active profile name. /// Resolved runtime profile selected by the root active profile name.
#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize, TS)] #[derive(Clone, serde::Deserialize, Eq, PartialEq)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_config/settings/ProfileConfig.ts"
)]
pub struct ProfileConfig { pub struct ProfileConfig {
/// Profile code. /// Profile code.
pub name: std::string::String, pub name: std::string::String,
/// Application metadata.
pub app: AppSectionConfig,
/// Database configuration. /// Database configuration.
pub database: DatabaseConfig, pub database: DatabaseConfig,
/// Solana endpoint and listener configuration. /// Solana endpoint and listener configuration.
@@ -37,29 +28,10 @@ pub struct ProfileConfig {
pub wallet: WalletConfig, pub wallet: WalletConfig,
/// Execution safety configuration. /// Execution safety configuration.
pub execution: ExecutionConfig, pub execution: ExecutionConfig,
/// Demo application configuration.
pub demo: DemoConfig,
}
/// Application metadata for a profile.
#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize, TS)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_config/settings/AppSectionConfig.ts"
)]
pub struct AppSectionConfig {
/// Application name.
pub name: std::string::String,
/// Environment name.
pub environment: std::string::String,
} }
/// Database backend configuration. /// Database backend configuration.
#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize, TS)] #[derive(Clone, serde::Deserialize, Eq, PartialEq)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_config/settings/DatabaseConfig.ts"
)]
pub struct DatabaseConfig { pub struct DatabaseConfig {
/// Enables database storage. /// Enables database storage.
pub enabled: bool, pub enabled: bool,
@@ -72,33 +44,26 @@ pub struct DatabaseConfig {
} }
/// PostgreSQL backend configuration. /// PostgreSQL backend configuration.
#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize, TS)] #[derive(Clone, serde::Deserialize, Eq, PartialEq)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_config/settings/PostgresConfig.ts"
)]
pub struct PostgresConfig { pub struct PostgresConfig {
/// PostgreSQL URL or local placeholder. /// PostgreSQL URL or local placeholder.
pub url: std::string::String, pub url: std::string::String,
/// Maximum connection count. /// Maximum connection count.
pub max_connections: u32, pub max_connections: u32,
/// Connection timeout in milliseconds. /// Connection timeout in milliseconds.
#[ts(type = "number")]
pub connect_timeout_ms: u64, pub connect_timeout_ms: u64,
/// Enables schema initialization at startup. /// Enables schema initialization at startup.
pub auto_initialize_schema: bool, pub auto_initialize_schema: bool,
} }
/// SQLite backend configuration. /// SQLite backend configuration.
#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize, TS)] #[derive(Clone, serde::Deserialize, Eq, PartialEq)]
#[ts(export, export_to = "../frontend/ts/bindings/ks_config/settings/SqliteConfig.ts")]
pub struct SqliteConfig { pub struct SqliteConfig {
/// SQLite database path. /// SQLite database path.
pub path: std::string::String, pub path: std::string::String,
/// Creates the file when missing. /// Creates the file when missing.
pub create_if_missing: bool, pub create_if_missing: bool,
/// Busy timeout in milliseconds. /// Busy timeout in milliseconds.
#[ts(type = "number")]
pub busy_timeout_ms: u64, pub busy_timeout_ms: u64,
/// Maximum connection count. /// Maximum connection count.
pub max_connections: u32, pub max_connections: u32,
@@ -109,8 +74,7 @@ pub struct SqliteConfig {
} }
/// Solana endpoints and listener configuration. /// Solana endpoints and listener configuration.
#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize, TS)] #[derive(Clone, serde::Deserialize, Eq, PartialEq)]
#[ts(export, export_to = "../frontend/ts/bindings/ks_config/settings/SolanaConfig.ts")]
pub struct SolanaConfig { pub struct SolanaConfig {
/// HTTP JSON-RPC endpoints. /// HTTP JSON-RPC endpoints.
pub http_endpoints: std::vec::Vec<HttpEndpointConfig>, pub http_endpoints: std::vec::Vec<HttpEndpointConfig>,
@@ -121,11 +85,7 @@ pub struct SolanaConfig {
} }
/// HTTP JSON-RPC endpoint configuration. /// HTTP JSON-RPC endpoint configuration.
#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize, TS)] #[derive(Clone, serde::Deserialize, Eq, PartialEq)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_config/settings/HttpEndpointConfig.ts"
)]
pub struct HttpEndpointConfig { pub struct HttpEndpointConfig {
/// Endpoint name. /// Endpoint name.
pub name: std::string::String, pub name: std::string::String,
@@ -138,10 +98,8 @@ pub struct HttpEndpointConfig {
/// Full endpoint URL. /// Full endpoint URL.
pub url: std::string::String, pub url: std::string::String,
/// Connection timeout in milliseconds. /// Connection timeout in milliseconds.
#[ts(type = "number")]
pub connect_timeout_ms: u64, pub connect_timeout_ms: u64,
/// Request timeout in milliseconds. /// Request timeout in milliseconds.
#[ts(type = "number")]
pub request_timeout_ms: u64, pub request_timeout_ms: u64,
/// Maximum idle connections per host. /// Maximum idle connections per host.
pub max_idle_connections_per_host: u32, pub max_idle_connections_per_host: u32,
@@ -150,11 +108,7 @@ pub struct HttpEndpointConfig {
} }
/// Standard Solana WebSocket endpoint configuration. /// Standard Solana WebSocket endpoint configuration.
#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize, TS)] #[derive(Clone, serde::Deserialize, Eq, PartialEq)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_config/settings/WsEndpointConfig.ts"
)]
pub struct WsEndpointConfig { pub struct WsEndpointConfig {
/// Endpoint name. /// Endpoint name.
pub name: std::string::String, pub name: std::string::String,
@@ -167,13 +121,10 @@ pub struct WsEndpointConfig {
/// Full endpoint URL. /// Full endpoint URL.
pub url: std::string::String, pub url: std::string::String,
/// Connection timeout in milliseconds. /// Connection timeout in milliseconds.
#[ts(type = "number")]
pub connect_timeout_ms: u64, pub connect_timeout_ms: u64,
/// Request timeout in milliseconds. /// Request timeout in milliseconds.
#[ts(type = "number")]
pub request_timeout_ms: u64, pub request_timeout_ms: u64,
/// Unsubscribe timeout in milliseconds. /// Unsubscribe timeout in milliseconds.
#[ts(type = "number")]
pub unsubscribe_timeout_ms: u64, pub unsubscribe_timeout_ms: u64,
/// Writer channel capacity. /// Writer channel capacity.
pub write_channel_capacity: u32, pub write_channel_capacity: u32,
@@ -186,11 +137,7 @@ pub struct WsEndpointConfig {
} }
/// Role-specific endpoint limits. /// Role-specific endpoint limits.
#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize, TS)] #[derive(Clone, serde::Deserialize, Eq, PartialEq)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_config/settings/EndpointRoleConfig.ts"
)]
pub struct EndpointRoleConfig { pub struct EndpointRoleConfig {
/// Role code used by endpoint pools. /// Role code used by endpoint pools.
pub role: std::string::String, pub role: std::string::String,
@@ -209,16 +156,11 @@ pub struct EndpointRoleConfig {
/// Maximum subscriptions allowed for this role on this URL. /// Maximum subscriptions allowed for this role on this URL.
pub max_subscriptions: u32, pub max_subscriptions: u32,
/// Pause after a rate limit response in milliseconds. /// Pause after a rate limit response in milliseconds.
#[ts(type = "number")]
pub pause_after_rate_limit_ms: u64, pub pause_after_rate_limit_ms: u64,
} }
/// Listener configuration used by standard WebSocket subscriptions. /// Listener configuration used by standard WebSocket subscriptions.
#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize, TS)] #[derive(Clone, serde::Deserialize, Eq, PartialEq)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_config/settings/ListenerConfig.ts"
)]
pub struct ListenerConfig { pub struct ListenerConfig {
/// Enables listener creation. /// Enables listener creation.
pub enabled: bool, pub enabled: bool,
@@ -233,11 +175,7 @@ pub struct ListenerConfig {
} }
/// Log listener filtered by a program identifier mention. /// Log listener filtered by a program identifier mention.
#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize, TS)] #[derive(Clone, serde::Deserialize, Eq, PartialEq)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_config/settings/LogListenerConfig.ts"
)]
pub struct LogListenerConfig { pub struct LogListenerConfig {
/// Listener name. /// Listener name.
pub name: std::string::String, pub name: std::string::String,
@@ -252,11 +190,7 @@ pub struct LogListenerConfig {
} }
/// Program account listener. /// Program account listener.
#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize, TS)] #[derive(Clone, serde::Deserialize, Eq, PartialEq)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_config/settings/ProgramListenerConfig.ts"
)]
pub struct ProgramListenerConfig { pub struct ProgramListenerConfig {
/// Listener name. /// Listener name.
pub name: std::string::String, pub name: std::string::String,
@@ -271,11 +205,7 @@ pub struct ProgramListenerConfig {
} }
/// Account listener. /// Account listener.
#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize, TS)] #[derive(Clone, serde::Deserialize, Eq, PartialEq)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_config/settings/AccountListenerConfig.ts"
)]
pub struct AccountListenerConfig { pub struct AccountListenerConfig {
/// Listener name. /// Listener name.
pub name: std::string::String, pub name: std::string::String,
@@ -290,8 +220,7 @@ pub struct AccountListenerConfig {
} }
/// Wallet configuration. /// Wallet configuration.
#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize, TS)] #[derive(Clone, serde::Deserialize, Eq, PartialEq)]
#[ts(export, export_to = "../frontend/ts/bindings/ks_config/settings/WalletConfig.ts")]
pub struct WalletConfig { pub struct WalletConfig {
/// Wallet directory path. /// Wallet directory path.
pub wallet_dir: std::string::String, pub wallet_dir: std::string::String,
@@ -306,11 +235,7 @@ pub struct WalletConfig {
} }
/// Execution safety configuration. /// Execution safety configuration.
#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize, TS)] #[derive(Clone, serde::Deserialize, Eq, PartialEq)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_config/settings/ExecutionConfig.ts"
)]
pub struct ExecutionConfig { pub struct ExecutionConfig {
/// Enables local validator transaction sending. /// Enables local validator transaction sending.
pub localnet_send_enabled: bool, pub localnet_send_enabled: bool,
@@ -327,48 +252,29 @@ pub struct ExecutionConfig {
/// Requires explicit operator confirmation before send. /// Requires explicit operator confirmation before send.
pub require_operator_confirmation: bool, pub require_operator_confirmation: bool,
/// Maximum spend in lamports for local validator tests. /// Maximum spend in lamports for local validator tests.
#[ts(type = "number")]
pub localnet_max_spend_lamports: u64, pub localnet_max_spend_lamports: u64,
/// Maximum spend in lamports for devnet tests. /// Maximum spend in lamports for devnet tests.
#[ts(type = "number")]
pub devnet_max_spend_lamports: u64, pub devnet_max_spend_lamports: u64,
/// Maximum spend in lamports for testnet tests. /// Maximum spend in lamports for testnet tests.
#[ts(type = "number")]
pub testnet_max_spend_lamports: u64, pub testnet_max_spend_lamports: u64,
/// Maximum spend in lamports for mainnet operations. /// Maximum spend in lamports for mainnet operations.
#[ts(type = "number")]
pub mainnet_max_spend_lamports: u64, pub mainnet_max_spend_lamports: u64,
/// Maximum estimated transaction fee in lamports. /// Maximum estimated transaction fee in lamports.
#[ts(type = "number")]
pub max_fee_lamports: u64, pub max_fee_lamports: u64,
/// Maximum compute-unit price in micro-lamports. /// Maximum compute-unit price in micro-lamports.
#[ts(type = "number")]
pub max_compute_unit_price_micro_lamports: u64, pub max_compute_unit_price_micro_lamports: u64,
/// Maximum accepted age for a recent blockhash in slots. /// Maximum accepted age for a recent blockhash in slots.
#[ts(type = "number")]
pub recent_blockhash_max_age_slots: u64, pub recent_blockhash_max_age_slots: u64,
/// Maximum node retransmission retries requested by `sendTransaction`. /// Maximum node retransmission retries requested by `sendTransaction`.
pub send_max_retries: u32, pub send_max_retries: u32,
/// Delay between transaction confirmation polls. /// Delay between transaction confirmation polls.
#[ts(type = "number")]
pub confirmation_poll_interval_ms: u64, pub confirmation_poll_interval_ms: u64,
/// Maximum number of transaction confirmation polls. /// Maximum number of transaction confirmation polls.
pub confirmation_max_attempts: u32, pub confirmation_max_attempts: u32,
/// Maximum devnet faucet airdrop allowed for a temporary wallet. /// Maximum devnet faucet airdrop allowed for a temporary wallet.
#[ts(type = "number")]
pub devnet_airdrop_max_lamports: u64, pub devnet_airdrop_max_lamports: u64,
} }
/// Demo application configuration.
#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize, TS)]
#[ts(export, export_to = "../frontend/ts/bindings/ks_config/settings/DemoConfig.ts")]
pub struct DemoConfig {
/// Enables live demo pages.
pub live_demo_enabled: bool,
/// Enables trading demo pages.
pub trading_demo_enabled: bool,
}
/// Returns the embedded JSON Schema text used for resolved runtime contract validation. /// Returns the embedded JSON Schema text used for resolved runtime contract validation.
pub fn config_json_schema_text() -> &'static str { pub fn config_json_schema_text() -> &'static str {
return CONFIG_JSON_SCHEMA; return CONFIG_JSON_SCHEMA;
@@ -413,9 +319,9 @@ pub fn validate_config_json_schema(raw_json: &str) -> ks_core::Result<()> {
let validation_result = validator.validate(&instance); let validation_result = validator.validate(&instance);
return match validation_result { return match validation_result {
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(ks_core::Error::new( std::result::Result::Err(_) => std::result::Result::Err(ks_core::Error::new(
"config_json_schema_validation_failed", "config_json_schema_validation_failed",
error.to_string(), "resolved configuration does not satisfy its schema",
)), )),
}; };
} }
@@ -428,10 +334,10 @@ pub fn parse_config_json(raw_json: &str) -> ks_core::Result<AppConfig> {
} }
let config = match serde_json::from_str::<AppConfig>(raw_json) { let config = match serde_json::from_str::<AppConfig>(raw_json) {
std::result::Result::Ok(config) => config, std::result::Result::Ok(config) => config,
std::result::Result::Err(error) => { std::result::Result::Err(_) => {
return std::result::Result::Err(ks_core::Error::new( return std::result::Result::Err(ks_core::Error::new(
"config_json_decode_failed", "config_json_decode_failed",
error.to_string(), "resolved configuration could not be decoded",
)); ));
}, },
}; };
@@ -477,36 +383,6 @@ pub fn read_config_json_file_with_environment(
return parse_config_json(&resolved); return parse_config_json(&resolved);
} }
/// Serializes a configuration value to compact JSON.
pub fn serialize_config_json(config: &AppConfig) -> ks_core::Result<std::string::String> {
match validate_config(config) {
std::result::Result::Ok(()) => (),
std::result::Result::Err(error) => return std::result::Result::Err(error),
}
return match serde_json::to_string(config) {
std::result::Result::Ok(serialized) => std::result::Result::Ok(serialized),
std::result::Result::Err(error) => std::result::Result::Err(ks_core::Error::new(
"config_json_serialize_failed",
error.to_string(),
)),
};
}
/// Serializes a configuration value to pretty JSON.
pub fn serialize_config_json_pretty(config: &AppConfig) -> ks_core::Result<std::string::String> {
match validate_config(config) {
std::result::Result::Ok(()) => (),
std::result::Result::Err(error) => return std::result::Result::Err(error),
}
return match serde_json::to_string_pretty(config) {
std::result::Result::Ok(serialized) => std::result::Result::Ok(serialized),
std::result::Result::Err(error) => std::result::Result::Err(ks_core::Error::new(
"config_json_pretty_serialize_failed",
error.to_string(),
)),
};
}
/// Returns the active profile declared by the root configuration. /// Returns the active profile declared by the root configuration.
pub fn active_profile(config: &AppConfig) -> ks_core::Result<&ProfileConfig> { pub fn active_profile(config: &AppConfig) -> ks_core::Result<&ProfileConfig> {
for profile in &config.profiles { for profile in &config.profiles {
@@ -576,10 +452,6 @@ fn validate_root_config(config: &AppConfig) -> ks_core::Result<()> {
} }
fn validate_profile(profile: &ProfileConfig) -> ks_core::Result<()> { fn validate_profile(profile: &ProfileConfig) -> ks_core::Result<()> {
match validate_app_section(&profile.app) {
std::result::Result::Ok(()) => (),
std::result::Result::Err(error) => return std::result::Result::Err(error),
}
match validate_database(&profile.database) { match validate_database(&profile.database) {
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),
@@ -599,14 +471,6 @@ fn validate_profile(profile: &ProfileConfig) -> ks_core::Result<()> {
return validate_wallet_execution_pair(&profile.wallet, &profile.execution); return validate_wallet_execution_pair(&profile.wallet, &profile.execution);
} }
fn validate_app_section(config: &AppSectionConfig) -> ks_core::Result<()> {
match require_non_empty(&config.name, "app.name") {
std::result::Result::Ok(()) => (),
std::result::Result::Err(error) => return std::result::Result::Err(error),
}
return require_non_empty(&config.environment, "app.environment");
}
pub(crate) fn validate_database(config: &DatabaseConfig) -> ks_core::Result<()> { 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(
@@ -683,7 +547,7 @@ pub(crate) fn validate_http_endpoint(config: &HttpEndpointConfig) -> ks_core::Re
if !config.url.starts_with("http://") && !config.url.starts_with("https://") { if !config.url.starts_with("http://") && !config.url.starts_with("https://") {
return std::result::Result::Err(ks_core::Error::new( return std::result::Result::Err(ks_core::Error::new(
"http_endpoint_url_invalid", "http_endpoint_url_invalid",
config.url.clone(), config.name.clone(),
)); ));
} }
if config.connect_timeout_ms == 0 if config.connect_timeout_ms == 0
@@ -714,7 +578,7 @@ pub(crate) fn validate_ws_endpoint(config: &WsEndpointConfig) -> ks_core::Result
if !config.url.starts_with("ws://") && !config.url.starts_with("wss://") { if !config.url.starts_with("ws://") && !config.url.starts_with("wss://") {
return std::result::Result::Err(ks_core::Error::new( return std::result::Result::Err(ks_core::Error::new(
"ws_endpoint_url_invalid", "ws_endpoint_url_invalid",
config.url.clone(), config.name.clone(),
)); ));
} }
if config.connect_timeout_ms == 0 if config.connect_timeout_ms == 0
@@ -1006,24 +870,6 @@ pub(crate) fn require_non_empty(value: &str, field_name: &str) -> ks_core::Resul
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use ts_rs::TS; // rust-rules: derive-import
#[test]
fn exported_json_configuration_numbers_do_not_use_bigint() {
let config = ts_rs::Config::default();
let declarations = [
<crate::PostgresConfig as TS>::decl(&config),
<crate::SqliteConfig as TS>::decl(&config),
<crate::HttpEndpointConfig as TS>::decl(&config),
<crate::WsEndpointConfig as TS>::decl(&config),
<crate::EndpointRoleConfig as TS>::decl(&config),
<crate::ExecutionConfig as TS>::decl(&config),
];
for declaration in declarations {
assert!(!declaration.contains("bigint"));
}
}
const DEFAULT_CONFIG: &str = const DEFAULT_CONFIG: &str =
include_str!("../../test-fixtures/config/resolved.app.config.json"); include_str!("../../test-fixtures/config/resolved.app.config.json");
const EXAMPLE_CONFIG: &str = const EXAMPLE_CONFIG: &str =
@@ -1077,29 +923,6 @@ mod tests {
//assert_eq!(active.name, "local_devnet"); //assert_eq!(active.name, "local_devnet");
} }
#[test]
fn default_app_config_serializes_and_roundtrips() {
let config_result = super::parse_config_json(DEFAULT_CONFIG);
assert!(config_result.is_ok());
let config = match config_result {
std::result::Result::Ok(config) => config,
std::result::Result::Err(error) => panic!("default app config must parse: {error}"),
};
let serialized_result = super::serialize_config_json_pretty(&config);
assert!(serialized_result.is_ok());
let serialized = match serialized_result {
std::result::Result::Ok(serialized) => serialized,
std::result::Result::Err(error) => panic!("default app config must serialize: {error}"),
};
let reparsed_result = super::parse_config_json(&serialized);
assert!(reparsed_result.is_ok());
let reparsed = match reparsed_result {
std::result::Result::Ok(reparsed) => reparsed,
std::result::Result::Err(error) => panic!("serialized config must parse: {error}"),
};
assert_eq!(config, reparsed);
}
#[test] #[test]
fn parser_rejects_missing_active_profile() { fn parser_rejects_missing_active_profile() {
let mut value = parse_default_value(); let mut value = parse_default_value();

View File

@@ -1,12 +1,12 @@
// file: ks-config/src/store.rs // file: ks-config/src/store.rs
// version: 1 // version: 2
//! Independent store configuration document loading and validation. //! Independent store configuration document loading and validation.
const STORE_JSON_SCHEMA: &str = include_str!("../../config/schemas/store.config.schema.json"); const STORE_JSON_SCHEMA: &str = include_str!("../../config/schemas/store.config.schema.json");
/// Root store document with one default profile and named alternatives. /// Root store document with one default profile and named alternatives.
#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize)] #[derive(Clone, serde::Deserialize, Eq, PartialEq)]
pub struct StoreConfigDocument { pub struct StoreConfigDocument {
/// Default store profile used when a composition does not override it. /// Default store profile used when a composition does not override it.
pub default_profile: std::string::String, pub default_profile: std::string::String,
@@ -15,7 +15,7 @@ pub struct StoreConfigDocument {
} }
/// Named store profile. /// Named store profile.
#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize)] #[derive(Clone, serde::Deserialize, Eq, PartialEq)]
pub struct StoreProfileConfig { pub struct StoreProfileConfig {
/// Profile code. /// Profile code.
pub name: std::string::String, pub name: std::string::String,
@@ -66,9 +66,9 @@ pub fn validate_store_json_schema(raw_json: &str) -> ks_core::Result<()> {
}; };
return match validator.validate(&instance) { return match validator.validate(&instance) {
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(ks_core::Error::new( std::result::Result::Err(_) => std::result::Result::Err(ks_core::Error::new(
"store_config_schema_validation_failed", "store_config_schema_validation_failed",
error.to_string(), "store configuration does not satisfy its schema",
)), )),
}; };
} }
@@ -81,10 +81,10 @@ pub fn parse_store_json(raw_json: &str) -> ks_core::Result<StoreConfigDocument>
} }
let document = match serde_json::from_str::<StoreConfigDocument>(raw_json) { let document = match serde_json::from_str::<StoreConfigDocument>(raw_json) {
std::result::Result::Ok(value) => value, std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => { std::result::Result::Err(_) => {
return std::result::Result::Err(ks_core::Error::new( return std::result::Result::Err(ks_core::Error::new(
"store_config_json_decode_failed", "store_config_json_decode_failed",
error.to_string(), "store configuration could not be decoded",
)); ));
}, },
}; };
@@ -176,7 +176,7 @@ pub fn validate_store_document(document: &StoreConfigDocument) -> ks_core::Resul
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
const DEFAULT_STORE: &str = include_str!("../../config/store.config.json"); const DEFAULT_STORE: &str = include_str!("../../config/store.config.json");
const EXAMPLE_STORE: &str = include_str!("../../config/example.store.config.json"); const EXAMPLE_STORE: &str = include_str!("../../config/exemples/example.store.config.json");
#[test] #[test]
fn default_and_example_store_configs_validate() { fn default_and_example_store_configs_validate() {

View File

@@ -1,5 +1,5 @@
// file: ks-config/src/transport.rs // file: ks-config/src/transport.rs
// version: 2 // version: 3
//! Independent Solana transport configuration document loading and validation. //! Independent Solana transport configuration document loading and validation.
@@ -7,7 +7,7 @@ const TRANSPORT_JSON_SCHEMA: &str =
include_str!("../../config/schemas/transport.config.schema.json"); include_str!("../../config/schemas/transport.config.schema.json");
/// Root transport document containing shared endpoint defaults and named profiles. /// Root transport document containing shared endpoint defaults and named profiles.
#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize)] #[derive(Clone, serde::Deserialize, Eq, PartialEq)]
pub struct TransportConfigDocument { pub struct TransportConfigDocument {
/// Default transport profile used when a composition does not override it. /// Default transport profile used when a composition does not override it.
pub default_profile: std::string::String, pub default_profile: std::string::String,
@@ -18,7 +18,7 @@ pub struct TransportConfigDocument {
} }
/// Default values shared by one class of WebSocket endpoints. /// Default values shared by one class of WebSocket endpoints.
#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize)] #[derive(Clone, serde::Deserialize, Eq, PartialEq)]
pub struct WsEndpointDefaultsConfig { pub struct WsEndpointDefaultsConfig {
/// Defaults code referenced by endpoints. /// Defaults code referenced by endpoints.
pub name: std::string::String, pub name: std::string::String,
@@ -37,7 +37,7 @@ pub struct WsEndpointDefaultsConfig {
} }
/// Endpoint-specific overrides applied on top of named WebSocket defaults. /// Endpoint-specific overrides applied on top of named WebSocket defaults.
#[derive(Clone, Debug, Default, serde::Deserialize, Eq, PartialEq, serde::Serialize)] #[derive(Clone, Default, serde::Deserialize, Eq, PartialEq)]
pub struct WsEndpointOverridesConfig { pub struct WsEndpointOverridesConfig {
/// Optional connection timeout override. /// Optional connection timeout override.
pub connect_timeout_ms: std::option::Option<u64>, pub connect_timeout_ms: std::option::Option<u64>,
@@ -54,7 +54,7 @@ pub struct WsEndpointOverridesConfig {
} }
/// Source shape of one WebSocket endpoint before defaults are resolved. /// Source shape of one WebSocket endpoint before defaults are resolved.
#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize)] #[derive(Clone, serde::Deserialize, Eq, PartialEq)]
pub struct WsEndpointSourceConfig { pub struct WsEndpointSourceConfig {
/// Endpoint name. /// Endpoint name.
pub name: std::string::String, pub name: std::string::String,
@@ -76,7 +76,7 @@ pub struct WsEndpointSourceConfig {
} }
/// Named Solana HTTP and WebSocket endpoint source profile. /// Named Solana HTTP and WebSocket endpoint source profile.
#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize)] #[derive(Clone, serde::Deserialize, Eq, PartialEq)]
pub struct TransportProfileSourceConfig { pub struct TransportProfileSourceConfig {
/// Profile code. /// Profile code.
pub name: std::string::String, pub name: std::string::String,
@@ -87,7 +87,7 @@ pub struct TransportProfileSourceConfig {
} }
/// Fully resolved transport profile consumed by transport clients. /// Fully resolved transport profile consumed by transport clients.
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Clone, Eq, PartialEq)]
pub struct TransportProfileConfig { pub struct TransportProfileConfig {
/// Profile code. /// Profile code.
pub name: std::string::String, pub name: std::string::String,
@@ -140,9 +140,9 @@ pub fn validate_transport_json_schema(raw_json: &str) -> ks_core::Result<()> {
}; };
return match validator.validate(&instance) { return match validator.validate(&instance) {
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(ks_core::Error::new( std::result::Result::Err(_) => std::result::Result::Err(ks_core::Error::new(
"transport_config_schema_validation_failed", "transport_config_schema_validation_failed",
error.to_string(), "transport configuration does not satisfy its schema",
)), )),
}; };
} }
@@ -155,10 +155,10 @@ pub fn parse_transport_json(raw_json: &str) -> ks_core::Result<TransportConfigDo
} }
let document = match serde_json::from_str::<TransportConfigDocument>(raw_json) { let document = match serde_json::from_str::<TransportConfigDocument>(raw_json) {
std::result::Result::Ok(value) => value, std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => { std::result::Result::Err(_) => {
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",
error.to_string(), "transport configuration could not be decoded",
)); ));
}, },
}; };
@@ -393,7 +393,8 @@ pub fn validate_transport_document(document: &TransportConfigDocument) -> ks_cor
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
const DEFAULT_TRANSPORT: &str = include_str!("../../config/transport.config.json"); const DEFAULT_TRANSPORT: &str = include_str!("../../config/transport.config.json");
const EXAMPLE_TRANSPORT: &str = include_str!("../../config/example.transport.config.json"); const EXAMPLE_TRANSPORT: &str =
include_str!("../../config/exemples/example.transport.config.json");
#[test] #[test]
fn default_and_example_transport_configs_validate() { fn default_and_example_transport_configs_validate() {

View File

@@ -1,12 +1,12 @@
// file: ks-config/src/wallet.rs // file: ks-config/src/wallet.rs
// version: 1 // version: 3
//! Independent wallet configuration document loading and validation. //! Independent wallet configuration document loading and validation.
const WALLET_JSON_SCHEMA: &str = include_str!("../../config/schemas/wallet.config.schema.json"); const WALLET_JSON_SCHEMA: &str = include_str!("../../config/schemas/wallet.config.schema.json");
/// Root wallet document with global storage and named runtime profiles. /// Root wallet document with global storage and named runtime profiles.
#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize)] #[derive(Clone, serde::Deserialize, Eq, PartialEq)]
pub struct WalletConfigDocument { pub struct WalletConfigDocument {
/// Root directory for wallet storage, independent from profile selection. /// Root directory for wallet storage, independent from profile selection.
pub wallets_directory: std::string::String, pub wallets_directory: std::string::String,
@@ -17,7 +17,7 @@ pub struct WalletConfigDocument {
} }
/// Named wallet profile whose directory is relative to the global wallet root unless absolute. /// Named wallet profile whose directory is relative to the global wallet root unless absolute.
#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize)] #[derive(Clone, serde::Deserialize, Eq, PartialEq)]
pub struct WalletProfileConfig { pub struct WalletProfileConfig {
/// Profile code. /// Profile code.
pub name: std::string::String, pub name: std::string::String,
@@ -67,18 +67,18 @@ pub fn parse_wallet_json(raw_json: &str) -> ks_core::Result<WalletConfigDocument
)); ));
}, },
}; };
if let std::result::Result::Err(error) = validator.validate(&instance) { if validator.validate(&instance).is_err() {
return std::result::Result::Err(ks_core::Error::new( return std::result::Result::Err(ks_core::Error::new(
"wallet_config_schema_validation_failed", "wallet_config_schema_validation_failed",
error.to_string(), "wallet configuration does not satisfy its schema",
)); ));
} }
let document = match serde_json::from_str::<WalletConfigDocument>(raw_json) { let document = match serde_json::from_str::<WalletConfigDocument>(raw_json) {
std::result::Result::Ok(value) => value, std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => { std::result::Result::Err(_) => {
return std::result::Result::Err(ks_core::Error::new( return std::result::Result::Err(ks_core::Error::new(
"wallet_config_json_decode_failed", "wallet_config_json_decode_failed",
error.to_string(), "wallet configuration could not be decoded",
)); ));
}, },
}; };
@@ -206,7 +206,7 @@ pub fn validate_wallet_document(document: &WalletConfigDocument) -> ks_core::Res
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
const DEFAULT_WALLET: &str = include_str!("../../config/wallet.config.json"); const DEFAULT_WALLET: &str = include_str!("../../config/wallet.config.json");
const EXAMPLE_WALLET: &str = include_str!("../../config/example.wallet.config.json"); const EXAMPLE_WALLET: &str = include_str!("../../config/exemples/example.wallet.config.json");
#[test] #[test]
fn default_and_example_wallet_configs_validate() { fn default_and_example_wallet_configs_validate() {

View File

@@ -6,36 +6,37 @@
#[test] #[test]
fn external_consumer_resolves_composition_through_crate_root_exports() { fn external_consumer_resolves_composition_through_crate_root_exports() {
let composition = match ks_config::parse_composition_json(include_str!( let composition = match ks_config::parse_composition_json(include_str!(
"../../config/example.kb-app-demo-desktop.default.config.json" "../../config/exemples/example.kb-app-demo-desktop.default.config.json"
)) { )) {
std::result::Result::Ok(value) => value, std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("composition must parse: {error}"), std::result::Result::Err(error) => panic!("composition must parse: {error}"),
}; };
let transport = match ks_config::parse_transport_json(include_str!( let transport = match ks_config::parse_transport_json(include_str!(
"../../config/example.transport.config.json" "../../config/exemples/example.transport.config.json"
)) { )) {
std::result::Result::Ok(value) => value, std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("transport must parse: {error}"), std::result::Result::Err(error) => panic!("transport must parse: {error}"),
}; };
let listeners = match ks_config::parse_listeners_json(include_str!( let listeners = match ks_config::parse_listeners_json(include_str!(
"../../config/example.listeners.config.json" "../../config/exemples/example.listeners.config.json"
)) { )) {
std::result::Result::Ok(value) => value, std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("listeners must parse: {error}"), std::result::Result::Err(error) => panic!("listeners must parse: {error}"),
}; };
let store = let store = match ks_config::parse_store_json(include_str!(
match ks_config::parse_store_json(include_str!("../../config/example.store.config.json")) { "../../config/exemples/example.store.config.json"
std::result::Result::Ok(value) => value, )) {
std::result::Result::Err(error) => panic!("store must parse: {error}"), 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")) let wallet = match ks_config::parse_wallet_json(include_str!(
{ "../../config/exemples/example.wallet.config.json"
std::result::Result::Ok(value) => value, )) {
std::result::Result::Err(error) => panic!("wallet must parse: {error}"), 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!( let execution = match ks_config::parse_execution_json(include_str!(
"../../config/example.execution.config.json" "../../config/exemples/example.execution.config.json"
)) { )) {
std::result::Result::Ok(value) => value, std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => panic!("execution must parse: {error}"), std::result::Result::Err(error) => panic!("execution must parse: {error}"),

View File

@@ -0,0 +1,18 @@
// file: ks-config/tests/external_sensitivity_api.rs
// version: 1
//! External-contract coverage for configuration sensitivity helpers.
#[test]
fn external_consumer_classifies_composed_values_and_redacts_secrets() {
let sensitivity = ks_config::classify_environment_template(
"https://provider.invalid/?api-key=${KS_SECRET_HELIUS_API_KEY}",
);
assert_eq!(
sensitivity,
std::option::Option::Some(ks_config::EnvironmentValueSensitivity::Secret)
);
let diagnostic =
ks_config::diagnostic_environment_value("KS_SECRET_HELIUS_API_KEY", "SECRET-CANARY");
assert_eq!(diagnostic, std::option::Option::Some("<redacted>".to_string()));
}

View File

@@ -1,8 +1,13 @@
<!-- file: ks-lib/CHANGELOG.md --> <!-- file: ks-lib/CHANGELOG.md -->
<!-- version: 52 --> <!-- version: 53 -->
# CHANGELOG — ks-lib # CHANGELOG — ks-lib
## `0.5.1-pre.008`
- retire la dépendance `ts-rs`, les dérivations/attributs TS-RS et les bindings TypeScript générés historiques de `ks-lib` ;
- conserve inchangés les contrats Rust de décodage, matérialisation, exécution et modèles : toute exposition TypeScript appartient désormais à un DTO/wrapper de l'application consommatrice.
## `0.5.1-pre.003` ## `0.5.1-pre.003`
- migre les 255 identités runtime/persistables effectivement présentes de `kb-lib.decoder.*`, `kb-lib.executor.*` et `kb-lib.materializer.*` vers `ks-lib-decoder.*`, `ks-lib-executor.*` et `ks-lib-materializer.*` ; - migre les 255 identités runtime/persistables effectivement présentes de `kb-lib.decoder.*`, `kb-lib.executor.*` et `kb-lib.materializer.*` vers `ks-lib-decoder.*`, `ks-lib-executor.*` et `ks-lib-materializer.*` ;

View File

@@ -1,5 +1,5 @@
# file: ks-lib/Cargo.toml # file: ks-lib/Cargo.toml
# version: 16 # version: 17
[package] [package]
name = "ks-lib" name = "ks-lib"
@@ -47,7 +47,6 @@ spl-token-confidential-transfer-proof-extraction.workspace = true
spl-token-interface.workspace = true spl-token-interface.workspace = true
spl-memo-interface.workspace = true spl-memo-interface.workspace = true
tracing.workspace = true tracing.workspace = true
ts-rs.workspace = true
wincode.workspace = true wincode.workspace = true
[dev-dependencies] [dev-dependencies]

View File

@@ -1,5 +1,5 @@
<!-- file: ks-lib/README.md --> <!-- file: ks-lib/README.md -->
<!-- version: 30 --> <!-- version: 31 -->
# ks-lib # ks-lib
@@ -65,6 +65,8 @@ Linventaire audité comprend 25 composants nommés : onze matérialisateurs a
`ks-lib` ne sélectionne aucun endpoint, ne lit pas directement un RPC, ne persiste aucune donnée, ne gère pas les secrets de wallet et ne soumet aucune transaction. `ks-lib` ne sélectionne aucun endpoint, ne lit pas directement un RPC, ne persiste aucune donnée, ne gère pas les secrets de wallet et ne soumet aucune transaction.
Depuis `0.5.1-pre.008`, `ks-lib` ne dépend plus de TS-RS et ne génère plus de bindings TypeScript. Ses contrats Rust restent généralistes ; une application Tauri qui doit exposer une partie de ces contrats définit un DTO/wrapper applicatif explicite.
## Surface publique principale ## Surface publique principale
- contrats `DcApi*` et décodeurs concrets `Dc*Decoder` ; - contrats `DcApi*` et décodeurs concrets `Dc*Decoder` ;

View File

@@ -1,5 +1,5 @@
<!-- file: ks-lib/USAGE.md --> <!-- file: ks-lib/USAGE.md -->
<!-- version: 17 --> <!-- version: 18 -->
# Utilisation de ks-lib # Utilisation de ks-lib
@@ -16,6 +16,8 @@ ks-lib = { path = "../ks-lib" }
Toutes les fonctions retournant `ks_core::Result<T>` utilisent le contrat derreur structuré du workspace. Toutes les fonctions retournant `ks_core::Result<T>` utilisent le contrat derreur structuré du workspace.
`ks-lib` n'est pas une frontière TypeScript : depuis `0.5.1-pre.008`, la crate ne dépend plus de TS-RS et ne produit plus de bindings. Les applications exposent uniquement leurs wrappers/DTO propres.
## Décodage contextualisé ## Décodage contextualisé
`DcApiInstructionDecoder` définit la reconnaissance et le décodage dune instruction contextualisée. Les décodeurs concrets sont des valeurs sans état. `DcApiInstructionDecoder` définit la reconnaissance et le décodage dune instruction contextualisée. Les décodeurs concrets sont des valeurs sans état.

View File

@@ -1,17 +1,11 @@
// file: ks-lib/src/executor/api/execution.rs // file: ks-lib/src/executor/api/execution.rs
// version: 10 // version: 11
//! Typed execution plans, policies and result contracts. //! Typed execution plans, policies and result contracts.
use ts_rs::TS; // rust-rules: derive-import
/// Exact capability returned for one program operation. /// Exact capability returned for one program operation.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[serde(tag = "status", rename_all = "snake_case")] #[serde(tag = "status", rename_all = "snake_case")]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/api/execution/ExecutionCapability.ts"
)]
pub enum ExApiExecutionCapability { pub enum ExApiExecutionCapability {
/// The operation is implemented by the executor. /// The operation is implemented by the executor.
Supported { Supported {
@@ -51,12 +45,8 @@ impl crate::ExApiExecutionCapability {
} }
/// Solana cluster expected by an execution plan. /// Solana cluster expected by an execution plan.
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Serialize, TS)] #[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Serialize)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/api/execution/ExecutionCluster.ts"
)]
pub enum ExApiExecutionCluster { pub enum ExApiExecutionCluster {
/// Local validator cluster. /// Local validator cluster.
Localnet, Localnet,
@@ -101,11 +91,7 @@ impl<'de> serde::Deserialize<'de> for crate::ExApiExecutionCluster {
} }
/// Cluster restrictions attached to an execution plan. /// Cluster restrictions attached to an execution plan.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/api/execution/ExecutionClusterPolicy.ts"
)]
pub struct ExApiExecutionClusterPolicy { pub struct ExApiExecutionClusterPolicy {
/// Cluster that the RPC endpoint must report. /// Cluster that the RPC endpoint must report.
pub expected_cluster: crate::ExApiExecutionCluster, pub expected_cluster: crate::ExApiExecutionCluster,
@@ -126,12 +112,8 @@ impl std::default::Default for crate::ExApiExecutionClusterPolicy {
} }
/// Simulation requirement attached to an execution plan. /// Simulation requirement attached to an execution plan.
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/api/execution/ExecutionSimulationPolicy.ts"
)]
pub enum ExApiExecutionSimulationPolicy { pub enum ExApiExecutionSimulationPolicy {
/// Simulation must succeed before signing or sending. /// Simulation must succeed before signing or sending.
Required, Required,
@@ -140,12 +122,8 @@ pub enum ExApiExecutionSimulationPolicy {
} }
/// Blockhash source required when a transaction is assembled. /// Blockhash source required when a transaction is assembled.
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/api/execution/ExecutionBlockhashKind.ts"
)]
pub enum ExApiExecutionBlockhashKind { pub enum ExApiExecutionBlockhashKind {
/// Fetch a recent blockhash and enforce a bounded age. /// Fetch a recent blockhash and enforce a bounded age.
Latest, Latest,
@@ -154,11 +132,7 @@ pub enum ExApiExecutionBlockhashKind {
} }
/// Blockhash or durable nonce policy attached to an execution plan. /// Blockhash or durable nonce policy attached to an execution plan.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/api/execution/ExecutionBlockhashPolicy.ts"
)]
pub struct ExApiExecutionBlockhashPolicy { pub struct ExApiExecutionBlockhashPolicy {
/// Selected blockhash source. /// Selected blockhash source.
pub kind: crate::ExApiExecutionBlockhashKind, pub kind: crate::ExApiExecutionBlockhashKind,
@@ -182,11 +156,7 @@ impl std::default::Default for crate::ExApiExecutionBlockhashPolicy {
} }
/// Explicit cost ceilings attached to an execution plan. /// Explicit cost ceilings attached to an execution plan.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/api/execution/ExecutionCostLimit.ts"
)]
pub struct ExApiExecutionCostLimit { pub struct ExApiExecutionCostLimit {
/// Maximum lamports that operation instructions may transfer or lock. /// Maximum lamports that operation instructions may transfer or lock.
pub max_spend_lamports: std::option::Option<u64>, pub max_spend_lamports: std::option::Option<u64>,
@@ -207,11 +177,7 @@ impl std::default::Default for crate::ExApiExecutionCostLimit {
} }
/// Post-execution replay validation policy. /// Post-execution replay validation policy.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/api/execution/PostExecutionValidationPolicy.ts"
)]
pub struct ExApiPostExecutionValidationPolicy { pub struct ExApiPostExecutionValidationPolicy {
/// Whether the sent signature must be inserted into canonical storage. /// Whether the sent signature must be inserted into canonical storage.
pub canonical_insert_required: bool, pub canonical_insert_required: bool,
@@ -235,11 +201,7 @@ impl std::default::Default for crate::ExApiPostExecutionValidationPolicy {
} }
/// Conservative policy carried from intent creation to transaction assembly. /// Conservative policy carried from intent creation to transaction assembly.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/api/execution/ExecutionPolicy.ts"
)]
pub struct ExApiExecutionPolicy { pub struct ExApiExecutionPolicy {
/// Cluster restrictions. /// Cluster restrictions.
pub cluster: crate::ExApiExecutionClusterPolicy, pub cluster: crate::ExApiExecutionClusterPolicy,
@@ -272,11 +234,7 @@ impl std::default::Default for crate::ExApiExecutionPolicy {
} }
/// One account required by a planned instruction. /// One account required by a planned instruction.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/api/execution/PlannedAccount.ts"
)]
pub struct ExApiPlannedAccount { pub struct ExApiPlannedAccount {
/// Account public key. /// Account public key.
pub pubkey: crate::MdPubkey, pub pubkey: crate::MdPubkey,
@@ -287,11 +245,7 @@ pub struct ExApiPlannedAccount {
} }
/// One fully encoded instruction in an execution plan. /// One fully encoded instruction in an execution plan.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/api/execution/PlannedInstruction.ts"
)]
pub struct ExApiPlannedInstruction { pub struct ExApiPlannedInstruction {
/// Target program id. /// Target program id.
pub program_id: crate::MdProgramId, pub program_id: crate::MdProgramId,
@@ -304,11 +258,7 @@ pub struct ExApiPlannedInstruction {
} }
/// Signer required before transaction assembly. /// Signer required before transaction assembly.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/api/execution/RequiredSigner.ts"
)]
pub struct ExApiRequiredSigner { pub struct ExApiRequiredSigner {
/// Signer public key. /// Signer public key.
pub pubkey: crate::MdPubkey, pub pubkey: crate::MdPubkey,
@@ -317,11 +267,7 @@ pub struct ExApiRequiredSigner {
} }
/// Typed execution plan produced before transaction assembly. /// Typed execution plan produced before transaction assembly.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/api/execution/PreparedExecutionPlan.ts"
)]
pub struct ExApiPreparedExecutionPlan { pub struct ExApiPreparedExecutionPlan {
/// Stable executor crate name. /// Stable executor crate name.
pub executor_name: std::string::String, pub executor_name: std::string::String,
@@ -346,11 +292,7 @@ pub struct ExApiPreparedExecutionPlan {
} }
/// Result returned by a transaction simulation adapter. /// Result returned by a transaction simulation adapter.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/api/execution/ExecutionSimulationResult.ts"
)]
pub struct ExApiExecutionSimulationResult { pub struct ExApiExecutionSimulationResult {
/// Whether an RPC simulation call was actually performed. /// Whether an RPC simulation call was actually performed.
pub simulated: bool, pub simulated: bool,
@@ -386,11 +328,7 @@ pub struct ExApiExecutionSimulationResult {
} }
/// Result returned after an adapter submits a signed transaction. /// Result returned after an adapter submits a signed transaction.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/api/execution/ExecutionSendResult.ts"
)]
pub struct ExApiExecutionSendResult { pub struct ExApiExecutionSendResult {
/// Cluster used for submission. /// Cluster used for submission.
pub cluster: crate::ExApiExecutionCluster, pub cluster: crate::ExApiExecutionCluster,
@@ -401,12 +339,8 @@ pub struct ExApiExecutionSendResult {
} }
/// Confirmation state returned by an execution confirmation adapter. /// Confirmation state returned by an execution confirmation adapter.
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/api/execution/ExecutionConfirmationStatus.ts"
)]
pub enum ExApiExecutionConfirmationStatus { pub enum ExApiExecutionConfirmationStatus {
/// The transaction was observed at processed commitment. /// The transaction was observed at processed commitment.
Processed, Processed,
@@ -423,11 +357,7 @@ pub enum ExApiExecutionConfirmationStatus {
} }
/// Result returned after waiting for transaction confirmation. /// Result returned after waiting for transaction confirmation.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/api/execution/ExecutionConfirmationResult.ts"
)]
pub struct ExApiExecutionConfirmationResult { pub struct ExApiExecutionConfirmationResult {
/// Cluster queried for confirmation. /// Cluster queried for confirmation.
pub cluster: crate::ExApiExecutionCluster, pub cluster: crate::ExApiExecutionCluster,
@@ -446,11 +376,7 @@ pub struct ExApiExecutionConfirmationResult {
} }
/// Diagnostic produced by post-execution canonical replay validation. /// Diagnostic produced by post-execution canonical replay validation.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/api/execution/PostExecutionDiagnostic.ts"
)]
pub struct ExApiPostExecutionDiagnostic { pub struct ExApiPostExecutionDiagnostic {
/// Executed transaction signature. /// Executed transaction signature.
pub signature: crate::MdSignature, pub signature: crate::MdSignature,

View File

@@ -1,16 +1,10 @@
// file: ks-lib/src/executor/api/executor.rs // file: ks-lib/src/executor/api/executor.rs
// version: 5 // version: 6
//! Shared execution contract and neutral request models. //! Shared execution contract and neutral request models.
use ts_rs::TS; // rust-rules: derive-import
/// Support level returned by an executor for a requested operation. /// Support level returned by an executor for a requested operation.
#[derive(Clone, Copy, Debug, Eq, PartialEq, TS)] #[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/api/executor/ExecutionSupport.ts"
)]
pub enum ExApiExecutionSupport { pub enum ExApiExecutionSupport {
/// The executor cannot handle the request. /// The executor cannot handle the request.
No, No,
@@ -21,11 +15,7 @@ pub enum ExApiExecutionSupport {
} }
/// Generic request used before protocol-specific execution models are introduced. /// Generic request used before protocol-specific execution models are introduced.
#[derive(Clone, Debug, Eq, PartialEq, TS)] #[derive(Clone, Debug, Eq, PartialEq)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/api/executor/ExecutionRequest.ts"
)]
pub struct ExApiExecutionRequest { pub struct ExApiExecutionRequest {
/// Target program id. /// Target program id.
pub program_id: crate::MdProgramId, pub program_id: crate::MdProgramId,
@@ -36,11 +26,7 @@ pub struct ExApiExecutionRequest {
} }
/// Neutral execution plan placeholder produced by executor crates. /// Neutral execution plan placeholder produced by executor crates.
#[derive(Clone, Debug, Eq, PartialEq, TS)] #[derive(Clone, Debug, Eq, PartialEq)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/api/executor/ExecutionPlan.ts"
)]
pub struct ExApiExecutionPlan { pub struct ExApiExecutionPlan {
/// Stable executor crate name. /// Stable executor crate name.
pub executor_name: std::string::String, pub executor_name: std::string::String,

View File

@@ -1,10 +1,8 @@
// file: ks-lib/src/executor/metadata/metaplex_token_metadata/intent.rs // file: ks-lib/src/executor/metadata/metaplex_token_metadata/intent.rs
// version: 11 // version: 12
//! Typed current and deprecated Metaplex Token Metadata execution intents. //! Typed current and deprecated Metaplex Token Metadata execution intents.
use ts_rs::TS; // rust-rules: derive-import
/// Stable operation code for the current Create wrapper, discriminator 42. /// Stable operation code for the current Create wrapper, discriminator 42.
pub const EX_METAPLEX_TOKEN_METADATA_CREATE_OPERATION: &str = pub const EX_METAPLEX_TOKEN_METADATA_CREATE_OPERATION: &str =
"metadata.metaplex_token_metadata.create"; "metadata.metaplex_token_metadata.create";
@@ -164,12 +162,8 @@ pub const EX_METAPLEX_TOKEN_METADATA_SUPPORTED_OPERATION_CODES: &[&str] = &[
]; ];
/// One current or explicitly deprecated Metaplex Token Metadata operation. /// One current or explicitly deprecated Metaplex Token Metadata operation.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[serde(tag = "operation", rename_all = "snake_case")] #[serde(tag = "operation", rename_all = "snake_case")]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/metadata/metaplex_token_metadata/intent/ExMetaplexTokenMetadataOperation.ts"
)]
pub enum ExMetaplexTokenMetadataOperation { pub enum ExMetaplexTokenMetadataOperation {
/// Build the current Create wrapper with `CreateArgs::V1`. /// Build the current Create wrapper with `CreateArgs::V1`.
Create { Create {
@@ -190,7 +184,6 @@ pub enum ExMetaplexTokenMetadataOperation {
/// Optional SPL Token or Token-2022 program account. /// Optional SPL Token or Token-2022 program account.
spl_token_program: std::option::Option<crate::MdProgramId>, spl_token_program: std::option::Option<crate::MdProgramId>,
/// Official Metaplex create arguments. /// Official Metaplex create arguments.
#[ts(type = "unknown")]
create_args: mpl_token_metadata::types::CreateArgs, create_args: mpl_token_metadata::types::CreateArgs,
}, },
/// Build the current Update wrapper as the update authority. /// Build the current Update wrapper as the update authority.
@@ -210,7 +203,6 @@ pub enum ExMetaplexTokenMetadataOperation {
/// Optional Token Authorization Rules account. /// Optional Token Authorization Rules account.
authorization_rules: std::option::Option<crate::MdPubkey>, authorization_rules: std::option::Option<crate::MdPubkey>,
/// Official Metaplex update arguments. Only `AsUpdateAuthorityV2` is accepted in this prerelease. /// Official Metaplex update arguments. Only `AsUpdateAuthorityV2` is accepted in this prerelease.
#[ts(type = "unknown")]
update_args: mpl_token_metadata::types::UpdateArgs, update_args: mpl_token_metadata::types::UpdateArgs,
}, },
/// Build the obsolete PuffMetadata instruction retained for compatibility workflows. /// Build the obsolete PuffMetadata instruction retained for compatibility workflows.
@@ -252,7 +244,6 @@ pub enum ExMetaplexTokenMetadataOperation {
/// Optional canonical collection master edition PDA. /// Optional canonical collection master edition PDA.
collection_master_edition: std::option::Option<crate::MdPubkey>, collection_master_edition: std::option::Option<crate::MdPubkey>,
/// Official unified verification variant. /// Official unified verification variant.
#[ts(type = "unknown")]
verification_args: mpl_token_metadata::types::VerificationArgs, verification_args: mpl_token_metadata::types::VerificationArgs,
}, },
/// Build the current unified Unverify wrapper for a creator or collection. /// Build the current unified Unverify wrapper for a creator or collection.
@@ -268,7 +259,6 @@ pub enum ExMetaplexTokenMetadataOperation {
/// Optional canonical collection metadata PDA. /// Optional canonical collection metadata PDA.
collection_metadata: std::option::Option<crate::MdPubkey>, collection_metadata: std::option::Option<crate::MdPubkey>,
/// Official unified verification variant. /// Official unified verification variant.
#[ts(type = "unknown")]
verification_args: mpl_token_metadata::types::VerificationArgs, verification_args: mpl_token_metadata::types::VerificationArgs,
}, },
/// Build obsolete SetCollectionSize retained for compatibility workflows. /// Build obsolete SetCollectionSize retained for compatibility workflows.
@@ -302,7 +292,6 @@ pub enum ExMetaplexTokenMetadataOperation {
master_edition_token: std::option::Option<crate::MdPubkey>, master_edition_token: std::option::Option<crate::MdPubkey>,
edition_marker: std::option::Option<crate::MdPubkey>, edition_marker: std::option::Option<crate::MdPubkey>,
token_record: std::option::Option<crate::MdPubkey>, token_record: std::option::Option<crate::MdPubkey>,
#[ts(type = "unknown")]
burn_args: mpl_token_metadata::types::BurnArgs, burn_args: mpl_token_metadata::types::BurnArgs,
}, },
#[allow(missing_docs)] #[allow(missing_docs)]
@@ -319,7 +308,6 @@ pub enum ExMetaplexTokenMetadataOperation {
spl_token_program: std::option::Option<crate::MdProgramId>, spl_token_program: std::option::Option<crate::MdProgramId>,
authorization_rules_program: std::option::Option<crate::MdProgramId>, authorization_rules_program: std::option::Option<crate::MdProgramId>,
authorization_rules: std::option::Option<crate::MdPubkey>, authorization_rules: std::option::Option<crate::MdPubkey>,
#[ts(type = "unknown")]
delegate_args: mpl_token_metadata::types::DelegateArgs, delegate_args: mpl_token_metadata::types::DelegateArgs,
}, },
#[allow(missing_docs)] #[allow(missing_docs)]
@@ -336,7 +324,6 @@ pub enum ExMetaplexTokenMetadataOperation {
spl_token_program: std::option::Option<crate::MdProgramId>, spl_token_program: std::option::Option<crate::MdProgramId>,
authorization_rules_program: std::option::Option<crate::MdProgramId>, authorization_rules_program: std::option::Option<crate::MdProgramId>,
authorization_rules: std::option::Option<crate::MdPubkey>, authorization_rules: std::option::Option<crate::MdPubkey>,
#[ts(type = "unknown")]
revoke_args: mpl_token_metadata::types::RevokeArgs, revoke_args: mpl_token_metadata::types::RevokeArgs,
}, },
#[allow(missing_docs)] #[allow(missing_docs)]
@@ -352,7 +339,6 @@ pub enum ExMetaplexTokenMetadataOperation {
spl_token_program: std::option::Option<crate::MdProgramId>, spl_token_program: std::option::Option<crate::MdProgramId>,
authorization_rules_program: std::option::Option<crate::MdProgramId>, authorization_rules_program: std::option::Option<crate::MdProgramId>,
authorization_rules: std::option::Option<crate::MdPubkey>, authorization_rules: std::option::Option<crate::MdPubkey>,
#[ts(type = "unknown")]
lock_args: mpl_token_metadata::types::LockArgs, lock_args: mpl_token_metadata::types::LockArgs,
}, },
#[allow(missing_docs)] #[allow(missing_docs)]
@@ -368,7 +354,6 @@ pub enum ExMetaplexTokenMetadataOperation {
spl_token_program: std::option::Option<crate::MdProgramId>, spl_token_program: std::option::Option<crate::MdProgramId>,
authorization_rules_program: std::option::Option<crate::MdProgramId>, authorization_rules_program: std::option::Option<crate::MdProgramId>,
authorization_rules: std::option::Option<crate::MdPubkey>, authorization_rules: std::option::Option<crate::MdPubkey>,
#[ts(type = "unknown")]
unlock_args: mpl_token_metadata::types::UnlockArgs, unlock_args: mpl_token_metadata::types::UnlockArgs,
}, },
#[allow(missing_docs)] #[allow(missing_docs)]
@@ -386,7 +371,6 @@ pub enum ExMetaplexTokenMetadataOperation {
authority: crate::MdPubkey, authority: crate::MdPubkey,
authorization_rules_program: std::option::Option<crate::MdProgramId>, authorization_rules_program: std::option::Option<crate::MdProgramId>,
authorization_rules: std::option::Option<crate::MdPubkey>, authorization_rules: std::option::Option<crate::MdPubkey>,
#[ts(type = "unknown")]
transfer_args: mpl_token_metadata::types::TransferArgs, transfer_args: mpl_token_metadata::types::TransferArgs,
}, },
#[allow(missing_docs)] #[allow(missing_docs)]
@@ -619,7 +603,6 @@ pub enum ExMetaplexTokenMetadataOperation {
/// Account `rent` from the official IDL contract. /// Account `rent` from the official IDL contract.
rent: crate::MdPubkey, rent: crate::MdPubkey,
/// Official Metaplex instruction arguments. /// Official Metaplex instruction arguments.
#[ts(type = "unknown")]
args: mpl_token_metadata::types::MintNewEditionFromMasterEditionViaTokenArgs, args: mpl_token_metadata::types::MintNewEditionFromMasterEditionViaTokenArgs,
}, },
/// Build deprecated Utilize, retained for explicit compatibility workflows. /// Build deprecated Utilize, retained for explicit compatibility workflows.
@@ -651,7 +634,6 @@ pub enum ExMetaplexTokenMetadataOperation {
/// Account `burner` from the official IDL contract. /// Account `burner` from the official IDL contract.
burner: crate::MdPubkey, burner: crate::MdPubkey,
/// Official Metaplex instruction arguments. /// Official Metaplex instruction arguments.
#[ts(type = "unknown")]
args: mpl_token_metadata::instructions::UtilizeInstructionArgs, args: mpl_token_metadata::instructions::UtilizeInstructionArgs,
}, },
/// Build deprecated ApproveUseAuthority, retained for explicit compatibility workflows. /// Build deprecated ApproveUseAuthority, retained for explicit compatibility workflows.
@@ -683,7 +665,6 @@ pub enum ExMetaplexTokenMetadataOperation {
/// Account `rent` from the official IDL contract. /// Account `rent` from the official IDL contract.
rent: crate::MdPubkey, rent: crate::MdPubkey,
/// Official Metaplex instruction arguments. /// Official Metaplex instruction arguments.
#[ts(type = "unknown")]
args: mpl_token_metadata::instructions::ApproveUseAuthorityInstructionArgs, args: mpl_token_metadata::instructions::ApproveUseAuthorityInstructionArgs,
}, },
/// Build deprecated RevokeUseAuthority, retained for explicit compatibility workflows. /// Build deprecated RevokeUseAuthority, retained for explicit compatibility workflows.
@@ -780,7 +761,6 @@ pub enum ExMetaplexTokenMetadataOperation {
/// Optional authority from the current SDK contract. /// Optional authority from the current SDK contract.
authority: std::option::Option<crate::MdPubkey>, authority: std::option::Option<crate::MdPubkey>,
/// Official Metaplex instruction arguments. /// Official Metaplex instruction arguments.
#[ts(type = "unknown")]
args: mpl_token_metadata::instructions::TransferOutOfEscrowInstructionArgs, args: mpl_token_metadata::instructions::TransferOutOfEscrowInstructionArgs,
}, },
/// Build the current Mint instruction. /// Build the current Mint instruction.
@@ -816,7 +796,6 @@ pub enum ExMetaplexTokenMetadataOperation {
/// Optional Token Authorization Rules account in the current SDK contract. /// Optional Token Authorization Rules account in the current SDK contract.
authorization_rules: std::option::Option<crate::MdPubkey>, authorization_rules: std::option::Option<crate::MdPubkey>,
/// Official Metaplex instruction arguments. /// Official Metaplex instruction arguments.
#[ts(type = "unknown")]
args: mpl_token_metadata::types::MintArgs, args: mpl_token_metadata::types::MintArgs,
}, },
/// Build the current Migrate instruction. /// Build the current Migrate instruction.
@@ -879,7 +858,6 @@ pub enum ExMetaplexTokenMetadataOperation {
/// Optional Token Authorization Rules account in the current SDK contract. /// Optional Token Authorization Rules account in the current SDK contract.
authorization_rules: std::option::Option<crate::MdPubkey>, authorization_rules: std::option::Option<crate::MdPubkey>,
/// Official Metaplex instruction arguments. /// Official Metaplex instruction arguments.
#[ts(type = "unknown")]
args: mpl_token_metadata::types::UseArgs, args: mpl_token_metadata::types::UseArgs,
}, },
/// Build the current Collect instruction. /// Build the current Collect instruction.
@@ -928,7 +906,6 @@ pub enum ExMetaplexTokenMetadataOperation {
/// Account `systemProgram` from the official IDL contract. /// Account `systemProgram` from the official IDL contract.
system_program: crate::MdPubkey, system_program: crate::MdPubkey,
/// Official Metaplex instruction arguments. /// Official Metaplex instruction arguments.
#[ts(type = "unknown")]
args: mpl_token_metadata::types::PrintArgs, args: mpl_token_metadata::types::PrintArgs,
}, },
/// Build the current Resize instruction. /// Build the current Resize instruction.
@@ -1027,11 +1004,7 @@ impl crate::ExMetaplexTokenMetadataOperation {
} }
/// Complete typed intent accepted by the Metaplex Token Metadata executor. /// Complete typed intent accepted by the Metaplex Token Metadata executor.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/metadata/metaplex_token_metadata/intent/ExMetaplexTokenMetadataExecutionIntent.ts"
)]
pub struct ExMetaplexTokenMetadataExecutionIntent { pub struct ExMetaplexTokenMetadataExecutionIntent {
/// Stable caller-provided identifier used for logs and replay correlation. /// Stable caller-provided identifier used for logs and replay correlation.
pub intent_id: std::string::String, pub intent_id: std::string::String,

View File

@@ -1,18 +1,12 @@
// file: ks-lib/src/executor/metadata/solana_program_metadata/intent.rs // file: ks-lib/src/executor/metadata/solana_program_metadata/intent.rs
// version: 2 // version: 3
//! Typed intents for the Solana Program Metadata executor. //! Typed intents for the Solana Program Metadata executor.
use ts_rs::TS; // rust-rules: derive-import
/// Encoding written to a Solana Program Metadata account. /// Encoding written to a Solana Program Metadata account.
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[repr(u8)] #[repr(u8)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/metadata/solana_program_metadata/intent/ExMetadataSpmEncoding.ts"
)]
pub enum ExMetadataSpmEncoding { pub enum ExMetadataSpmEncoding {
/// No encoding declaration. /// No encoding declaration.
None = 0, None = 0,
@@ -32,13 +26,9 @@ impl crate::ExMetadataSpmEncoding {
} }
/// Compression written to a Solana Program Metadata account. /// Compression written to a Solana Program Metadata account.
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[repr(u8)] #[repr(u8)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/metadata/solana_program_metadata/intent/ExMetadataSpmCompression.ts"
)]
pub enum ExMetadataSpmCompression { pub enum ExMetadataSpmCompression {
/// No compression declaration. /// No compression declaration.
None = 0, None = 0,
@@ -56,13 +46,9 @@ impl crate::ExMetadataSpmCompression {
} }
/// Structured format written to a Solana Program Metadata account. /// Structured format written to a Solana Program Metadata account.
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[repr(u8)] #[repr(u8)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/metadata/solana_program_metadata/intent/ExMetadataSpmFormat.ts"
)]
pub enum ExMetadataSpmFormat { pub enum ExMetadataSpmFormat {
/// No structured format declaration. /// No structured format declaration.
None = 0, None = 0,
@@ -82,13 +68,9 @@ impl crate::ExMetadataSpmFormat {
} }
/// On-chain data source written to a Solana Program Metadata account. /// On-chain data source written to a Solana Program Metadata account.
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[repr(u8)] #[repr(u8)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/metadata/solana_program_metadata/intent/ExMetadataSpmDataSource.ts"
)]
pub enum ExMetadataSpmDataSource { pub enum ExMetadataSpmDataSource {
/// Direct on-chain bytes. /// Direct on-chain bytes.
Direct = 0, Direct = 0,
@@ -106,12 +88,8 @@ impl crate::ExMetadataSpmDataSource {
} }
/// Exact inline content accepted by `Initialize` and `SetData`. /// Exact inline content accepted by `Initialize` and `SetData`.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[serde(tag = "source", rename_all = "snake_case")] #[serde(tag = "source", rename_all = "snake_case")]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/metadata/solana_program_metadata/intent/ExMetadataSpmDataInput.ts"
)]
pub enum ExMetadataSpmDataInput { pub enum ExMetadataSpmDataInput {
/// Direct on-chain bytes. /// Direct on-chain bytes.
Direct { Direct {
@@ -148,12 +126,8 @@ impl crate::ExMetadataSpmDataInput {
} }
/// Source used by the `Write` instruction. /// Source used by the `Write` instruction.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[serde(tag = "source", rename_all = "snake_case")] #[serde(tag = "source", rename_all = "snake_case")]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/metadata/solana_program_metadata/intent/ExMetadataSpmWriteSource.ts"
)]
pub enum ExMetadataSpmWriteSource { pub enum ExMetadataSpmWriteSource {
/// Inline bytes placed after the offset argument. /// Inline bytes placed after the offset argument.
Inline { Inline {
@@ -168,12 +142,8 @@ pub enum ExMetadataSpmWriteSource {
} }
/// Source used by the `SetData` instruction. /// Source used by the `SetData` instruction.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[serde(tag = "source", rename_all = "snake_case")] #[serde(tag = "source", rename_all = "snake_case")]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/metadata/solana_program_metadata/intent/ExMetadataSpmSetDataSource.ts"
)]
pub enum ExMetadataSpmSetDataSource { pub enum ExMetadataSpmSetDataSource {
/// Runtime-supported three-byte variant that preserves current data bytes. /// Runtime-supported three-byte variant that preserves current data bytes.
PreserveExisting, PreserveExisting,
@@ -192,11 +162,7 @@ pub enum ExMetadataSpmSetDataSource {
} }
/// Optional program-upgrade-authority validation context. /// Optional program-upgrade-authority validation context.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/metadata/solana_program_metadata/intent/ExMetadataSpmProgramContext.ts"
)]
pub struct ExMetadataSpmProgramContext { pub struct ExMetadataSpmProgramContext {
/// Executable program account. /// Executable program account.
pub program: crate::MdPubkey, pub program: crate::MdPubkey,
@@ -205,12 +171,8 @@ pub struct ExMetadataSpmProgramContext {
} }
/// One exact Solana Program Metadata operation. /// One exact Solana Program Metadata operation.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[serde(tag = "operation", rename_all = "snake_case")] #[serde(tag = "operation", rename_all = "snake_case")]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/metadata/solana_program_metadata/intent/ExMetadataSpmOperation.ts"
)]
pub enum ExMetadataSpmOperation { pub enum ExMetadataSpmOperation {
/// Write bytes to a Buffer account. /// Write bytes to a Buffer account.
Write { Write {
@@ -452,11 +414,7 @@ impl crate::ExMetadataSpmOperation {
} }
/// Complete Solana Program Metadata execution intent. /// Complete Solana Program Metadata execution intent.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/metadata/solana_program_metadata/intent/ExMetadataSpmExecutionIntent.ts"
)]
pub struct ExMetadataSpmExecutionIntent { pub struct ExMetadataSpmExecutionIntent {
/// Caller-provided correlation identifier. /// Caller-provided correlation identifier.
pub intent_id: std::string::String, pub intent_id: std::string::String,

View File

@@ -1,17 +1,11 @@
// file: ks-lib/src/executor/safety/evaluation.rs // file: ks-lib/src/executor/safety/evaluation.rs
// version: 4 // version: 5
//! Safety checks applied before simulation, signing or sending. //! Safety checks applied before simulation, signing or sending.
use ts_rs::TS; // rust-rules: derive-import
/// Safety decision returned before continuing an execution stage. /// Safety decision returned before continuing an execution stage.
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/safety/evaluation/ExSafetyDecision.ts"
)]
pub enum ExSafetyDecision { pub enum ExSafetyDecision {
/// The plan is not allowed to continue. /// The plan is not allowed to continue.
Deny, Deny,
@@ -22,11 +16,7 @@ pub enum ExSafetyDecision {
} }
/// One stable safety policy violation. /// One stable safety policy violation.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/safety/evaluation/ExSafetyViolation.ts"
)]
pub struct ExSafetyViolation { pub struct ExSafetyViolation {
/// Stable violation code. /// Stable violation code.
pub code: std::string::String, pub code: std::string::String,
@@ -35,11 +25,7 @@ pub struct ExSafetyViolation {
} }
/// Complete safety evaluation for one execution stage. /// Complete safety evaluation for one execution stage.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/safety/evaluation/ExSafetyEvaluation.ts"
)]
pub struct ExSafetyEvaluation { pub struct ExSafetyEvaluation {
/// Aggregate decision. /// Aggregate decision.
pub decision: crate::ExSafetyDecision, pub decision: crate::ExSafetyDecision,

View File

@@ -1,10 +1,8 @@
// file: ks-lib/src/executor/solana/core/intent.rs // file: ks-lib/src/executor/solana/core/intent.rs
// version: 3 // version: 4
//! Typed Solana core execution intents. //! Typed Solana core execution intents.
use ts_rs::TS; // rust-rules: derive-import
/// Stable operation code for a System Program lamport transfer. /// Stable operation code for a System Program lamport transfer.
pub const EX_SOLANA_CORE_SYSTEM_TRANSFER_OPERATION: &str = "solana.core.system.transfer"; pub const EX_SOLANA_CORE_SYSTEM_TRANSFER_OPERATION: &str = "solana.core.system.transfer";
/// Stable operation code for a bounded batch of System Program transfers. /// Stable operation code for a bounded batch of System Program transfers.
@@ -412,11 +410,7 @@ pub const EX_SOLANA_CORE_OPERATION_CODES: [&str; 109] = [
]; ];
/// One destination in a System Program multi-transfer. /// One destination in a System Program multi-transfer.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/solana/core/intent/ExSolanaCoreSystemTransferRecipient.ts"
)]
pub struct ExSolanaCoreSystemTransferRecipient { pub struct ExSolanaCoreSystemTransferRecipient {
/// Recipient account. /// Recipient account.
pub to: crate::MdPubkey, pub to: crate::MdPubkey,
@@ -425,11 +419,7 @@ pub struct ExSolanaCoreSystemTransferRecipient {
} }
/// One Ed25519 signature verification offset record. /// One Ed25519 signature verification offset record.
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/solana/core/intent/ExSolanaCoreEd25519VerificationOffsets.ts"
)]
pub struct ExSolanaCoreEd25519VerificationOffsets { pub struct ExSolanaCoreEd25519VerificationOffsets {
/// Offset to the 64-byte signature. /// Offset to the 64-byte signature.
pub signature_offset: u16, pub signature_offset: u16,
@@ -448,11 +438,7 @@ pub struct ExSolanaCoreEd25519VerificationOffsets {
} }
/// One secp256k1 signature verification offset record. /// One secp256k1 signature verification offset record.
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/solana/core/intent/ExSolanaCoreSecp256k1VerificationOffsets.ts"
)]
pub struct ExSolanaCoreSecp256k1VerificationOffsets { pub struct ExSolanaCoreSecp256k1VerificationOffsets {
/// Offset to the 64-byte signature followed by its one-byte recovery id. /// Offset to the 64-byte signature followed by its one-byte recovery id.
pub signature_offset: u16, pub signature_offset: u16,
@@ -471,11 +457,7 @@ pub struct ExSolanaCoreSecp256k1VerificationOffsets {
} }
/// One secp256r1 signature verification offset record. /// One secp256r1 signature verification offset record.
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/solana/core/intent/ExSolanaCoreSecp256r1VerificationOffsets.ts"
)]
pub struct ExSolanaCoreSecp256r1VerificationOffsets { pub struct ExSolanaCoreSecp256r1VerificationOffsets {
/// Offset to the compact 64-byte signature. /// Offset to the compact 64-byte signature.
pub signature_offset: u16, pub signature_offset: u16,
@@ -494,12 +476,8 @@ pub struct ExSolanaCoreSecp256r1VerificationOffsets {
} }
/// Stake authority role changed by an authorize instruction. /// Stake authority role changed by an authorize instruction.
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/solana/core/intent/ExSolanaCoreStakeAuthorizationKind.ts"
)]
pub enum ExSolanaCoreStakeAuthorizationKind { pub enum ExSolanaCoreStakeAuthorizationKind {
/// Staker authority controlling delegation and stake movement. /// Staker authority controlling delegation and stake movement.
Staker, Staker,
@@ -517,12 +495,8 @@ impl crate::ExSolanaCoreStakeAuthorizationKind {
} }
/// Vote authority role changed by an authorize instruction. /// Vote authority role changed by an authorize instruction.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[serde(tag = "kind", rename_all = "snake_case")] #[serde(tag = "kind", rename_all = "snake_case")]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/solana/core/intent/ExSolanaCoreVoteAuthorizationKind.ts"
)]
pub enum ExSolanaCoreVoteAuthorizationKind { pub enum ExSolanaCoreVoteAuthorizationKind {
/// Vote authority using an Ed25519 public key. /// Vote authority using an Ed25519 public key.
Voter, Voter,
@@ -538,12 +512,8 @@ pub enum ExSolanaCoreVoteAuthorizationKind {
} }
/// Vote v2 commission category. /// Vote v2 commission category.
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/solana/core/intent/ExSolanaCoreVoteCommissionKind.ts"
)]
pub enum ExSolanaCoreVoteCommissionKind { pub enum ExSolanaCoreVoteCommissionKind {
/// Inflation-reward commission. /// Inflation-reward commission.
InflationRewards, InflationRewards,
@@ -563,11 +533,7 @@ impl crate::ExSolanaCoreVoteCommissionKind {
} }
/// One lockout entry used by Vote-state and tower synchronization instructions. /// One lockout entry used by Vote-state and tower synchronization instructions.
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/solana/core/intent/ExSolanaCoreVoteLockout.ts"
)]
pub struct ExSolanaCoreVoteLockout { pub struct ExSolanaCoreVoteLockout {
/// Voted slot. /// Voted slot.
pub slot: u64, pub slot: u64,
@@ -576,11 +542,7 @@ pub struct ExSolanaCoreVoteLockout {
} }
/// One key entry serialized in the Config Program `ConfigKeys` short vector. /// One key entry serialized in the Config Program `ConfigKeys` short vector.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/solana/core/intent/ExSolanaCoreConfigKey.ts"
)]
pub struct ExSolanaCoreConfigKey { pub struct ExSolanaCoreConfigKey {
/// Public key stored in the Config Program key list. /// Public key stored in the Config Program key list.
pub pubkey: crate::MdPubkey, pub pubkey: crate::MdPubkey,
@@ -589,11 +551,7 @@ pub struct ExSolanaCoreConfigKey {
} }
/// Optional context-state accounts attached to a ZK ElGamal proof verification. /// Optional context-state accounts attached to a ZK ElGamal proof verification.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/solana/core/intent/ExSolanaCoreZkElGamalContextState.ts"
)]
pub struct ExSolanaCoreZkElGamalContextState { pub struct ExSolanaCoreZkElGamalContextState {
/// Preallocated writable context-state account. /// Preallocated writable context-state account.
pub account: crate::MdPubkey, pub account: crate::MdPubkey,
@@ -602,12 +560,8 @@ pub struct ExSolanaCoreZkElGamalContextState {
} }
/// Official proof kinds accepted by the native ZK ElGamal Proof program. /// Official proof kinds accepted by the native ZK ElGamal Proof program.
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/solana/core/intent/ExSolanaCoreZkElGamalProofType.ts"
)]
pub enum ExSolanaCoreZkElGamalProofType { pub enum ExSolanaCoreZkElGamalProofType {
/// Zero-ciphertext proof. /// Zero-ciphertext proof.
ZeroCiphertext, ZeroCiphertext,
@@ -732,12 +686,8 @@ impl crate::ExSolanaCoreZkElGamalProofType {
} }
/// Typed Solana core operation arguments. /// Typed Solana core operation arguments.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[serde(tag = "operation", rename_all = "snake_case")] #[serde(tag = "operation", rename_all = "snake_case")]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/solana/core/intent/ExSolanaCoreOperation.ts"
)]
pub enum ExSolanaCoreOperation { pub enum ExSolanaCoreOperation {
/// Transfer lamports between System Program accounts. /// Transfer lamports between System Program accounts.
SystemTransfer { SystemTransfer {
@@ -2449,11 +2399,7 @@ impl crate::ExSolanaCoreOperation {
} }
/// Complete typed intent accepted by the Solana core executor. /// Complete typed intent accepted by the Solana core executor.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/solana/core/intent/ExSolanaCoreExecutionIntent.ts"
)]
pub struct ExSolanaCoreExecutionIntent { pub struct ExSolanaCoreExecutionIntent {
/// Stable caller-provided identifier used for logs and replay correlation. /// Stable caller-provided identifier used for logs and replay correlation.
pub intent_id: std::string::String, pub intent_id: std::string::String,

View File

@@ -1,10 +1,8 @@
// file: ks-lib/src/executor/spl/associated_token_account/intent.rs // file: ks-lib/src/executor/spl/associated_token_account/intent.rs
// version: 6 // version: 7
//! Typed SPL Associated Token Account execution intents. //! Typed SPL Associated Token Account execution intents.
use ts_rs::TS; // rust-rules: derive-import
/// Stable operation code for strict ATA creation. /// Stable operation code for strict ATA creation.
pub const EX_SPL_ATA_CREATE_OPERATION: &str = "spl.associated_token_account.create"; pub const EX_SPL_ATA_CREATE_OPERATION: &str = "spl.associated_token_account.create";
/// Stable operation code for idempotent ATA creation or reuse. /// Stable operation code for idempotent ATA creation or reuse.
@@ -20,18 +18,13 @@ pub const EX_SPL_ATA_SUPPORTED_OPERATION_CODES: &[&str] = &[
]; ];
/// Exact Token Program targeted by ATA derivation and runtime CPIs. /// Exact Token Program targeted by ATA derivation and runtime CPIs.
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/spl/associated_token_account/intent/ExSplAssociatedTokenProgram.ts"
)]
pub enum ExSplAssociatedTokenProgram { pub enum ExSplAssociatedTokenProgram {
/// Classic SPL Token program. /// Classic SPL Token program.
Classic, Classic,
/// Token-2022 program without extension-specific executor semantics. /// Token-2022 program without extension-specific executor semantics.
#[serde(rename = "token_2022")] #[serde(rename = "token_2022")]
#[ts(rename = "token_2022")]
Token2022, Token2022,
} }
@@ -46,12 +39,8 @@ impl crate::ExSplAssociatedTokenProgram {
} }
/// One officially constructible ATA operation. /// One officially constructible ATA operation.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[serde(tag = "operation", rename_all = "snake_case")] #[serde(tag = "operation", rename_all = "snake_case")]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/spl/associated_token_account/intent/ExSplAssociatedTokenAccountOperation.ts"
)]
pub enum ExSplAssociatedTokenAccountOperation { pub enum ExSplAssociatedTokenAccountOperation {
/// Strictly create an absent canonical ATA. /// Strictly create an absent canonical ATA.
Create { Create {
@@ -109,11 +98,7 @@ impl crate::ExSplAssociatedTokenAccountOperation {
} }
/// Complete typed intent accepted by the ATA executor. /// Complete typed intent accepted by the ATA executor.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/spl/associated_token_account/intent/ExSplAssociatedTokenAccountExecutionIntent.ts"
)]
pub struct ExSplAssociatedTokenAccountExecutionIntent { pub struct ExSplAssociatedTokenAccountExecutionIntent {
/// Stable caller-provided identifier used for logs and replay correlation. /// Stable caller-provided identifier used for logs and replay correlation.
pub intent_id: std::string::String, pub intent_id: std::string::String,

View File

@@ -1,22 +1,16 @@
// file: ks-lib/src/executor/spl/elgamal_registry/intent.rs // file: ks-lib/src/executor/spl/elgamal_registry/intent.rs
// version: 4 // version: 5
//! Typed intents for the SPL ElGamal public-key registry. //! Typed intents for the SPL ElGamal public-key registry.
use ts_rs::TS; // rust-rules: derive-import
/// Stable operation code for registry creation. /// Stable operation code for registry creation.
pub const EX_SPL_ELGAMAL_REGISTRY_CREATE_OPERATION: &str = "spl.elgamal_registry.create_registry"; pub const EX_SPL_ELGAMAL_REGISTRY_CREATE_OPERATION: &str = "spl.elgamal_registry.create_registry";
/// Stable operation code for registry update. /// Stable operation code for registry update.
pub const EX_SPL_ELGAMAL_REGISTRY_UPDATE_OPERATION: &str = "spl.elgamal_registry.update_registry"; pub const EX_SPL_ELGAMAL_REGISTRY_UPDATE_OPERATION: &str = "spl.elgamal_registry.update_registry";
/// Exact location of the public-key validity proof. /// Exact location of the public-key validity proof.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[serde(tag = "location", rename_all = "snake_case")] #[serde(tag = "location", rename_all = "snake_case")]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/spl/elgamal_registry/intent/ExSplElgamalRegistryProofLocation.ts"
)]
pub enum ExSplElgamalRegistryProofLocation { pub enum ExSplElgamalRegistryProofLocation {
/// A pre-verified proof context-state account. /// A pre-verified proof context-state account.
ContextStateAccount { ContextStateAccount {
@@ -31,12 +25,8 @@ pub enum ExSplElgamalRegistryProofLocation {
} }
/// Typed registry operation. /// Typed registry operation.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[serde(tag = "operation", rename_all = "snake_case")] #[serde(tag = "operation", rename_all = "snake_case")]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/spl/elgamal_registry/intent/ExSplElgamalRegistryOperation.ts"
)]
pub enum ExSplElgamalRegistryOperation { pub enum ExSplElgamalRegistryOperation {
/// Create the deterministic registry PDA for one owner. /// Create the deterministic registry PDA for one owner.
CreateRegistry { CreateRegistry {
@@ -69,11 +59,7 @@ impl crate::ExSplElgamalRegistryOperation {
} }
/// Complete typed execution intent. /// Complete typed execution intent.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/spl/elgamal_registry/intent/ExSplElgamalRegistryExecutionIntent.ts"
)]
pub struct ExSplElgamalRegistryExecutionIntent { pub struct ExSplElgamalRegistryExecutionIntent {
/// Caller-provided correlation identifier. /// Caller-provided correlation identifier.
pub intent_id: std::string::String, pub intent_id: std::string::String,

View File

@@ -1,10 +1,8 @@
// file: ks-lib/src/executor/spl/memo/intent.rs // file: ks-lib/src/executor/spl/memo/intent.rs
// version: 3 // version: 4
//! Typed SPL Memo execution intents. //! Typed SPL Memo execution intents.
use ts_rs::TS; // rust-rules: derive-import
/// Stable operation code for adding one SPL Memo annotation. /// Stable operation code for adding one SPL Memo annotation.
pub const EX_SPL_MEMO_ADD_MEMO_OPERATION: &str = "spl.memo.add_memo"; pub const EX_SPL_MEMO_ADD_MEMO_OPERATION: &str = "spl.memo.add_memo";
/// Conservative UTF-8 payload bound used before transaction assembly. /// Conservative UTF-8 payload bound used before transaction assembly.
@@ -13,12 +11,8 @@ pub const EX_SPL_MEMO_MAX_MESSAGE_BYTES: usize = 566;
pub const EX_SPL_MEMO_MAX_SIGNERS: usize = 32; pub const EX_SPL_MEMO_MAX_SIGNERS: usize = 32;
/// Exact SPL Memo program generation. /// Exact SPL Memo program generation.
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/spl/memo/intent/ExSplMemoGeneration.ts"
)]
pub enum ExSplMemoGeneration { pub enum ExSplMemoGeneration {
/// Historical v1 program, whose runtime ignores supplied accounts. /// Historical v1 program, whose runtime ignores supplied accounts.
V1, V1,
@@ -49,23 +43,15 @@ impl crate::ExSplMemoGeneration {
} }
/// One ordered signer account supplied to the Memo instruction. /// One ordered signer account supplied to the Memo instruction.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/spl/memo/intent/ExSplMemoSigner.ts"
)]
pub struct ExSplMemoSigner { pub struct ExSplMemoSigner {
/// Signer public key; duplicates are preserved in instruction order. /// Signer public key; duplicates are preserved in instruction order.
pub pubkey: crate::MdPubkey, pub pubkey: crate::MdPubkey,
} }
/// Typed SPL Memo operation arguments. /// Typed SPL Memo operation arguments.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[serde(tag = "operation", rename_all = "snake_case")] #[serde(tag = "operation", rename_all = "snake_case")]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/spl/memo/intent/ExSplMemoOperation.ts"
)]
pub enum ExSplMemoOperation { pub enum ExSplMemoOperation {
/// Add one exact UTF-8 Memo payload with ordered readonly signer accounts. /// Add one exact UTF-8 Memo payload with ordered readonly signer accounts.
AddMemo { AddMemo {
@@ -95,11 +81,7 @@ impl crate::ExSplMemoOperation {
} }
/// Complete typed intent accepted by the SPL Memo executor. /// Complete typed intent accepted by the SPL Memo executor.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/spl/memo/intent/ExSplMemoExecutionIntent.ts"
)]
pub struct ExSplMemoExecutionIntent { pub struct ExSplMemoExecutionIntent {
/// Stable caller-provided identifier used for logs and replay correlation. /// Stable caller-provided identifier used for logs and replay correlation.
pub intent_id: std::string::String, pub intent_id: std::string::String,

View File

@@ -1,10 +1,8 @@
// file: ks-lib/src/executor/spl/token/intent.rs // file: ks-lib/src/executor/spl/token/intent.rs
// version: 6 // version: 7
//! Typed classic SPL Token execution intents. //! Typed classic SPL Token execution intents.
use ts_rs::TS; // rust-rules: derive-import
/// Stable operation code for mint initialization through `InitializeMint2`. /// Stable operation code for mint initialization through `InitializeMint2`.
pub const EX_SPL_TOKEN_INITIALIZE_MINT_OPERATION: &str = "spl.token.initialize_mint"; pub const EX_SPL_TOKEN_INITIALIZE_MINT_OPERATION: &str = "spl.token.initialize_mint";
/// Stable operation code for token-account initialization through `InitializeAccount3`. /// Stable operation code for token-account initialization through `InitializeAccount3`.
@@ -85,22 +83,14 @@ pub const EX_SPL_TOKEN_SUPPORTED_OPERATION_CODES: &[&str] = &[
]; ];
/// Exact unsigned on-chain amount represented as a decimal JSON string. /// Exact unsigned on-chain amount represented as a decimal JSON string.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/spl/token/intent/ExSplClassicTokenAmount.ts"
)]
pub struct ExSplClassicTokenAmount( pub struct ExSplClassicTokenAmount(
/// Canonical unsigned decimal representation. /// Canonical unsigned decimal representation.
pub std::string::String, pub std::string::String,
); );
/// Ordered simple or multisig authority supplied to an instruction builder. /// Ordered simple or multisig authority supplied to an instruction builder.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/spl/token/intent/ExSplClassicTokenAuthority.ts"
)]
pub struct ExSplClassicTokenAuthority { pub struct ExSplClassicTokenAuthority {
/// Authority account. It signs only when `multisig_signers` is empty. /// Authority account. It signs only when `multisig_signers` is empty.
pub authority: crate::MdPubkey, pub authority: crate::MdPubkey,
@@ -109,12 +99,8 @@ pub struct ExSplClassicTokenAuthority {
} }
/// Authority domain used by `SetAuthority`. /// Authority domain used by `SetAuthority`.
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/spl/token/intent/ExSplClassicTokenAuthorityType.ts"
)]
pub enum ExSplClassicTokenAuthorityType { pub enum ExSplClassicTokenAuthorityType {
/// Mint authority. /// Mint authority.
MintTokens, MintTokens,
@@ -127,12 +113,8 @@ pub enum ExSplClassicTokenAuthorityType {
} }
/// One officially constructible non-batch instruction. /// One officially constructible non-batch instruction.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[serde(tag = "instruction", rename_all = "snake_case")] #[serde(tag = "instruction", rename_all = "snake_case")]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/spl/token/intent/ExSplClassicTokenSingleOperation.ts"
)]
pub enum ExSplClassicTokenSingleOperation { pub enum ExSplClassicTokenSingleOperation {
/// Initialize a mint with the current no-Rent builder. /// Initialize a mint with the current no-Rent builder.
InitializeMint { InitializeMint {
@@ -407,12 +389,8 @@ impl crate::ExSplClassicTokenSingleOperation {
} }
/// Top-level SPL Token operation, including the recent bounded Batch builder. /// Top-level SPL Token operation, including the recent bounded Batch builder.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[serde(tag = "operation", rename_all = "snake_case")] #[serde(tag = "operation", rename_all = "snake_case")]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/spl/token/intent/ExSplClassicTokenOperation.ts"
)]
pub enum ExSplClassicTokenOperation { pub enum ExSplClassicTokenOperation {
/// Build one non-batch instruction. /// Build one non-batch instruction.
Instruction { Instruction {
@@ -446,11 +424,7 @@ impl crate::ExSplClassicTokenOperation {
} }
/// Complete typed intent accepted by the classic SPL Token executor. /// Complete typed intent accepted by the classic SPL Token executor.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/spl/token/intent/ExSplClassicTokenExecutionIntent.ts"
)]
pub struct ExSplClassicTokenExecutionIntent { pub struct ExSplClassicTokenExecutionIntent {
/// Stable caller-provided identifier used for logs and replay correlation. /// Stable caller-provided identifier used for logs and replay correlation.
pub intent_id: std::string::String, pub intent_id: std::string::String,

View File

@@ -1,10 +1,8 @@
// file: ks-lib/src/executor/spl/token_2022/confidential.rs // file: ks-lib/src/executor/spl/token_2022/confidential.rs
// version: 10 // version: 11
//! Audited Confidential Transfer execution contracts. //! Audited Confidential Transfer execution contracts.
use ts_rs::TS; // rust-rules: derive-import
/// Maximum number of proof references accepted by one confidential operation. /// Maximum number of proof references accepted by one confidential operation.
pub const EX_SPL_TOKEN_2022_MAX_CONFIDENTIAL_PROOF_REFERENCES: usize = 5; pub const EX_SPL_TOKEN_2022_MAX_CONFIDENTIAL_PROOF_REFERENCES: usize = 5;
/// Exact byte length of an ElGamal public key accepted from callers. /// Exact byte length of an ElGamal public key accepted from callers.
@@ -27,11 +25,7 @@ pub const EX_SPL_TOKEN_2022_CONFIDENTIAL_TRANSFER_WITH_FEE_INSTRUCTION_DATA_BYTE
2 + EX_SPL_TOKEN_2022_CONFIDENTIAL_TRANSFER_WITH_FEE_PAYLOAD_BYTES; 2 + EX_SPL_TOKEN_2022_CONFIDENTIAL_TRANSFER_WITH_FEE_PAYLOAD_BYTES;
/// Caller-generated ElGamal public key encoded as lowercase or uppercase hexadecimal. /// Caller-generated ElGamal public key encoded as lowercase or uppercase hexadecimal.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/spl/token_2022/confidential/ExSplTokenElGamalPubkey.ts"
)]
pub struct ExSplTokenElGamalPubkey(pub std::string::String); pub struct ExSplTokenElGamalPubkey(pub std::string::String);
impl ExSplTokenElGamalPubkey { impl ExSplTokenElGamalPubkey {
@@ -56,11 +50,7 @@ impl ExSplTokenElGamalPubkey {
} }
/// Caller-generated decryptable balance encoded as hexadecimal bytes. /// Caller-generated decryptable balance encoded as hexadecimal bytes.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/spl/token_2022/confidential/ExSplTokenDecryptableBalance.ts"
)]
pub struct ExSplTokenDecryptableBalance(pub std::string::String); pub struct ExSplTokenDecryptableBalance(pub std::string::String);
impl ExSplTokenDecryptableBalance { impl ExSplTokenDecryptableBalance {
@@ -86,11 +76,7 @@ impl ExSplTokenDecryptableBalance {
} }
/// Caller-generated ElGamal ciphertext encoded as hexadecimal bytes. /// Caller-generated ElGamal ciphertext encoded as hexadecimal bytes.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/spl/token_2022/confidential/ExSplTokenElGamalCiphertext.ts"
)]
pub struct ExSplTokenElGamalCiphertext(pub std::string::String); pub struct ExSplTokenElGamalCiphertext(pub std::string::String);
impl ExSplTokenElGamalCiphertext { impl ExSplTokenElGamalCiphertext {
@@ -153,12 +139,8 @@ fn decode_hex_nibble(value: u8) -> Option<u8> {
} }
/// Proof statement required by a Confidential Transfer operation. /// Proof statement required by a Confidential Transfer operation.
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/spl/token_2022/confidential/ExSplTokenConfidentialProofKind.ts"
)]
pub enum ExSplTokenConfidentialProofKind { pub enum ExSplTokenConfidentialProofKind {
/// ElGamal public-key validity. /// ElGamal public-key validity.
PubkeyValidity, PubkeyValidity,
@@ -183,12 +165,8 @@ pub enum ExSplTokenConfidentialProofKind {
} }
/// Explicit source for one already-generated zero-knowledge proof statement. /// Explicit source for one already-generated zero-knowledge proof statement.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "snake_case", tag = "mode")] #[serde(rename_all = "snake_case", tag = "mode")]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/spl/token_2022/confidential/ExSplTokenConfidentialProofLocation.ts"
)]
pub enum ExSplTokenConfidentialProofLocation { pub enum ExSplTokenConfidentialProofLocation {
/// Relative transaction offset of a separate ZK proof-program instruction. /// Relative transaction offset of a separate ZK proof-program instruction.
InstructionOffset { InstructionOffset {
@@ -216,12 +194,8 @@ impl ExSplTokenConfidentialProofLocation {
} }
/// One proof requirement paired with its explicit location. /// One proof requirement paired with its explicit location.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/spl/token_2022/confidential/ExSplTokenConfidentialProofReference.ts"
)]
pub struct ExSplTokenConfidentialProofReference { pub struct ExSplTokenConfidentialProofReference {
/// Exact proof statement required by the Token-2022 builder. /// Exact proof statement required by the Token-2022 builder.
pub kind: ExSplTokenConfidentialProofKind, pub kind: ExSplTokenConfidentialProofKind,
@@ -230,12 +204,8 @@ pub struct ExSplTokenConfidentialProofReference {
} }
/// Audited Confidential Transfer operation surface. /// Audited Confidential Transfer operation surface.
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/spl/token_2022/confidential/ExSplTokenConfidentialOperation.ts"
)]
pub enum ExSplTokenConfidentialOperation { pub enum ExSplTokenConfidentialOperation {
/// Initialize confidential-transfer mint configuration. /// Initialize confidential-transfer mint configuration.
InitializeMint, InitializeMint,
@@ -332,12 +302,8 @@ impl ExSplTokenConfidentialOperation {
} }
/// Audited Confidential Mint/Burn operation surface. /// Audited Confidential Mint/Burn operation surface.
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/spl/token_2022/confidential/ExSplTokenConfidentialMintBurnOperation.ts"
)]
pub enum ExSplTokenConfidentialMintBurnOperation { pub enum ExSplTokenConfidentialMintBurnOperation {
/// Initialize the mint extension with supply encryption material. /// Initialize the mint extension with supply encryption material.
InitializeMint, InitializeMint,
@@ -395,12 +361,8 @@ impl ExSplTokenConfidentialMintBurnOperation {
} }
/// Executor-readiness classification established by the Confidential Transfer audit. /// Executor-readiness classification established by the Confidential Transfer audit.
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/spl/token_2022/confidential/ExSplTokenConfidentialExecutorSupport.ts"
)]
pub enum ExSplTokenConfidentialExecutorSupport { pub enum ExSplTokenConfidentialExecutorSupport {
/// The builder needs only public fields and ordinary authorities. /// The builder needs only public fields and ordinary authorities.
PublicBuilderReady, PublicBuilderReady,

View File

@@ -1,10 +1,8 @@
// file: ks-lib/src/executor/spl/token_2022/intent.rs // file: ks-lib/src/executor/spl/token_2022/intent.rs
// version: 28 // version: 29
//! Typed Token-2022 execution intents. //! Typed Token-2022 execution intents.
use ts_rs::TS; // rust-rules: derive-import
/// Stable operation code for mint initialization through `InitializeMint2`. /// Stable operation code for mint initialization through `InitializeMint2`.
pub const EX_SPL_TOKEN_2022_INITIALIZE_MINT_OPERATION: &str = "spl.token_2022.initialize_mint"; pub const EX_SPL_TOKEN_2022_INITIALIZE_MINT_OPERATION: &str = "spl.token_2022.initialize_mint";
/// Stable operation code for token-account initialization through `InitializeAccount3`. /// Stable operation code for token-account initialization through `InitializeAccount3`.
@@ -357,33 +355,21 @@ pub const EX_SPL_TOKEN_2022_SUPPORTED_OPERATION_CODES: &[&str] = &[
]; ];
/// Exact unsigned on-chain amount represented as a decimal JSON string. /// Exact unsigned on-chain amount represented as a decimal JSON string.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/spl/token_2022/intent/ExSplTokenAmount.ts"
)]
pub struct ExSplTokenAmount( pub struct ExSplTokenAmount(
/// Canonical unsigned decimal representation. /// Canonical unsigned decimal representation.
pub std::string::String, pub std::string::String,
); );
/// Positive normal finite scaled-UI multiplier represented as a decimal JSON string. /// Positive normal finite scaled-UI multiplier represented as a decimal JSON string.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/spl/token_2022/intent/ExSplTokenMultiplier.ts"
)]
pub struct ExSplTokenMultiplier( pub struct ExSplTokenMultiplier(
/// Decimal representation parsed to the official `f64` wire contract. /// Decimal representation parsed to the official `f64` wire contract.
pub std::string::String, pub std::string::String,
); );
/// Ordered simple or multisig authority supplied to an instruction builder. /// Ordered simple or multisig authority supplied to an instruction builder.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/spl/token_2022/intent/ExSplTokenAuthority.ts"
)]
pub struct ExSplTokenAuthority { pub struct ExSplTokenAuthority {
/// Authority account. It signs only when `multisig_signers` is empty. /// Authority account. It signs only when `multisig_signers` is empty.
pub authority: crate::MdPubkey, pub authority: crate::MdPubkey,
@@ -392,12 +378,8 @@ pub struct ExSplTokenAuthority {
} }
/// Authority domain used by `SetAuthority`. /// Authority domain used by `SetAuthority`.
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/spl/token_2022/intent/ExSplTokenAuthorityType.ts"
)]
pub enum ExSplTokenAuthorityType { pub enum ExSplTokenAuthorityType {
/// Mint authority. /// Mint authority.
MintTokens, MintTokens,
@@ -410,12 +392,8 @@ pub enum ExSplTokenAuthorityType {
} }
/// Default state assigned to newly initialized Token-2022 accounts. /// Default state assigned to newly initialized Token-2022 accounts.
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/spl/token_2022/intent/ExSplTokenDefaultAccountState.ts"
)]
pub enum ExSplTokenDefaultAccountState { pub enum ExSplTokenDefaultAccountState {
/// Newly initialized accounts remain usable. /// Newly initialized accounts remain usable.
Initialized, Initialized,
@@ -424,12 +402,8 @@ pub enum ExSplTokenDefaultAccountState {
} }
/// Token-metadata field selected for an update. /// Token-metadata field selected for an update.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "snake_case", tag = "kind", content = "key")] #[serde(rename_all = "snake_case", tag = "kind", content = "key")]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/spl/token_2022/intent/ExSplTokenMetadataField.ts"
)]
pub enum ExSplTokenMetadataField { pub enum ExSplTokenMetadataField {
/// Update the required name field. /// Update the required name field.
Name, Name,
@@ -442,12 +416,8 @@ pub enum ExSplTokenMetadataField {
} }
/// One officially constructible non-batch instruction. /// One officially constructible non-batch instruction.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[serde(tag = "instruction", rename_all = "snake_case")] #[serde(tag = "instruction", rename_all = "snake_case")]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/spl/token_2022/intent/ExSplTokenSingleOperation.ts"
)]
pub enum ExSplTokenSingleOperation { pub enum ExSplTokenSingleOperation {
/// Initialize a mint with the current no-Rent builder. /// Initialize a mint with the current no-Rent builder.
InitializeMint { InitializeMint {
@@ -1605,12 +1575,8 @@ impl crate::ExSplTokenSingleOperation {
} }
/// Top-level SPL Token operation, including the recent bounded Batch builder. /// Top-level SPL Token operation, including the recent bounded Batch builder.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[serde(tag = "operation", rename_all = "snake_case")] #[serde(tag = "operation", rename_all = "snake_case")]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/spl/token_2022/intent/ExSplToken2022Operation.ts"
)]
pub enum ExSplToken2022Operation { pub enum ExSplToken2022Operation {
/// Build one non-batch instruction. /// Build one non-batch instruction.
Instruction { Instruction {
@@ -1644,11 +1610,7 @@ impl crate::ExSplToken2022Operation {
} }
/// Complete typed intent accepted by the Token-2022 executor. /// Complete typed intent accepted by the Token-2022 executor.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/executor/spl/token_2022/intent/ExSplToken2022ExecutionIntent.ts"
)]
pub struct ExSplToken2022ExecutionIntent { pub struct ExSplToken2022ExecutionIntent {
/// Stable caller-provided identifier used for logs and replay correlation. /// Stable caller-provided identifier used for logs and replay correlation.
pub intent_id: std::string::String, pub intent_id: std::string::String,

View File

@@ -1,16 +1,10 @@
// file: ks-lib/src/model/decoded.rs // file: ks-lib/src/model/decoded.rs
// version: 6 // version: 7
//! Shared decoded protocol event model. //! Shared decoded protocol event model.
use ts_rs::TS; // rust-rules: derive-import
/// Source from which a decoded event was derived. /// Source from which a decoded event was derived.
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/model/decoded/EventSourceKind.ts"
)]
pub enum MdEventSourceKind { pub enum MdEventSourceKind {
/// Top-level instruction. /// Top-level instruction.
Instruction, Instruction,
@@ -33,11 +27,7 @@ pub enum MdEventSourceKind {
} }
/// Confidence level for a decoded event. /// Confidence level for a decoded event.
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/model/decoded/DecoderConfidence.ts"
)]
pub enum MdDecoderConfidence { pub enum MdDecoderConfidence {
/// Exact manual decode. /// Exact manual decode.
Exact, Exact,
@@ -56,11 +46,7 @@ pub enum MdDecoderConfidence {
} }
/// Protocol-level decoded event produced by a decoder. /// Protocol-level decoded event produced by a decoder.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/model/decoded/DecodedProtocolEvent.ts"
)]
pub struct MdDecodedProtocolEvent { pub struct MdDecodedProtocolEvent {
/// Transaction signature. /// Transaction signature.
pub signature: crate::MdSignature, pub signature: crate::MdSignature,

View File

@@ -1,16 +1,10 @@
// file: ks-lib/src/model/materialized.rs // file: ks-lib/src/model/materialized.rs
// version: 7 // version: 8
//! Shared materialized business event model. //! Shared materialized business event model.
use ts_rs::TS; // rust-rules: derive-import
/// Materialized event family. /// Materialized event family.
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/model/materialized/MaterializedEventFamily.ts"
)]
pub enum MdMaterializedEventFamily { pub enum MdMaterializedEventFamily {
/// Trade materialization. /// Trade materialization.
Trade, Trade,
@@ -63,11 +57,7 @@ pub enum MdMaterializedEventFamily {
} }
/// Business-level materialized event placeholder. /// Business-level materialized event placeholder.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/model/materialized/MaterializedEvent.ts"
)]
pub struct MdMaterializedEvent { pub struct MdMaterializedEvent {
/// Source transaction signature. /// Source transaction signature.
pub signature: crate::MdSignature, pub signature: crate::MdSignature,

View File

@@ -1,56 +1,30 @@
// file: ks-lib/src/model/nomenclature.rs // file: ks-lib/src/model/nomenclature.rs
// version: 6 // version: 7
//! Shared protocol, surface, and event nomenclature types. //! Shared protocol, surface, and event nomenclature types.
use ts_rs::TS; // rust-rules: derive-import
/// Stable internal program code. /// Stable internal program code.
#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/model/nomenclature/ProgramCode.ts"
)]
pub struct MdProgramCode(pub std::string::String); pub struct MdProgramCode(pub std::string::String);
/// Protocol family code, for example `raydium` or `meteora`. /// Protocol family code, for example `raydium` or `meteora`.
#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/model/nomenclature/ProtocolCode.ts"
)]
pub struct MdProtocolCode(pub std::string::String); pub struct MdProtocolCode(pub std::string::String);
/// Concrete protocol surface code, for example `raydium_amm_v4`. /// Concrete protocol surface code, for example `raydium_amm_v4`.
#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/model/nomenclature/SurfaceCode.ts"
)]
pub struct MdSurfaceCode(pub std::string::String); pub struct MdSurfaceCode(pub std::string::String);
/// Event name without surface prefix. /// Event name without surface prefix.
#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/model/nomenclature/EventName.ts"
)]
pub struct MdEventName(pub std::string::String); pub struct MdEventName(pub std::string::String);
/// Canonical event code in `<surface_code>.<event_name>` format. /// Canonical event code in `<surface_code>.<event_name>` format.
#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/model/nomenclature/EventCode.ts"
)]
pub struct MdEventCode(pub std::string::String); pub struct MdEventCode(pub std::string::String);
/// High-level event family. /// High-level event family.
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/model/nomenclature/EventFamily.ts"
)]
pub enum MdEventFamily { pub enum MdEventFamily {
/// Swap or buy/sell event. /// Swap or buy/sell event.
Trade, Trade,

View File

@@ -1,16 +1,10 @@
// file: ks-lib/src/model/observation.rs // file: ks-lib/src/model/observation.rs
// version: 6 // version: 7
//! Shared program observation model. //! Shared program observation model.
use ts_rs::TS; // rust-rules: derive-import
/// Generic program observation derived from a Solana instruction. /// Generic program observation derived from a Solana instruction.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/model/observation/ProgramObservation.ts"
)]
pub struct MdProgramObservation { pub struct MdProgramObservation {
/// Transaction signature. /// Transaction signature.
pub signature: crate::MdSignature, pub signature: crate::MdSignature,

View File

@@ -1,46 +1,26 @@
// file: ks-lib/src/model/solana.rs // file: ks-lib/src/model/solana.rs
// version: 6 // version: 7
//! Shared Solana primitive wrapper types. //! Shared Solana primitive wrapper types.
use ts_rs::TS; // rust-rules: derive-import
/// Solana transaction signature. /// Solana transaction signature.
#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize)]
#[ts(export, export_to = "../frontend/ts/bindings/ks_lib/model/solana/MdSignature.ts")]
pub struct MdSignature(pub std::string::String); pub struct MdSignature(pub std::string::String);
/// Solana slot. /// Solana slot.
#[derive( #[derive(
Clone, Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, serde::Deserialize, serde::Serialize,
Copy,
Debug,
Eq,
PartialEq,
Ord,
PartialOrd,
Hash,
serde::Deserialize,
serde::Serialize,
TS,
)] )]
#[ts(export, export_to = "../frontend/ts/bindings/ks_lib/model/solana/MdSlot.ts")]
pub struct MdSlot(pub u64); pub struct MdSlot(pub u64);
/// Solana program id. /// Solana program id.
#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize)]
#[ts(export, export_to = "../frontend/ts/bindings/ks_lib/model/solana/MdProgramId.ts")]
pub struct MdProgramId(pub std::string::String); pub struct MdProgramId(pub std::string::String);
/// Solana public key. /// Solana public key.
#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize)]
#[ts(export, export_to = "../frontend/ts/bindings/ks_lib/model/solana/MdPubkey.ts")]
pub struct MdPubkey(pub std::string::String); pub struct MdPubkey(pub std::string::String);
/// Stable top-level or inner instruction path. /// Stable top-level or inner instruction path.
#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize, TS)] #[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize)]
#[ts(
export,
export_to = "../frontend/ts/bindings/ks_lib/model/solana/MdInstructionPath.ts"
)]
pub struct MdInstructionPath(pub std::string::String); pub struct MdInstructionPath(pub std::string::String);

View File

@@ -1,8 +1,14 @@
<!-- file: ks-logging/CHANGELOG.md --> <!-- file: ks-logging/CHANGELOG.md -->
<!-- version: 18 --> <!-- version: 20 -->
# CHANGELOG — ks-logging # CHANGELOG — ks-logging
## `0.5.1-pre.008`
- Fix: remplace un ancien fragment de credential Helius utilisé comme canari négatif par le nom synthétique `KS_SECRET_HELIUS_API_KEY`; aucune donnée réelle ou partielle de credential ne doit être conservée dans les tests.
- clarifie que la non-divulgation des secrets de configuration est garantie à la source et aux frontières public/diagnostic ; conserve comme défense en profondeur future l'évaluation d'un redactor générique pour des chaînes arbitraires fournies par les appelants.
## `0.5.1-pre.007` ## `0.5.1-pre.007`
- place `logs_directory` hors profils dans le document logging et le rend configurable via `${KS_LOGS_DIRECTORY:-logs}` ; - place `logs_directory` hors profils dans le document logging et le rend configurable via `${KS_LOGS_DIRECTORY:-logs}` ;

View File

@@ -1,12 +1,11 @@
<!-- file: ks-logging/TODO.md --> <!-- file: ks-logging/TODO.md -->
<!-- version: 6 --> <!-- version: 7 -->
# TODO — ks-logging # TODO — ks-logging
## Série `0.5.x` ## Série `0.5.x`
- [ ] `0.5.1` - intégrer la propagation de sensibilité et le camouflage des valeurs provenant de `KS_SECRET_*` / `KB_SECRET_*` avant tout diagnostic ou log.
- [ ] Compatibilité - préserver targets, niveaux, filtres, formats, routes et sémantique wildcard pendant les prochaines migrations. - [ ] Compatibilité - préserver targets, niveaux, filtres, formats, routes et sémantique wildcard pendant les prochaines migrations.
- [ ] Contrat - réévaluer `LogFileRoute`, actuellement sans consommateur actif hors de la crate. - [ ] Contrat - réévaluer `LogFileRoute`, actuellement sans consommateur actif hors de la crate.
- [ ] Tests - ajouter les canaris garantissant quun secret résolu natteint jamais une sortie logging. - [ ] Défense en profondeur - évaluer ultérieurement un filtre de sortie générique pour les secrets fournis arbitrairement par les appelants, sans remplacer linterdiction de journaliser ces valeurs à la source.
- [ ] Documentation - maintenir le schéma et les exemples logging alignés avec les types possédés par la crate. - [ ] Documentation - maintenir le schéma et les exemples logging alignés avec les types possédés par la crate.

View File

@@ -1,5 +1,5 @@
// file: ks-logging/src/document.rs // file: ks-logging/src/document.rs
// version: 5 // version: 6
//! Independent logging configuration document loading and validation. //! Independent logging configuration document loading and validation.
@@ -351,7 +351,8 @@ fn require_non_empty(value: &str, field_name: &str) -> ks_core::Result<()> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
const DEFAULT_LOGGING_CONFIG: &str = include_str!("../../config/logging.config.json"); const DEFAULT_LOGGING_CONFIG: &str = include_str!("../../config/logging.config.json");
const EXAMPLE_LOGGING_CONFIG: &str = include_str!("../../config/example.logging.config.json"); const EXAMPLE_LOGGING_CONFIG: &str =
include_str!("../../config/exemples/example.logging.config.json");
fn parse_default_value() -> serde_json::Value { fn parse_default_value() -> serde_json::Value {
let result = serde_json::from_str::<serde_json::Value>(DEFAULT_LOGGING_CONFIG); let result = serde_json::from_str::<serde_json::Value>(DEFAULT_LOGGING_CONFIG);
@@ -522,7 +523,7 @@ mod tests {
#[test] #[test]
fn default_logging_config_uses_canonical_wallet_and_pipeline_routes() { fn default_logging_config_uses_canonical_wallet_and_pipeline_routes() {
assert!(!DEFAULT_LOGGING_CONFIG.contains("api-key=95e73621")); assert!(!DEFAULT_LOGGING_CONFIG.contains("KS_SECRET_HELIUS_API_KEY"));
assert!(!DEFAULT_LOGGING_CONFIG.contains("\"ks_wallet\"")); assert!(!DEFAULT_LOGGING_CONFIG.contains("\"ks_wallet\""));
assert!(DEFAULT_LOGGING_CONFIG.contains("\"ks-wallet\"")); assert!(DEFAULT_LOGGING_CONFIG.contains("\"ks-wallet\""));
assert!(DEFAULT_LOGGING_CONFIG.contains("\"ks-pipeline\"")); assert!(DEFAULT_LOGGING_CONFIG.contains("\"ks-pipeline\""));

View File

@@ -1,8 +1,15 @@
<!-- file: ks-onchain-transport/CHANGELOG.md --> <!-- file: ks-onchain-transport/CHANGELOG.md -->
<!-- version: 15 --> <!-- version: 21 -->
# CHANGELOG — ks-onchain-transport # CHANGELOG — ks-onchain-transport
## `0.5.1-pre.008`
- Fix: retire complètement les URLs résolues des snapshots HTTP/WS/session, conserve ces snapshots backend-only et non sérialisables directement, sanitise le `Debug` de `WsSession`/`RpcEndpoint`, et ne recopie plus les erreurs brutes de connexion HTTP/WS susceptibles dinclure une URL résolue. Les corps HTTP non-success et messages JSON-RPC distants ne sont plus recopiés dans les erreurs/logs afin quun fournisseur ne puisse pas y réinjecter une URL ou un credential. Le getter dURL HTTP inutilisé est supprimé et le getter WS nécessaire à la session devient `pub(crate)`.
- remplace les dérivations `Debug` de `HttpClient` et `WsClient` par des implémentations manuelles sanitisées qui ne recopient aucune chaîne issue de la configuration dendpoint et utilisent un marqueur `<redacted>` ;
- conserve ainsi `Debug` sur les clients et pools sans réintroduire `Debug` sur `ks_config::HttpEndpointConfig` / `WsEndpointConfig`, et ajoute des tests canaris HTTP/WS de non-divulgation.
## `0.5.1-pre.007` ## `0.5.1-pre.007`
- aligne les fixtures dexécution sur le déplacement des autorisations `*_send_enabled` vers `ExecutionConfig` ; le comportement RPC reste inchangé. - aligne les fixtures dexécution sur le déplacement des autorisations `*_send_enabled` vers `ExecutionConfig` ; le comportement RPC reste inchangé.

View File

@@ -1,10 +1,10 @@
// file: ks-onchain-transport/src/client.rs // file: ks-onchain-transport/src/client.rs
// version: 4 // version: 5
//! RPC client scaffold for Solana ingestion. //! RPC client scaffold for Solana ingestion.
/// RPC endpoint configuration. /// RPC endpoint configuration.
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Clone, Eq, PartialEq)]
pub struct RpcEndpoint { pub struct RpcEndpoint {
/// HTTP RPC URL. /// HTTP RPC URL.
pub http_url: std::string::String, pub http_url: std::string::String,
@@ -12,6 +12,17 @@ pub struct RpcEndpoint {
pub ws_url: std::option::Option<std::string::String>, pub ws_url: std::option::Option<std::string::String>,
} }
impl std::fmt::Debug for crate::RpcEndpoint {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let ws_state = if self.ws_url.is_some() { "configured" } else { "missing" };
return formatter
.debug_struct("RpcEndpoint")
.field("http_url", &"<redacted>")
.field("ws_url", &ws_state)
.finish();
}
}
/// Minimal Solana RPC client abstraction. /// Minimal Solana RPC client abstraction.
pub trait SolanaRpcClient { pub trait SolanaRpcClient {
/// Fetches a raw transaction payload by signature. /// Fetches a raw transaction payload by signature.
@@ -20,3 +31,22 @@ pub trait SolanaRpcClient {
signature: &ks_lib::MdSignature, signature: &ks_lib::MdSignature,
) -> ks_core::Result<std::option::Option<std::string::String>>; ) -> ks_core::Result<std::option::Option<std::string::String>>;
} }
#[cfg(test)]
mod tests {
#[test]
fn rpc_endpoint_debug_omits_resolved_urls() {
let endpoint = crate::RpcEndpoint {
http_url: "https://HTTP-RPC-SECRET-CANARY.invalid/?api-key=secret".to_string(),
ws_url: std::option::Option::Some(
"wss://WS-RPC-SECRET-CANARY.invalid/?api-key=secret".to_string(),
),
};
let rendered = format!("{endpoint:?}");
assert!(rendered.contains("<redacted>"));
assert!(rendered.contains("configured"));
assert!(!rendered.contains("HTTP-RPC-SECRET-CANARY"));
assert!(!rendered.contains("WS-RPC-SECRET-CANARY"));
assert!(!rendered.contains("api-key=secret"));
}
}

View File

@@ -1,5 +1,5 @@
// file: ks-onchain-transport/src/http_client.rs // file: ks-onchain-transport/src/http_client.rs
// version: 14 // version: 20
//! HTTP JSON-RPC client for standard Solana RPC endpoints. //! HTTP JSON-RPC client for standard Solana RPC endpoints.
@@ -15,21 +15,30 @@ pub enum HttpMethodClass {
} }
/// Snapshot of one pooled HTTP endpoint. /// Snapshot of one pooled HTTP endpoint.
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize)] #[derive(Clone, Eq, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct HttpPoolClientSnapshot { pub struct HttpPoolClientSnapshot {
/// Logical endpoint name. /// Logical endpoint name.
pub endpoint_name: std::string::String, pub endpoint_name: std::string::String,
/// Provider name. /// Provider name.
pub provider: std::string::String, pub provider: std::string::String,
/// Endpoint URL.
pub endpoint_url: std::string::String,
/// Supported roles. /// Supported roles.
pub roles: std::vec::Vec<crate::EndpointRoleSnapshot>, pub roles: std::vec::Vec<crate::EndpointRoleSnapshot>,
/// Status string. /// Status string.
pub status: std::string::String, pub status: std::string::String,
} }
impl std::fmt::Debug for crate::HttpPoolClientSnapshot {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
return formatter
.debug_struct("HttpPoolClientSnapshot")
.field("endpoint_name", &self.endpoint_name)
.field("provider", &self.provider)
.field("roles", &self.roles)
.field("status", &self.status)
.finish();
}
}
#[derive(Debug)] #[derive(Debug)]
struct HttpRequestLimitState { struct HttpRequestLimitState {
available_tokens: f64, available_tokens: f64,
@@ -138,7 +147,7 @@ impl HttpRequestLimiter {
} }
/// HTTP JSON-RPC client bound to one configured endpoint. /// HTTP JSON-RPC client bound to one configured endpoint.
#[derive(Clone, Debug)] #[derive(Clone)]
pub struct HttpClient { pub struct HttpClient {
endpoint: ks_config::HttpEndpointConfig, endpoint: ks_config::HttpEndpointConfig,
client: reqwest::Client, client: reqwest::Client,
@@ -147,6 +156,16 @@ pub struct HttpClient {
selected_role: std::option::Option<std::string::String>, selected_role: std::option::Option<std::string::String>,
} }
impl std::fmt::Debug for crate::HttpClient {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
return formatter
.debug_struct("HttpClient")
.field("endpoint", &"<redacted>")
.field("selected_role_configured", &self.selected_role.is_some())
.finish_non_exhaustive();
}
}
impl crate::HttpClient { impl crate::HttpClient {
/// Creates a new HTTP client bound to one endpoint. /// Creates a new HTTP client bound to one endpoint.
pub fn new(endpoint: ks_config::HttpEndpointConfig) -> ks_core::Result<Self> { pub fn new(endpoint: ks_config::HttpEndpointConfig) -> ks_core::Result<Self> {
@@ -207,11 +226,6 @@ impl crate::HttpClient {
return self.endpoint.provider.as_str(); return self.endpoint.provider.as_str();
} }
/// Returns the endpoint URL.
pub fn endpoint_url(&self) -> &str {
return self.endpoint.url.as_str();
}
/// Returns the endpoint configuration. /// Returns the endpoint configuration.
pub fn endpoint_config(&self) -> &ks_config::HttpEndpointConfig { pub fn endpoint_config(&self) -> &ks_config::HttpEndpointConfig {
return &self.endpoint; return &self.endpoint;
@@ -230,7 +244,7 @@ impl crate::HttpClient {
return false; return false;
} }
/// Returns a serializable endpoint snapshot. /// Returns a backend-only endpoint snapshot without resolved URL material.
pub fn snapshot(&self) -> crate::HttpPoolClientSnapshot { pub fn snapshot(&self) -> crate::HttpPoolClientSnapshot {
let mut roles = std::vec::Vec::new(); let mut roles = std::vec::Vec::new();
for role in &self.endpoint.roles { for role in &self.endpoint.roles {
@@ -239,7 +253,6 @@ impl crate::HttpClient {
return crate::HttpPoolClientSnapshot { return crate::HttpPoolClientSnapshot {
endpoint_name: self.endpoint.name.clone(), endpoint_name: self.endpoint.name.clone(),
provider: self.endpoint.provider.clone(), provider: self.endpoint.provider.clone(),
endpoint_url: self.endpoint.url.clone(),
roles, roles,
status: "active".to_string(), status: "active".to_string(),
}; };
@@ -342,9 +355,10 @@ impl crate::HttpClient {
let response = match response_result { let response = match response_result {
std::result::Result::Ok(response) => response, std::result::Result::Ok(response) => response,
std::result::Result::Err(error) => { std::result::Result::Err(error) => {
tracing::error!(target: crate::TRACING_TARGET, action = "execute_http_json_rpc", endpoint_name = %self.endpoint.name, provider = %self.endpoint.provider, request_id, method = %method, retry_index, error = %error, "HTTP JSON-RPC transport failed"); let error_kind = reqwest_error_kind(&error);
tracing::error!(target: crate::TRACING_TARGET, action = "execute_http_json_rpc", endpoint_name = %self.endpoint.name, provider = %self.endpoint.provider, request_id, method = %method, retry_index, error_kind, "HTTP JSON-RPC transport failed");
return std::result::Result::Err(ks_core::Error::http(format!( return std::result::Result::Err(ks_core::Error::http(format!(
"http json-rpc request '{}' failed on endpoint '{}': {error}", "http json-rpc request '{}' failed on endpoint '{}' ({error_kind})",
method, self.endpoint.name method, self.endpoint.name
))); )));
}, },
@@ -355,9 +369,10 @@ impl crate::HttpClient {
let text = match text_result { let text = match text_result {
std::result::Result::Ok(text) => text, std::result::Result::Ok(text) => text,
std::result::Result::Err(error) => { std::result::Result::Err(error) => {
tracing::error!(target: crate::TRACING_TARGET, action = "execute_http_json_rpc", endpoint_name = %self.endpoint.name, provider = %self.endpoint.provider, request_id, method = %method, http_status = %status, retry_index, error = %error, "HTTP JSON-RPC response body read failed"); let error_kind = reqwest_error_kind(&error);
tracing::error!(target: crate::TRACING_TARGET, action = "execute_http_json_rpc", endpoint_name = %self.endpoint.name, provider = %self.endpoint.provider, request_id, method = %method, http_status = %status, retry_index, error_kind, "HTTP JSON-RPC response body read failed");
return std::result::Result::Err(ks_core::Error::http(format!( return std::result::Result::Err(ks_core::Error::http(format!(
"cannot read http json-rpc response '{}' from endpoint '{}': {error}", "cannot read http json-rpc response '{}' from endpoint '{}' ({error_kind})",
method, self.endpoint.name method, self.endpoint.name
))); )));
}, },
@@ -383,8 +398,8 @@ impl crate::HttpClient {
if !status.is_success() { if !status.is_success() {
tracing::error!(target: crate::TRACING_TARGET, action = "execute_http_json_rpc", endpoint_name = %self.endpoint.name, provider = %self.endpoint.provider, request_id, method = %method, http_status = %status, retry_index, response_byte_length = text.len(), "HTTP JSON-RPC endpoint returned non-success status"); tracing::error!(target: crate::TRACING_TARGET, action = "execute_http_json_rpc", endpoint_name = %self.endpoint.name, provider = %self.endpoint.provider, request_id, method = %method, http_status = %status, retry_index, response_byte_length = text.len(), "HTTP JSON-RPC endpoint returned non-success status");
return std::result::Result::Err(ks_core::Error::http(format!( return std::result::Result::Err(ks_core::Error::http(format!(
"http json-rpc endpoint '{}' returned status {} after {} retries: {}", "http json-rpc endpoint '{}' returned status {} after {} retries",
self.endpoint.name, status, retry_index, text self.endpoint.name, status, retry_index
))); )));
} }
let parsed = match crate::parse_json_rpc_text(&text) { let parsed = match crate::parse_json_rpc_text(&text) {
@@ -412,7 +427,7 @@ impl crate::HttpClient {
}, },
std::option::Option::None => configured_pause_ms, std::option::Option::None => configured_pause_ms,
}; };
tracing::warn!(target: crate::TRACING_TARGET, action = "retry_http_json_rpc_after_rate_limit", endpoint_name = %self.endpoint.name, provider = %self.endpoint.provider, request_id, method = %method, rpc_error_code = error_response.error.code, rpc_error_message = %error_response.error.message, retry_index, pause_ms, "HTTP JSON-RPC endpoint returned a rate-limit RPC error; retrying with bounded backoff"); tracing::warn!(target: crate::TRACING_TARGET, action = "retry_http_json_rpc_after_rate_limit", endpoint_name = %self.endpoint.name, provider = %self.endpoint.provider, request_id, method = %method, rpc_error_code = error_response.error.code, retry_index, pause_ms, "HTTP JSON-RPC endpoint returned a rate-limit RPC error; retrying with bounded backoff");
if let std::option::Option::Some(limiter) = &request_limiter { if let std::option::Option::Some(limiter) = &request_limiter {
limiter.block_for(pause_ms).await; limiter.block_for(pause_ms).await;
} }
@@ -420,13 +435,10 @@ impl crate::HttpClient {
continue; continue;
}, },
crate::JsonRpcResponse::Error(error_response) => { crate::JsonRpcResponse::Error(error_response) => {
tracing::error!(target: crate::TRACING_TARGET, action = "execute_http_json_rpc", endpoint_name = %self.endpoint.name, provider = %self.endpoint.provider, request_id, method = %method, retry_index, rpc_error_code = error_response.error.code, rpc_error_message = %error_response.error.message, "HTTP JSON-RPC endpoint returned an RPC error"); tracing::error!(target: crate::TRACING_TARGET, action = "execute_http_json_rpc", endpoint_name = %self.endpoint.name, provider = %self.endpoint.provider, request_id, method = %method, retry_index, rpc_error_code = error_response.error.code, "HTTP JSON-RPC endpoint returned an RPC error");
return std::result::Result::Err(ks_core::Error::http(format!( return std::result::Result::Err(ks_core::Error::http(format!(
"json-rpc error {} from '{}' after {} retries: {}", "json-rpc error {} from '{}' after {} retries",
error_response.error.code, error_response.error.code, self.endpoint.name, retry_index
self.endpoint.name,
retry_index,
error_response.error.message
))); )));
}, },
crate::JsonRpcResponse::Notification(_) => { crate::JsonRpcResponse::Notification(_) => {
@@ -515,6 +527,25 @@ impl crate::HttpClient {
} }
} }
fn reqwest_error_kind(error: &reqwest::Error) -> &'static str {
if error.is_timeout() {
return "timeout";
}
if error.is_connect() {
return "connect";
}
if error.is_request() {
return "request";
}
if error.is_body() {
return "body";
}
if error.is_decode() {
return "decode";
}
return "transport";
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
fn role_config( fn role_config(
@@ -577,6 +608,27 @@ mod tests {
assert!(client.can_handle("http_any", "send_transaction")); assert!(client.can_handle("http_any", "send_transaction"));
} }
#[test]
fn debug_output_omits_endpoint_url() {
let mut endpoint = endpoint(true);
endpoint.url = "https://HTTP-SECRET-CANARY.invalid/?api-key=secret".to_string();
let client = match crate::HttpClient::new(endpoint) {
std::result::Result::Ok(client) => client,
std::result::Result::Err(error) => panic!("client creation failed: {error}"),
};
let rendered = format!("{client:?}");
assert!(rendered.contains("<redacted>"));
assert!(rendered.contains("selected_role_configured: false"));
assert!(!rendered.contains("HTTP-SECRET-CANARY"));
assert!(!rendered.contains("api-key=secret"));
let snapshot = client.snapshot();
let snapshot_rendered = format!("{snapshot:?}");
assert!(!snapshot_rendered.contains("endpoint_url"));
assert!(!snapshot_rendered.contains("endpointUrl"));
assert!(!snapshot_rendered.contains("HTTP-SECRET-CANARY"));
assert!(!snapshot_rendered.contains("api-key=secret"));
}
#[test] #[test]
fn snapshot_preserves_endpoint_metadata() { fn snapshot_preserves_endpoint_metadata() {
let client = match crate::HttpClient::new(endpoint(true)) { let client = match crate::HttpClient::new(endpoint(true)) {
@@ -586,7 +638,6 @@ mod tests {
let snapshot = client.snapshot(); let snapshot = client.snapshot();
assert_eq!(snapshot.endpoint_name, "http_a"); assert_eq!(snapshot.endpoint_name, "http_a");
assert_eq!(snapshot.provider, "test"); assert_eq!(snapshot.provider, "test");
assert_eq!(snapshot.endpoint_url, "https://example.invalid");
assert_eq!(snapshot.roles.len(), 3); assert_eq!(snapshot.roles.len(), 3);
} }

View File

@@ -1,5 +1,5 @@
// file: ks-onchain-transport/src/http_pool.rs // file: ks-onchain-transport/src/http_pool.rs
// version: 10 // version: 11
//! HTTP endpoint pool and role-based routing. //! HTTP endpoint pool and role-based routing.
@@ -53,7 +53,7 @@ impl crate::HttpEndpointPool {
}); });
} }
/// Returns a serializable snapshot of every endpoint in the pool. /// Returns backend-only endpoint metadata without resolved URL material.
pub fn snapshot(&self) -> std::vec::Vec<crate::HttpPoolClientSnapshot> { pub fn snapshot(&self) -> std::vec::Vec<crate::HttpPoolClientSnapshot> {
let mut snapshots = std::vec::Vec::new(); let mut snapshots = std::vec::Vec::new();
for client in &self.clients { for client in &self.clients {

View File

@@ -1,5 +1,5 @@
// file: ks-onchain-transport/src/ws_client.rs // file: ks-onchain-transport/src/ws_client.rs
// version: 9 // version: 15
//! Standard Solana WebSocket client helpers. //! Standard Solana WebSocket client helpers.
@@ -7,28 +7,46 @@ use futures_util::SinkExt; // rust-rules: trait-import
use futures_util::StreamExt; // rust-rules: trait-import use futures_util::StreamExt; // rust-rules: trait-import
/// Snapshot of one pooled WebSocket endpoint. /// Snapshot of one pooled WebSocket endpoint.
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize)] #[derive(Clone, Eq, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct WsPoolClientSnapshot { pub struct WsPoolClientSnapshot {
/// Logical endpoint name. /// Logical endpoint name.
pub endpoint_name: std::string::String, pub endpoint_name: std::string::String,
/// Provider name. /// Provider name.
pub provider: std::string::String, pub provider: std::string::String,
/// Endpoint URL.
pub endpoint_url: std::string::String,
/// Supported roles. /// Supported roles.
pub roles: std::vec::Vec<crate::EndpointRoleSnapshot>, pub roles: std::vec::Vec<crate::EndpointRoleSnapshot>,
/// Status string. /// Status string.
pub status: std::string::String, pub status: std::string::String,
} }
impl std::fmt::Debug for crate::WsPoolClientSnapshot {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
return formatter
.debug_struct("WsPoolClientSnapshot")
.field("endpoint_name", &self.endpoint_name)
.field("provider", &self.provider)
.field("roles", &self.roles)
.field("status", &self.status)
.finish();
}
}
/// Standard Solana WebSocket client bound to one configured endpoint. /// Standard Solana WebSocket client bound to one configured endpoint.
#[derive(Clone, Debug)] #[derive(Clone)]
pub struct WsClient { pub struct WsClient {
endpoint: ks_config::WsEndpointConfig, endpoint: ks_config::WsEndpointConfig,
next_request_id: std::sync::Arc<std::sync::atomic::AtomicU64>, next_request_id: std::sync::Arc<std::sync::atomic::AtomicU64>,
} }
impl std::fmt::Debug for crate::WsClient {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
return formatter
.debug_struct("WsClient")
.field("endpoint", &"<redacted>")
.finish_non_exhaustive();
}
}
impl crate::WsClient { impl crate::WsClient {
/// Creates a new WebSocket client bound to one endpoint. /// Creates a new WebSocket client bound to one endpoint.
pub fn new(endpoint: ks_config::WsEndpointConfig) -> ks_core::Result<Self> { pub fn new(endpoint: ks_config::WsEndpointConfig) -> ks_core::Result<Self> {
@@ -56,8 +74,7 @@ impl crate::WsClient {
return self.endpoint.provider.as_str(); return self.endpoint.provider.as_str();
} }
/// Returns the endpoint URL. pub(crate) fn endpoint_url(&self) -> &str {
pub fn endpoint_url(&self) -> &str {
return self.endpoint.url.as_str(); return self.endpoint.url.as_str();
} }
@@ -79,7 +96,7 @@ impl crate::WsClient {
return false; return false;
} }
/// Returns a serializable endpoint snapshot. /// Returns a backend-only endpoint snapshot without resolved URL material.
pub fn snapshot(&self) -> crate::WsPoolClientSnapshot { pub fn snapshot(&self) -> crate::WsPoolClientSnapshot {
let mut roles = std::vec::Vec::new(); let mut roles = std::vec::Vec::new();
for role in &self.endpoint.roles { for role in &self.endpoint.roles {
@@ -88,7 +105,6 @@ impl crate::WsClient {
return crate::WsPoolClientSnapshot { return crate::WsPoolClientSnapshot {
endpoint_name: self.endpoint.name.clone(), endpoint_name: self.endpoint.name.clone(),
provider: self.endpoint.provider.clone(), provider: self.endpoint.provider.clone(),
endpoint_url: self.endpoint.url.clone(),
roles, roles,
status: "idle".to_string(), status: "idle".to_string(),
}; };
@@ -156,10 +172,10 @@ impl crate::WsClient {
}; };
let (mut stream, _response) = match connect_result { let (mut stream, _response) = match connect_result {
std::result::Result::Ok(pair) => pair, std::result::Result::Ok(pair) => pair,
std::result::Result::Err(error) => { std::result::Result::Err(_error) => {
tracing::error!(target: crate::TRACING_TARGET, action = "connect_ws_endpoint", endpoint_name = %self.endpoint.name, provider = %self.endpoint.provider, request_id = ?request.id, method = %method, error = %error, "WebSocket endpoint connection failed"); tracing::error!(target: crate::TRACING_TARGET, action = "connect_ws_endpoint", endpoint_name = %self.endpoint.name, provider = %self.endpoint.provider, request_id = ?request.id, method = %method, error_kind = "connect", "WebSocket endpoint connection failed");
return std::result::Result::Err(ks_core::Error::ws(format!( return std::result::Result::Err(ks_core::Error::ws(format!(
"cannot connect websocket endpoint '{}': {error}", "cannot connect websocket endpoint '{}'",
self.endpoint.name self.endpoint.name
))); )));
}, },
@@ -217,7 +233,7 @@ impl crate::WsClient {
match parse_result { match parse_result {
std::result::Result::Ok(response) => { std::result::Result::Ok(response) => {
if let crate::JsonRpcResponse::Error(error_response) = &response { if let crate::JsonRpcResponse::Error(error_response) = &response {
tracing::error!(target: crate::TRACING_TARGET, action = "execute_ws_json_rpc_once", endpoint_name = %self.endpoint.name, provider = %self.endpoint.provider, request_id = ?request.id, method = %method, response_kind = response.kind_name(), rpc_error_code = error_response.error.code, rpc_error_message = %error_response.error.message, "WebSocket JSON-RPC endpoint returned an RPC error"); tracing::error!(target: crate::TRACING_TARGET, action = "execute_ws_json_rpc_once", endpoint_name = %self.endpoint.name, provider = %self.endpoint.provider, request_id = ?request.id, method = %method, response_kind = response.kind_name(), rpc_error_code = error_response.error.code, "WebSocket JSON-RPC endpoint returned an RPC error");
} else { } else {
tracing::debug!(target: crate::TRACING_TARGET, action = "execute_ws_json_rpc_once", endpoint_name = %self.endpoint.name, provider = %self.endpoint.provider, request_id = ?request.id, method = %method, response_kind = response.kind_name(), "one-shot WebSocket JSON-RPC request completed"); tracing::debug!(target: crate::TRACING_TARGET, action = "execute_ws_json_rpc_once", endpoint_name = %self.endpoint.name, provider = %self.endpoint.provider, request_id = ?request.id, method = %method, response_kind = response.kind_name(), "one-shot WebSocket JSON-RPC request completed");
} }
@@ -334,6 +350,26 @@ mod tests {
assert!(client.can_handle("ws_any", "logs_subscribe_mentions")); assert!(client.can_handle("ws_any", "logs_subscribe_mentions"));
} }
#[test]
fn debug_output_omits_endpoint_url() {
let mut endpoint = endpoint(true);
endpoint.url = "wss://WS-SECRET-CANARY.invalid/?api-key=secret".to_string();
let client = match crate::WsClient::new(endpoint) {
std::result::Result::Ok(client) => client,
std::result::Result::Err(error) => panic!("client creation failed: {error}"),
};
let rendered = format!("{client:?}");
assert!(rendered.contains("<redacted>"));
assert!(!rendered.contains("WS-SECRET-CANARY"));
assert!(!rendered.contains("api-key=secret"));
let snapshot = client.snapshot();
let snapshot_rendered = format!("{snapshot:?}");
assert!(!snapshot_rendered.contains("endpoint_url"));
assert!(!snapshot_rendered.contains("endpointUrl"));
assert!(!snapshot_rendered.contains("WS-SECRET-CANARY"));
assert!(!snapshot_rendered.contains("api-key=secret"));
}
#[test] #[test]
fn snapshot_preserves_endpoint_metadata() { fn snapshot_preserves_endpoint_metadata() {
let client = match crate::WsClient::new(endpoint(true)) { let client = match crate::WsClient::new(endpoint(true)) {
@@ -343,7 +379,6 @@ mod tests {
let snapshot = client.snapshot(); let snapshot = client.snapshot();
assert_eq!(snapshot.endpoint_name, "ws_a"); assert_eq!(snapshot.endpoint_name, "ws_a");
assert_eq!(snapshot.provider, "test"); assert_eq!(snapshot.provider, "test");
assert_eq!(snapshot.endpoint_url, "wss://example.invalid");
assert_eq!(snapshot.roles.len(), 3); assert_eq!(snapshot.roles.len(), 3);
} }

View File

@@ -1,5 +1,5 @@
// file: ks-onchain-transport/src/ws_pool.rs // file: ks-onchain-transport/src/ws_pool.rs
// version: 8 // version: 9
//! WebSocket endpoint pool and role-based routing. //! WebSocket endpoint pool and role-based routing.
@@ -53,7 +53,7 @@ impl crate::WsEndpointPool {
}); });
} }
/// Returns a serializable snapshot of every endpoint in the pool. /// Returns backend-only endpoint metadata without resolved URL material.
pub fn snapshot(&self) -> std::vec::Vec<crate::WsPoolClientSnapshot> { pub fn snapshot(&self) -> std::vec::Vec<crate::WsPoolClientSnapshot> {
let mut snapshots = std::vec::Vec::new(); let mut snapshots = std::vec::Vec::new();
for client in &self.clients { for client in &self.clients {

View File

@@ -1,5 +1,5 @@
// file: ks-onchain-transport/src/ws_session.rs // file: ks-onchain-transport/src/ws_session.rs
// version: 6 // version: 9
//! Persistent multiplexed WebSocket session with bounded reconnect and resubscription. //! Persistent multiplexed WebSocket session with bounded reconnect and resubscription.
@@ -100,15 +100,12 @@ pub struct WsSubscriptionSnapshot {
} }
/// Current snapshot of one persistent WebSocket session. /// Current snapshot of one persistent WebSocket session.
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] #[derive(Clone, Eq, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct WsSessionSnapshot { pub struct WsSessionSnapshot {
/// Endpoint name. /// Endpoint name.
pub endpoint_name: std::string::String, pub endpoint_name: std::string::String,
/// Provider name. /// Provider name.
pub provider: std::string::String, pub provider: std::string::String,
/// Endpoint URL.
pub endpoint_url: std::string::String,
/// Current lifecycle state. /// Current lifecycle state.
pub state: crate::WsSessionState, pub state: crate::WsSessionState,
/// Number of successful reconnects since session creation. /// Number of successful reconnects since session creation.
@@ -119,6 +116,20 @@ pub struct WsSessionSnapshot {
pub subscriptions: std::vec::Vec<crate::WsSubscriptionSnapshot>, pub subscriptions: std::vec::Vec<crate::WsSubscriptionSnapshot>,
} }
impl std::fmt::Debug for crate::WsSessionSnapshot {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
return formatter
.debug_struct("WsSessionSnapshot")
.field("endpoint_name", &self.endpoint_name)
.field("provider", &self.provider)
.field("state", &self.state)
.field("reconnect_count", &self.reconnect_count)
.field("capabilities", &self.capabilities)
.field("subscriptions", &self.subscriptions)
.finish();
}
}
/// Successful subscription acknowledgement. /// Successful subscription acknowledgement.
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct WsSubscriptionAck { pub struct WsSubscriptionAck {
@@ -202,7 +213,6 @@ impl std::fmt::Debug for crate::WsSession {
.debug_struct("WsSession") .debug_struct("WsSession")
.field("endpoint_name", &self.endpoint.name) .field("endpoint_name", &self.endpoint.name)
.field("provider", &self.endpoint.provider) .field("provider", &self.endpoint.provider)
.field("endpoint_url", &self.endpoint.url)
.finish_non_exhaustive(); .finish_non_exhaustive();
} }
} }
@@ -238,7 +248,6 @@ impl crate::WsSession {
let snapshot = std::sync::Arc::new(tokio::sync::RwLock::new(crate::WsSessionSnapshot { let snapshot = std::sync::Arc::new(tokio::sync::RwLock::new(crate::WsSessionSnapshot {
endpoint_name: endpoint.name.clone(), endpoint_name: endpoint.name.clone(),
provider: endpoint.provider.clone(), provider: endpoint.provider.clone(),
endpoint_url: endpoint.url.clone(),
state: crate::WsSessionState::Connected, state: crate::WsSessionState::Connected,
reconnect_count: 0, reconnect_count: 0,
capabilities: initial_capabilities, capabilities: initial_capabilities,
@@ -788,10 +797,8 @@ impl WsSessionRuntime {
async fn handle_error_response(&mut self, error_response: crate::JsonRpcErrorResponse) { async fn handle_error_response(&mut self, error_response: crate::JsonRpcErrorResponse) {
let request_id = error_response.id.as_u64(); let request_id = error_response.id.as_u64();
let error = ks_core::Error::ws(format!( let error =
"WebSocket JSON-RPC error {}: {}", ks_core::Error::ws(format!("WebSocket JSON-RPC error {}", error_response.error.code));
error_response.error.code, error_response.error.message
));
if let std::option::Option::Some(request_id) = request_id { if let std::option::Option::Some(request_id) = request_id {
if let std::option::Option::Some(pending) = self.pending.remove(&request_id) { if let std::option::Option::Some(pending) = self.pending.remove(&request_id) {
match pending { match pending {
@@ -852,10 +859,8 @@ impl WsSessionRuntime {
self.emit_diagnostic( self.emit_diagnostic(
"ws_capability_disabled", "ws_capability_disabled",
format!( format!(
"endpoint '{}' rejected unstable method '{method}'; capability disabled for this session: JSON-RPC {} {}", "endpoint '{}' rejected unstable method '{method}'; capability disabled for this session: JSON-RPC {}",
self.client.endpoint_name(), self.client.endpoint_name(), error_response.error.code
error_response.error.code,
error_response.error.message
), ),
); );
} }
@@ -1180,8 +1185,8 @@ async fn connect_stream(client: &crate::WsClient) -> ks_core::Result<WsStream> {
}; };
return match connect_result { return match connect_result {
std::result::Result::Ok((stream, _response)) => std::result::Result::Ok(stream), std::result::Result::Ok((stream, _response)) => std::result::Result::Ok(stream),
std::result::Result::Err(error) => std::result::Result::Err(ks_core::Error::ws(format!( std::result::Result::Err(_error) => std::result::Result::Err(ks_core::Error::ws(format!(
"cannot connect WebSocket endpoint '{}': {error}", "cannot connect WebSocket endpoint '{}'",
client.endpoint_name() client.endpoint_name()
))), ))),
}; };
@@ -1281,6 +1286,21 @@ mod tests {
); );
} }
#[test]
fn session_snapshot_debug_contains_no_endpoint_url_field() {
let snapshot = crate::WsSessionSnapshot {
endpoint_name: "helius".to_string(),
provider: "helius".to_string(),
state: crate::WsSessionState::Connected,
reconnect_count: 0,
capabilities: crate::StandardWsCapabilities::default(),
subscriptions: std::vec::Vec::new(),
};
let rendered = format!("{snapshot:?}");
assert!(!rendered.contains("endpoint_url"));
assert!(!rendered.contains("endpointUrl"));
}
#[tokio::test] #[tokio::test]
async fn persistent_session_multiplexes_notification_and_explicit_unsubscribe() { async fn persistent_session_multiplexes_notification_and_explicit_unsubscribe() {
let listener = match tokio::net::TcpListener::bind("127.0.0.1:0").await { let listener = match tokio::net::TcpListener::bind("127.0.0.1:0").await {

View File

@@ -32,17 +32,17 @@ Cette orientation générale est aussi conservée dans `docs/IDEA_REMINDERS.md`.
Les crates suivantes sont des bibliothèques ou composants de support Solana généralistes et sont donc candidates au renommage direct en `0.5.1` : Les crates suivantes sont des bibliothèques ou composants de support Solana généralistes et sont donc candidates au renommage direct en `0.5.1` :
| Actuel | Cible de travail | | Actuel | Cible de travail |
|---|---| |------------------------------|------------------------------------------------------------------------------------------|
| `kb-core` | `ks-core` | | `kb-core` | `ks-core` |
| `kb-config` | `ks-config` | | `kb-config` | `ks-config` |
| `kb-lib` | `ks-lib` | | `kb-lib` | `ks-lib` |
| `kb-logging` | `ks-logging` | | `kb-logging` | `ks-logging` |
| `kb-program-ids` | `ks-program-ids` | | `kb-program-ids` | `ks-program-ids` |
| `kb-pipeline` | `ks-pipeline` | | `kb-pipeline` | `ks-pipeline` |
| `kb-onchain-transport` | `ks-onchain-transport` | | `kb-onchain-transport` | `ks-onchain-transport` |
| `kb-store` | `ks-store` | | `kb-store` | `ks-store` |
| `kb-wallet` | `ks-wallet` | | `kb-wallet` | `ks-wallet` |
| `kb-pipeline-demo-scenarios` | `ks-pipeline-demo-scenarios` à confirmer comme composant de validation Khadhroony Solana | | `kb-pipeline-demo-scenarios` | `ks-pipeline-demo-scenarios` à confirmer comme composant de validation Khadhroony Solana |
`kb-app-demo-desktop` reste hors de cette substitution automatique : c'est une application Khadhroony Bot, même si son rôle courant est surtout de tester les composants `ks-*`. `kb-app-demo-desktop` reste hors de cette substitution automatique : c'est une application Khadhroony Bot, même si son rôle courant est surtout de tester les composants `ks-*`.
@@ -121,13 +121,13 @@ En revanche :
Le contrat `0.5.3` doit donc imposer la distinction suivante : Le contrat `0.5.3` doit donc imposer la distinction suivante :
| Dimension | Sens | | Dimension | Sens |
|---|---| |-------------------------------------------------|----------------------------------------------------------|
| `slot` | ordre/position Solana ; jamais assimilé à un temps civil | | `slot` | ordre/position Solana ; jamais assimilé à un temps civil |
| `block_time` | temps on-chain Unix optionnel observé depuis le cluster | | `block_time` | temps on-chain Unix optionnel observé depuis le cluster |
| `detected_at` / `received_at` / `normalized_at` | temps locaux d'acquisition | | `detected_at` / `received_at` / `normalized_at` | temps locaux d'acquisition |
| `persisted_at` | instant d'écriture de l'observation | | `persisted_at` | instant d'écriture de l'observation |
| `created_at` / `updated_at` | cycle de vie de la ligne SQL | | `created_at` / `updated_at` | cycle de vie de la ligne SQL |
Une requête de prix ou série temporelle ne doit jamais prendre `created_at` comme substitut implicite de `block_time`. Une requête de prix ou série temporelle ne doit jamais prendre `created_at` comme substitut implicite de `block_time`.
@@ -211,16 +211,16 @@ L'inventaire statique de `kb-lib` trouve :
Les huit exécuteurs actifs sont : Les huit exécuteurs actifs sont :
| Exécuteur actif | Scénario réutilisable actuel | Qualification | | Exécuteur actif | Scénario réutilisable actuel | Qualification |
|---|---|---| |------------------------------|------------------------------|-----------------------------------------------------------------|
| Solana Core | oui | plusieurs parcours Devnet ; matrice native existante | | Solana Core | oui | plusieurs parcours Devnet ; matrice native existante |
| SPL Memo v4 | oui | Devnet ; v1/v3 restent decode-only | | SPL Memo v4 | oui | Devnet ; v1/v3 restent decode-only |
| SPL Associated Token Account | oui | scénario Devnet | | SPL Associated Token Account | oui | scénario Devnet |
| SPL Token classique | oui | scénarios et lifecycle Devnet | | SPL Token classique | oui | scénarios et lifecycle Devnet |
| SPL Token-2022 | oui | scénarios Devnet et campagne Token Metadata | | SPL Token-2022 | oui | scénarios Devnet et campagne Token Metadata |
| SPL ElGamal registry | non | implémenté + synthétique seulement ; preuve réseau indisponible | | SPL ElGamal registry | non | implémenté + synthétique seulement ; preuve réseau indisponible |
| Metaplex Token Metadata | oui | matrice `15 confirmed / 5 unavailable` | | Metaplex Token Metadata | oui | matrice `15 confirmed / 5 unavailable` |
| Solana Program Metadata | oui | 9 opérations confirmées Devnet | | Solana Program Metadata | oui | 9 opérations confirmées Devnet |
Le seul exécuteur actif sans scénario réseau réutilisable est donc ElGamal, et son absence est **`unavailable`/report conditionnel**, pas `implement`, tant qu'une nouvelle possibilité de preuve n'existe pas. Le seul exécuteur actif sans scénario réseau réutilisable est donc ElGamal, et son absence est **`unavailable`/report conditionnel**, pas `implement`, tant qu'une nouvelle possibilité de preuve n'existe pas.
@@ -284,18 +284,18 @@ La future matrice transversale doit comparer uniquement les **surfaces actives o
Pour chaque capacité, enregistrer : Pour chaque capacité, enregistrer :
| Dimension | Valeur attendue | | Dimension | Valeur attendue |
|---|---| |-------------------|---------------------------------------------------------------------------------------|
| decoder | actif / réservé / absent | | decoder | actif / réservé / absent |
| materializer | actif / state-only / réservé / non applicable | | materializer | actif / state-only / réservé / non applicable |
| executor | actif / decode-only / deprecated / réservé / absent | | executor | actif / decode-only / deprecated / réservé / absent |
| synthetic | présent / absent / non applicable | | synthetic | présent / absent / non applicable |
| reusable scenario | présent / absent / unavailable / non applicable | | reusable scenario | présent / absent / unavailable / non applicable |
| simulation | prouvée / non exécutée / unavailable | | simulation | prouvée / non exécutée / unavailable |
| submission | confirmed / non exécutée / unsafe / unavailable | | submission | confirmed / non exécutée / unsafe / unavailable |
| postcondition | stateful / replay/materialization / non applicable | | postcondition | stateful / replay/materialization / non applicable |
| desktop | adaptateur / logique réutilisable résiduelle / absent | | desktop | adaptateur / logique réutilisable résiduelle / absent |
| final status | `implement`, `decode-only`, `deprecated`, `unavailable`, `not-applicable`, `deferred` | | final status | `implement`, `decode-only`, `deprecated`, `unavailable`, `not-applicable`, `deferred` |
### Classification actuelle de départ ### Classification actuelle de départ

View File

@@ -1,5 +1,5 @@
<!-- file: prompts/030_v0_5_1_khadhroony_solana_namespace_and_config.md --> <!-- file: prompts/030_v0_5_1_khadhroony_solana_namespace_and_config.md -->
<!-- version: 5 --> <!-- version: 6 -->
# Khadhroony Bot3 — `0.5.1` — migration Khadhroony Solana et configuration sûre # Khadhroony Bot3 — `0.5.1` — migration Khadhroony Solana et configuration sûre
@@ -351,11 +351,12 @@ Le détail doit être confirmé par `0.5.1-pre.001`, mais la trajectoire cible e
### `0.5.1-pre.008` — runtime/public, camouflage et Tauri sûr ### `0.5.1-pre.008` — runtime/public, camouflage et Tauri sûr
- séparer source/runtime/public/diagnostic ; - **réalisé** : rendre les contrats `ks-config` source/runtime sensibles backend-only, sans `Serialize`/`Debug` ;
- propager la sensibilité des valeurs composées ; - **réalisé** : rendre `application` opaque pour `ks-config` et valider le fragment desktop avec son schéma propriétaire ;
- supprimer les payloads de config complète ; - **réalisé** : remplacer l'exposition `AppConfig/ProfileConfig` par des DTO public/diagnostic construits explicitement ;
- diagnostics sûrs et camouflage systématique ; - **réalisé** : propager `Secret > Internal > Public` aux chaînes composées et supprimer l'écho de valeurs rejetées dans les erreurs de validation ;
- tests de non-divulgation et TS-RS aligné. - **réalisé** : retirer TS-RS et les bindings générés de `ks-config`/`ks-lib`, avec audit empêchant leur réintroduction implicite ;
- **réalisé** : ajouter les canaris de non-divulgation et le test d'API externe de sensibilité.
### `0.5.1-pre.009` — réconciliation et clôture ### `0.5.1-pre.009` — réconciliation et clôture

View File

@@ -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: 23 # version: 28
"""Audit mechanically verifiable rules specific to khadhroony-bot3.""" """Audit mechanically verifiable rules specific to khadhroony-bot3."""
@@ -915,13 +915,13 @@ def audit_environment_namespaces(root: pathlib.Path) -> list[Violation]:
"config/store.config.json", "config/store.config.json",
"config/wallet.config.json", "config/wallet.config.json",
"config/execution.config.json", "config/execution.config.json",
"config/example.kb-app-demo-desktop.default.config.json", "config/exemples/example.kb-app-demo-desktop.default.config.json",
"config/example.logging.config.json", "config/exemples/example.logging.config.json",
"config/example.transport.config.json", "config/exemples/example.transport.config.json",
"config/example.listeners.config.json", "config/exemples/example.listeners.config.json",
"config/example.store.config.json", "config/exemples/example.store.config.json",
"config/example.wallet.config.json", "config/exemples/example.wallet.config.json",
"config/example.execution.config.json", "config/exemples/example.execution.config.json",
]: ]:
config = root / relative config = root / relative
if not config.is_file(): if not config.is_file():
@@ -970,14 +970,15 @@ def audit_configuration_split(root: pathlib.Path) -> list[Violation]:
"config/store.config.json", "config/store.config.json",
"config/wallet.config.json", "config/wallet.config.json",
"config/execution.config.json", "config/execution.config.json",
"config/example.kb-app-demo-desktop.default.config.json", "config/exemples/example.kb-app-demo-desktop.default.config.json",
"config/example.logging.config.json", "config/exemples/example.logging.config.json",
"config/example.transport.config.json", "config/exemples/example.transport.config.json",
"config/example.listeners.config.json", "config/exemples/example.listeners.config.json",
"config/example.store.config.json", "config/exemples/example.store.config.json",
"config/example.wallet.config.json", "config/exemples/example.wallet.config.json",
"config/example.execution.config.json", "config/exemples/example.execution.config.json",
"config/schemas/composition.config.schema.json", "config/schemas/composition.config.schema.json",
"config/schemas/kb-app-demo-desktop.application.config.schema.json",
"config/schemas/logging.config.schema.json", "config/schemas/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",
@@ -1062,6 +1063,13 @@ def audit_configuration_split(root: pathlib.Path) -> list[Violation]:
"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/example.kb-app-demo-desktop.default.config.json",
"config/example.logging.config.json",
"config/example.transport.config.json",
"config/example.listeners.config.json",
"config/example.store.config.json",
"config/example.wallet.config.json",
"config/example.execution.config.json",
"config/schema.config.json", "config/schema.config.json",
"config/ks-pipeline-demo-scenarios.default.config.json", "config/ks-pipeline-demo-scenarios.default.config.json",
] ]
@@ -1069,9 +1077,238 @@ def audit_configuration_split(root: pathlib.Path) -> list[Violation]:
if not (root / relative).exists(): if not (root / relative).exists():
continue continue
violations.append(Violation("KH_CFG007", relative, 1, "legacy or unnecessary configuration composition file must be removed")) violations.append(Violation("KH_CFG007", relative, 1, "legacy or unnecessary configuration composition file must be removed"))
protected_runtime_files = [
"ks-config/src/composition.rs",
"ks-config/src/execution.rs",
"ks-config/src/listeners.rs",
"ks-config/src/settings.rs",
"ks-config/src/store.rs",
"ks-config/src/transport.rs",
"ks-config/src/wallet.rs",
]
for relative in protected_runtime_files:
path = root / relative
if not path.is_file():
continue
for index, line in enumerate(path.read_text(encoding="utf-8").splitlines(), 1):
if "#[derive(" not in line:
continue
if "serde::Serialize" in line:
violations.append(Violation("KH_CFG013", relative, index, "resolved/source configuration contracts must not derive Serialize because they may contain resolved secrets"))
if re.search(r"\bDebug\b", line):
violations.append(Violation("KH_CFG014", relative, index, "resolved/source configuration contracts must not derive Debug because they may contain resolved secrets or internal values"))
return violations return violations
def audit_ts_rs_boundaries(root: pathlib.Path) -> list[Violation]:
"""Keep TS-RS generation at explicit application boundaries."""
violations: list[Violation] = []
for cargo in sorted(root.glob("ks-*/Cargo.toml")):
crate = cargo.parent
data = tomllib.loads(cargo.read_text(encoding="utf-8"))
for section in ["dependencies", "dev-dependencies", "build-dependencies"]:
dependencies = data.get(section, {})
if "ts-rs" in dependencies:
violations.append(
Violation(
"KH_TS001",
cargo.relative_to(root).as_posix(),
1,
"generic ks-* crates must not depend on ts-rs without an explicit approved external TypeScript contract",
)
)
for path in sorted((crate / "src").rglob("*.rs")):
relative = path.relative_to(root).as_posix()
for index, line in enumerate(path.read_text(encoding="utf-8").splitlines(), 1):
if "ts_rs::" in line or "#[ts(" in line or re.search(r"#\[derive\([^]]*\bTS\b", line):
violations.append(
Violation(
"KH_TS002",
relative,
index,
"TS-RS derives/attributes belong to application DTO boundaries, not generic ks-* runtime types",
)
)
for generated in [crate / "frontend/ts/bindings", crate / "bindings"]:
if generated.exists():
violations.append(
Violation(
"KH_TS003",
generated.relative_to(root).as_posix(),
1,
"generic ks-* crates must not own generated TypeScript bindings",
)
)
desktop = root / "kb-app-demo-desktop/src"
if desktop.exists():
field_pattern = re.compile(r"^\s*pub\(crate\)\s+[A-Za-z0-9_]+:\s+ks_[a-z0-9_]+::")
for path in sorted(desktop.rglob("*.rs")):
relative = path.relative_to(root).as_posix()
for index, line in enumerate(path.read_text(encoding="utf-8").splitlines(), 1):
if field_pattern.search(line):
violations.append(
Violation(
"KH_TS004",
relative,
index,
"desktop DTO/state fields that cross application boundaries must use application-owned wrappers rather than direct ks-* field types",
)
)
tauri_path = root / "kb-app-demo-desktop/src/tauri.rs"
if tauri_path.is_file():
lines = tauri_path.read_text(encoding="utf-8").splitlines()
for index, line in enumerate(lines):
if line.strip() != "#[tauri::command]":
continue
signature_lines: list[str] = []
for candidate in lines[index + 1 : index + 20]:
signature_lines.append(candidate)
if "{" in candidate:
break
signature = "\n".join(signature_lines)
if re.search(r"->[^\{]*\bks_[a-z0-9_]+::", signature, re.DOTALL):
violations.append(
Violation(
"KH_TS005",
"kb-app-demo-desktop/src/tauri.rs",
index + 1,
"Tauri command return types must use desktop-owned DTO wrappers instead of direct ks-* contracts",
)
)
return violations
def audit_sensitive_transport_surfaces(root: pathlib.Path) -> list[Violation]:
"""Keep resolved transport URLs and other internal paths away from public boundaries."""
violations: list[Violation] = []
protected = {
"ks-onchain-transport/src/http_client.rs": "HttpPoolClientSnapshot",
"ks-onchain-transport/src/ws_client.rs": "WsPoolClientSnapshot",
"ks-onchain-transport/src/ws_session.rs": "WsSessionSnapshot",
}
for relative, struct_name in protected.items():
path = root / relative
if not path.is_file():
continue
text = path.read_text(encoding="utf-8")
struct_match = re.search(
rf"pub struct {struct_name}\s*\{{(?P<body>.*?)\n\}}",
text,
re.DOTALL,
)
if struct_match is not None and re.search(r"\bendpoint_url\s*:", struct_match.group("body")):
violations.append(
Violation(
"KH_SEC001",
relative,
text[: struct_match.start()].count("\n") + 1,
f"backend snapshot `{struct_name}` must not retain a resolved endpoint URL",
)
)
derive_match = re.search(
rf"#\[derive\(([^)]*)\)\]\s*(?:#\[[^\]]+\]\s*)*pub struct {struct_name}\b",
text,
re.DOTALL,
)
if derive_match is not None and "serde::Serialize" in derive_match.group(1):
violations.append(
Violation(
"KH_SEC002",
relative,
text[: derive_match.start()].count("\n") + 1,
f"backend transport snapshot `{struct_name}` must not derive Serialize",
)
)
desktop_root = root / "kb-app-demo-desktop/src"
if desktop_root.is_dir():
forbidden_fields = {
"endpoint_url": "resolved endpoint URLs must not cross the Tauri boundary",
"fixture_path": "wallet-derived fixture paths must not cross functional Tauri payloads",
}
field_pattern = re.compile(r"pub\(crate\)\s+([a-z][a-z0-9_]*):")
for path in sorted(desktop_root.rglob("*.rs")):
relative = path.relative_to(root).as_posix()
for index, line in enumerate(path.read_text(encoding="utf-8").splitlines(), 1):
match = field_pattern.search(line)
if match is None or match.group(1) not in forbidden_fields:
continue
violations.append(
Violation(
"KH_SEC003",
relative,
index,
forbidden_fields[match.group(1)],
)
)
transport_root = root / "ks-onchain-transport/src"
if transport_root.is_dir():
forbidden_fragments = {
"rpc_error_message = %error_response.error.message": "remote JSON-RPC messages must not be copied into transport logs",
'.field("endpoint_url", &self.endpoint.url)': "transport Debug must not expose a resolved endpoint URL",
"pub fn endpoint_url(&self) -> &str": "resolved endpoint URL getters must remain crate-private",
}
for path in sorted(transport_root.rglob("*.rs")):
relative = path.relative_to(root).as_posix()
text = path.read_text(encoding="utf-8")
for fragment, message in forbidden_fragments.items():
start = 0
while True:
offset = text.find(fragment, start)
if offset < 0:
break
violations.append(
Violation(
"KH_SEC004",
relative,
text[:offset].count("\n") + 1,
message,
)
)
start = offset + len(fragment)
concrete_api_key = re.compile(r"api-key=([0-9a-fA-F]{8,}(?:-[0-9a-fA-F]+)*)")
secret_scan_roots = [root / "config", root / "ks-config", root / "ks-logging", root / "ks-onchain-transport", root / "kb-app-demo-desktop"]
for scan_root in secret_scan_roots:
if not scan_root.is_dir():
continue
for path in sorted(scan_root.rglob("*")):
if not path.is_file() or "frontend/ts/bindings" in path.as_posix():
continue
if path.suffix not in {".rs", ".json", ".md", ".ts", ".html"}:
continue
text = path.read_text(encoding="utf-8", errors="ignore")
match = concrete_api_key.search(text)
if match is None:
continue
violations.append(
Violation(
"KH_SEC006",
path.relative_to(root).as_posix(),
text[: match.start()].count("\n") + 1,
"concrete API-key material is forbidden in active source, configuration, documentation and tests",
)
)
http_client_path = root / "ks-onchain-transport/src/http_client.rs"
if http_client_path.is_file():
text = http_client_path.read_text(encoding="utf-8")
forbidden_http = [
"retry_index, text\n",
"error_response.error.message\n ))",
]
for fragment in forbidden_http:
offset = text.find(fragment)
if offset >= 0:
violations.append(
Violation(
"KH_SEC005",
"ks-onchain-transport/src/http_client.rs",
text[:offset].count("\n") + 1,
"HTTP transport errors must not copy remote response bodies or JSON-RPC messages",
)
)
return violations
def audit_wincode_resolution(root: pathlib.Path) -> list[Violation]: def audit_wincode_resolution(root: pathlib.Path) -> list[Violation]:
"""Require the currently compatible Solana wincode dependency family.""" """Require the currently compatible Solana wincode dependency family."""
@@ -1155,7 +1392,9 @@ def main() -> int:
+audit_private_ks_lib_paths_in_active_docs(root) +audit_private_ks_lib_paths_in_active_docs(root)
+audit_solana_types(root) +audit_solana_types(root)
+audit_environment_namespaces(root) +audit_environment_namespaces(root)
+audit_ts_rs_boundaries(root)
+audit_configuration_split(root) +audit_configuration_split(root)
+audit_sensitive_transport_surfaces(root)
+audit_wincode_resolution(root) +audit_wincode_resolution(root)
) )
violations.sort(key=lambda item: (item.code, item.path, item.line, item.message)) violations.sort(key=lambda item: (item.code, item.path, item.line, item.message))

View File

@@ -3,10 +3,6 @@
"profiles": [ "profiles": [
{ {
"name": "local_devnet", "name": "local_devnet",
"app": {
"name": "khadhroony-bot3",
"environment": "development"
},
"database": { "database": {
"enabled": true, "enabled": true,
"backend": "postgres", "backend": "postgres",
@@ -225,10 +221,6 @@
"devnet_send_enabled": true, "devnet_send_enabled": true,
"testnet_send_enabled": false, "testnet_send_enabled": false,
"mainnet_send_enabled": false "mainnet_send_enabled": false
},
"demo": {
"live_demo_enabled": true,
"trading_demo_enabled": false
} }
} }
] ]

View File

@@ -3,10 +3,6 @@
"profiles": [ "profiles": [
{ {
"name": "local_devnet", "name": "local_devnet",
"app": {
"name": "khadhroony-bot3",
"environment": "development"
},
"database": { "database": {
"enabled": true, "enabled": true,
"backend": "postgres", "backend": "postgres",
@@ -225,18 +221,10 @@
"devnet_send_enabled": true, "devnet_send_enabled": true,
"testnet_send_enabled": false, "testnet_send_enabled": false,
"mainnet_send_enabled": false "mainnet_send_enabled": false
},
"demo": {
"live_demo_enabled": true,
"trading_demo_enabled": false
} }
}, },
{ {
"name": "mainnet_research", "name": "mainnet_research",
"app": {
"name": "khadhroony-bot3",
"environment": "research"
},
"database": { "database": {
"enabled": true, "enabled": true,
"backend": "postgres", "backend": "postgres",
@@ -655,18 +643,10 @@
"devnet_send_enabled": false, "devnet_send_enabled": false,
"testnet_send_enabled": false, "testnet_send_enabled": false,
"mainnet_send_enabled": false "mainnet_send_enabled": false
},
"demo": {
"live_demo_enabled": true,
"trading_demo_enabled": false
} }
}, },
{ {
"name": "mainnet", "name": "mainnet",
"app": {
"name": "khadhroony-bot3",
"environment": "mainnet"
},
"database": { "database": {
"enabled": true, "enabled": true,
"backend": "postgres", "backend": "postgres",
@@ -1085,10 +1065,6 @@
"devnet_send_enabled": false, "devnet_send_enabled": false,
"testnet_send_enabled": false, "testnet_send_enabled": false,
"mainnet_send_enabled": false "mainnet_send_enabled": false
},
"demo": {
"live_demo_enabled": true,
"trading_demo_enabled": false
} }
} }
] ]