# Canot : documentation complète # Canot : les commandes Canot répète votre départ d'une plateforme de données, dans vos propres comptes. Une commande suffit : ```bash uvx canot drill SOURCE DESTINATION ``` `SOURCE` est la plateforme à quitter (Snowflake, Databricks, BigQuery), `DESTINATION` l'endroit où la copie est écrite. Les valeurs en `MY_…` sont à remplacer par les vôtres. Canot copie les tables en Apache Iceberg, compare tout, rejoue vos requêtes les plus coûteuses et écrit un dossier de preuve signé. Il ne vous envoie rien : pas de télémétrie. ```bash # Snowflake vers un dossier local uvx canot drill "snowflake://MY_ACCOUNT/MY_DATABASE?user=MY_USER" ./iceberg # Databricks vers un stockage S3 de votre choix uvx canot drill "databricks://MY_WORKSPACE.cloud.databricks.com/sql/1.0/warehouses/MY_WAREHOUSE_ID?catalog=MY_CATALOG" "s3://my-bucket/canot?endpoint=https://s3.internal:9000®ion=us-east-1" # BigQuery vers un hébergeur européen uvx canot drill "bigquery://MY_PROJECT/MY_DATASET?location=EU" scaleway:fr-par --bucket my-bucket ``` Si un connecteur manque, Canot affiche la commande exacte à lancer (par exemple `uvx --from "canot[bigquery]" canot drill …`). ## Les commandes | Commande | Ce qu'elle fait | |---|---| | `canot scan SOURCE` | Radiographie en lecture seule : inventaire, volumes, consommation, ce qui ne part pas, rejouabilité des requêtes. Écrit `scan.html` (à transmettre), `scan.json` et des CSV. Voir [scan](scan.md). | | `canot drill SOURCE DESTINATION` | Exercice de sortie complet : copie, comparaison table par table, rejeu des requêtes, dossier de preuve signé. | | `canot demo` | Démonstration de 30 secondes, sans compte ni données : un distributeur fictif et un exercice complet. | | `canot verify-evidence DOSSIER` | Vérifie la chaîne et la signature d'un dossier de preuve. `--expect-pub` refuse un dossier signé par une autre clé. | | `canot teardown DESTINATION` | Supprime les données copiées. | | `canot snowflake-setup` | Écrit le script SQL d'accès en lecture seule à faire relire et lancer par un administrateur. `--generate-key` crée la paire de clés. | | `canot bigquery-setup` | Écrit le script `gcloud` du rôle personnalisé en lecture seule. | | `canot init-tolerances` | Écrit un fichier de tolérances de comparaison à relire avec vous. | | `canot demo-data CHEMIN` | Crée une base DuckDB de démonstration. | | `canot mcp` | Serveur MCP : la documentation et l'explication de vos rapports, pour un agent. Voir [Avec un agent](#avec-un-agent-mcp). | Toutes les commandes acceptent `--debug` (trace complète en cas d'erreur). ## `canot drill` Options utiles : | Option | Rôle | |---|---| | `--bucket NOM` | Obligatoire avec un raccourci d'hébergeur (`scaleway:fr-par`, `ovh:gra`, `outscale:eu-west-2`, `ionos:eu-central-1`, `stackit:eu01`, `aws:eu-west-3`). | | `--catalog URI` | Catalogue Iceberg : `sqlite:///…`, ou `env:NOM_DE_VARIABLE` pour PostgreSQL (`canot[postgres]`). Par défaut : un fichier SQLite dans le dossier de sortie. | | `--out DOSSIER` | Où écrire le rapport et le dossier de preuve (défaut `canot-out`). | | `--tables SCHEMA.TABLE …` | Limiter à certaines tables. | | `--queries FICHIER.sql` | Rejouer vos propres requêtes. Par défaut : les plus coûteuses de l'historique de la plateforme. | | `--top N` | Nombre de requêtes de l'historique à rejouer (défaut 20). | | `--tolerances FICHIER.toml` | Tolérances de comparaison (arrondis, horodatages). | | `--credit-price USD` | Prix du crédit Snowflake de votre contrat. Sans lui : une fourchette du prix catalogue, datée. | | `--replay-max-gb N` | Taille maximale d'une table chargée en mémoire pour rejouer les requêtes (défaut 2). | | `--key CHEMIN` | Clé de signature (défaut `~/.canot/evidence.key`, ou la variable `CANOT_EVIDENCE_KEY_PATH`). | Résultat dans `--out` : `report.html` (lisible par un comité), `report.json`, le dossier de preuve (`evidence.jsonl`, `evidence.sig`, `evidence.pub`) et `relire.sql`. La signature atteste que le dossier n'a pas été modifié depuis sa signature. Elle n'atteste ni l'auteur ni la date. ## `canot scan` ```bash uvx canot scan "snowflake://MY_ACCOUNT/MY_DATABASE?user=MY_USER" --level 2 ``` `--level 1|2|3` (défaut 3) règle jusqu'où lire ; `--credit-price` remplace le prix catalogue ; `--include-sql-text` écrit aussi le SQL brut dans un fichier à part (jamais par défaut). Détail des lectures, des droits et des garanties : [scan](scan.md). ## Les sources | Source | Forme | Authentification | |---|---|---| | BigQuery | `bigquery://PROJET/dataset1,dataset2?location=EU` ; sans dataset : tous ceux du projet ; autre projet : `bigquery://FACTURATION/projet-donnees.dataset` | identifiants applicatifs Google (`gcloud auth application-default login`) | | Snowflake | `snowflake://COMPTE/BASE?user=…&warehouse=…&role=…&schemas=A,B` | paire de clés : variable `SNOWFLAKE_PRIVATE_KEY_PATH` (recommandé) ou `SNOWFLAKE_PASSWORD` | | Databricks | `databricks://HÔTE/CHEMIN_HTTP_DE_L_ENTREPÔT?catalog=…` | variable `DATABRICKS_TOKEN` (jeton en lecture) ; connecteur non encore testé sur un vrai compte | | DuckDB | `maison.duckdb` | aucune | ## Sortie en local ou on-prem Pas de cloud à l'arrivée : un dossier suffit. ```bash uvx canot drill "snowflake://MY_ACCOUNT/MY_DATABASE?user=MY_USER" ./iceberg ``` Canot écrit la copie au format Iceberg dans `./iceberg` et le dossier de preuve dans `canot-out/`. La copie ne dépend pas de l'outil : DuckDB, Spark ou Trino la lisent. Le fichier `relire.sql` donne les commandes DuckDB pour recompter chaque table : ```bash duckdb < canot-out/relire.sql ``` | Arrivée | Commande | |---|---| | Dossier local ou monté (disque, NAS, partage) | `canot drill SOURCE /chemin/vers/le/dossier` | | Stockage compatible S3 chez vous (MinIO, Ceph, NetApp…) | `canot drill SOURCE "s3://coffre/canot?endpoint=https://s3.interne:9000®ion=us-east-1"` | | Proxy d'entreprise, autorité de certification interne | ajoutez `&proxy=http://proxy.interne:3128` et `&ca=/chemin/ca.pem` à l'URL S3 | | Catalogue PostgreSQL | `--catalog env:CANOT_CATALOG_URI` (mot de passe dans la variable, jamais sur la ligne de commande) | Les clés du stockage passent par `CANOT_S3_ACCESS_KEY` et `CANOT_S3_SECRET_KEY` (ou la configuration `scw`). État de chaque cas (testé ou seulement branché) : [sur-site](sur-site.md). ## Les hébergeurs `scaleway:fr-par`, `ovh:gra`, `outscale:eu-west-2`, `ionos:eu-central-1`, `stackit:eu01`, `aws:eu-west-3`, avec `--bucket`. Vérifiez chaque point de terminaison dans la console de l'hébergeur. La qualification SecNumCloud, quand elle compte pour vous, dépend de l'offre précise : voir [cibles](cibles-ue.md). ## Avec un agent (MCP) Beaucoup d'équipes travaillent avec un agent. `canot mcp` lui donne la documentation de Canot et lui explique vos rapports, sans rien envoyer à personne : le serveur tourne sur votre machine. Installation dans Claude Code : ```bash claude mcp add canot -- uvx --from "canot[mcp]" canot mcp ``` Ou dans la configuration MCP de votre client (Claude Desktop, Cursor, VS Code…) : ```json { "mcpServers": { "canot": { "command": "uvx", "args": ["--from", "canot[mcp]", "canot", "mcp"] } } } ``` Outils exposés : | Outil | Ce qu'il fait | |---|---| | `commandes` | Liste les commandes et leurs options. | | `chercher_doc(requete)` | Cherche dans la documentation et rend les passages. | | `lire_doc(sujet)` | Rend une page entière : `commandes`, `scan`, `sur-site`, `cibles`, `ecarts`. | | `expliquer_scan(chemin)` | Lit un `scan.json` et le résume : constats, lectures non faites, chiffres estimés, ce qui est à examiner. | | `expliquer_rapport(chemin)` | Lit un `report.json` d'exercice : écarts, tables et requêtes en cause, causes connues du catalogue. | Les outils lisent des fichiers que vous désignez. Ils n'ouvrent aucune connexion à vos plateformes. Pour les agents qui lisent le web : [canot.io/llms.txt](https://canot.io/llms.txt) et [canot.io/llms-full.txt](https://canot.io/llms-full.txt). ## Pour aller plus loin - [scan](scan.md) : ce qui est lu, avec quels droits, et les garanties. - [sur-site](sur-site.md) : arrivée locale ou sur votre stockage. - [cibles](cibles-ue.md) : hébergeurs et pays. - [ecarts](ecarts.md) : ce que deux moteurs rendent différemment, mesuré. - Une aide pour l'exécuter sur votre périmètre : [canot.io](https://canot.io/#contact). --- # `canot scan` : ce qui est lu, avec quels droits Le scan produit `scan.html` (à transmettre), `scan.json` et des CSV. Il décrit votre plateforme ; il ne recommande ni de partir ni de rester. ## Garanties - **Lecture seule.** Chaque requête passe par une garde avant d'être envoyée : seuls `SELECT`, `SHOW`, `DESCRIBE` et `ALTER SESSION SET` sont admis. Tout le reste est refusé sans rien envoyer. Les fonctions `SYSTEM$` sont refusées aussi. - **Jamais le contenu des tables.** Le scan lit des métadonnées et l'historique d'exécution. Il ne demande aucun droit de lecture sur les données. - **Le SQL brut n'est pas écrit.** Les littéraux sont masqués (`WHERE email = ?`), les requêtes qui contiennent un mot de passe ou un secret sont comptées puis jetées. Le texte brut n'est écrit qu'avec `--include-sql-text`, dans `scan-sql-brut.json` (droits 0600), et le rapport le signale. - **Identités pseudonymisées.** Les utilisateurs et rôles de requêtes sont remplacés par un pseudonyme (HMAC, sel tiré à chaque scan et jamais écrit). Les e-mails, identifiants de connexion et adresses IP ne sont jamais lus. Les chemins de stockage sont réduits au nom du compartiment. - **Aucune lecture silencieusement perdue.** Chaque lecture est consignée « lu » ou « non lu » avec sa raison. Une valeur non lue reste vide, jamais 0. ## Niveaux (`--level`, 3 par défaut) | Niveau | Contenu | Droits en plus | |---|---|---| | 1 | Inventaire, types de colonnes, volumes, historique facturé, consommation, objets (vues, routines, tâches, stages, partages…), dépendances | métadonnées et consommation | | 2 | + formes de requêtes normalisées et leur rejouabilité, lectures et écritures par table, politiques | accès à l'historique des requêtes | | 3 | + rôles propriétaires, outils clients, types d'utilisateurs, capacités réservées | droits et sessions | Ce que vos droits ne permettent pas est consigné « non lu » dans le rapport (constat F12) : le scan lit ce qui lui est ouvert et dit ce qu'il n'a pas vu. ## Snowflake `canot snowflake-setup --generate-key --base MA_BASE` écrit le script à relire et à lancer par un administrateur. Il crée un rôle, un entrepôt XS qui se suspend après 60 s, un plafond de crédits, et accorde les rôles de base de Snowflake : | Rôle de base | Niveau | Sert à | |---|---|---| | `SNOWFLAKE.OBJECT_VIEWER` | 1 | tables, colonnes, vues, routines, tâches, stages, dépendances | | `SNOWFLAKE.USAGE_VIEWER` | 1 | volumes, crédits, historique, chargements | | `SNOWFLAKE.GOVERNANCE_VIEWER` | 2 | historique des requêtes, lectures par table, politiques, étiquettes | | `SNOWFLAKE.SECURITY_VIEWER` | 3 | rôles, utilisateurs (nom et type seulement), sessions | Jamais demandés : `IMPORTED PRIVILEGES`, `MANAGE GRANTS`, `IMPORT SHARE`, `USAGE` sur les fonctions, `READ` sur les stages. Limites connues : `ACCOUNT_USAGE` a une latence de 45 minutes à 3 heures (jusqu'à 2 jours pour l'historique de chargement). Le rapport affiche la latence de chaque lecture. `ACCESS_HISTORY` exige l'édition Enterprise ; sans elle, les lectures par table se déduisent de `TABLE_QUERY_PRUNING_HISTORY` avec une confiance moyenne. ## BigQuery `canot bigquery-setup --project MON_PROJET --member user:…` écrit le script `gcloud` d'un rôle personnalisé `canotScanner`. Permissions obligatoires : `bigquery.jobs.create`, `bigquery.jobs.listAll`, `bigquery.datasets.get`, `bigquery.tables.list`, `bigquery.tables.get`, `bigquery.tables.getIamPolicy`, `bigquery.routines.list`, `bigquery.routines.get`, `bigquery.models.list`, `bigquery.models.getMetadata`, `bigquery.reservations.list`, `bigquery.reservationAssignments.list`, `bigquery.capacityCommitments.list`, `bigquery.bireservations.get`, `bigquery.config.get`. Optionnelles : `bigquery.rowAccessPolicies.list`, `bigquery.transfers.get`, `bigquery.connections.list`, `bigquery.dataPolicies.list`. Jamais : `bigquery.tables.getData`, ni les rôles `bigquery.user` ou `jobUser`. Points de méthode : - Une requête `INFORMATION_SCHEMA` par région (les régions ne sont pas hiérarchiques : `EU` ne couvre pas `europe-west1`). BigQuery facture au moins 10 Mo par requête de ce type, sans cache : le scan plafonne chaque requête et fait un essai à blanc avant. - Les volumes viennent de `TABLE_STORAGE` quand elle est lisible, sinon de l'API `tables.get`. Canot ne modifie jamais le paramétrage du projet pour l'activer. - Sous une politique de lignes, `total_bytes_billed` est vide : le rapport le dit au lieu d'afficher 0. - Le palier gratuit est partagé par le compte de facturation : il n'est pas imputable par projet. ## Lire les chiffres Chaque chiffre porte sa nature : **mesuré** (lu tel quel), **calculé** (déduit de mesures, formule donnée), **estimé** (fourchette et hypothèses), **non lu** ou **non estimé** (avec la raison). Les prix sont des repères datés et sourcés ; passez `--credit-price` avec le prix de votre contrat pour les remplacer. Le coût de lecture et la durée d'une copie sont des estimations de planification : `canot drill` les remplace par des mesures. La rejouabilité mesure combien de formes de requêtes sont traduites vers DuckDB **et acceptées** par lui, sur un squelette vide typé à partir de vos colonnes. **Accepté ne veut pas dire résultat identique** : seul `canot drill` compare les résultats. Les requêtes sur des tables hors du périmètre lu, les requêtes dont la plateforme ne donne pas le texte et les requêtes de métadonnées sont comptées à part, hors taux. ## Ce que le scan ne voit pas Les lectures par l'API Storage Read et les requêtes lancées depuis d'autres projets (BigQuery), les consommateurs de partages et `RESULT_SCAN` (Snowflake), les traitements plus rares que la fenêtre lue (365 jours sur Snowflake, 90 à 180 jours sur BigQuery). La fenêtre réelle est affichée à côté de chaque constat. --- # Arrivée sur site Canot écrit la copie au format Apache Iceberg. L'arrivée peut être un dossier de votre réseau, ou un stockage compatible S3 que vous hébergez. | Destination | Commande | État | |---|---|---| | Dossier local ou monté (NAS, disque, partage) | `--to /chemin/vers/le/dossier` | testé | | Stockage compatible S3 hébergé chez vous | `--to "s3://coffre/canot?endpoint=https://s3.interne:9000®ion=us-east-1"` | testé sur un serveur S3 local (moto) | | Catalogue personnalisé (SQLite ailleurs) | `--catalog sqlite:///chemin/catalogue.db` | testé | | Catalogue PostgreSQL | `--catalog env:CANOT_CATALOG_URI` avec `pip install 'canot[postgres]'` | branché, pas encore testé contre un vrai PostgreSQL | | Proxy d'entreprise | `...?proxy=http://proxy.interne:3128` dans l'URL S3 | branché, pas encore testé | | Autorité de certification interne | `...?ca=/chemin/ca.pem` dans l'URL S3 (transmise par `AWS_CA_BUNDLE`) | branché, pas encore testé | Les clés du stockage passent par `CANOT_S3_ACCESS_KEY` et `CANOT_S3_SECRET_KEY` (ou la configuration `scw`). Un mot de passe de catalogue se met dans une variable d'environnement, jamais sur la ligne de commande : `--catalog env:NOM_DE_LA_VARIABLE`. ## Relire la copie sans Canot Chaque exercice écrit un fichier `relire.sql` à côté du rapport. Il contient les commandes DuckDB qui comptent les lignes de chaque table copiée : ```bash duckdb < canot-out/relire.sql ``` La copie ne dépend donc pas de l'outil : n'importe quel moteur qui lit Iceberg (DuckDB, Spark, Trino) peut la lire. Pour un stockage S3, créez d'abord un secret DuckDB avec vos clés (voir le fichier). ## Ce qui n'est pas pris en charge Écrire directement dans PostgreSQL, ClickHouse ou un autre moteur de base : la copie reste en Iceberg, que ces moteurs savent lire. Pas d'installation hors ligne documentée pour l'instant. ## Nettoyer ```bash canot teardown --to "s3://coffre/canot?endpoint=https://s3.interne:9000®ion=us-east-1" ``` --- # Cibles : hébergeurs souverains et pays Canot écrit sur tout stockage compatible S3. Raccourcis intégrés (`--to : --bucket `). **Vérifiez chaque point de terminaison dans la console de l'hébergeur avant usage : ils sont à confirmer.** | Raccourci | Hébergeur | Pays | Remarque | |---|---|---|---| | `scaleway:fr-par` (aussi `nl-ams`, `pl-waw`) | Scaleway | FR, NL, PL | pas qualifié SecNumCloud (à vérifier) | | `ovh:gra` | OVHcloud | FR | offre SNC qualifiée SecNumCloud (à vérifier sur la liste ANSSI) | | `outscale:eu-west-2` | Outscale | FR | qualification à renouveler fin 2026 (à vérifier) | | `ionos:eu-central-1` | IONOS | DE | | | `stackit:eu01` | STACKIT (groupe Schwarz) | DE | | | `aws:eu-west-3` | AWS | FR | pas un cloud souverain ; AWS European Sovereign Cloud existe aussi | ## Le même principe dans d'autres pays de l'UE Le Data Act s'applique dans toute l'UE : mêmes règles de changement de fournisseur (art. 23 à 31), mêmes frais à zéro à partir du 12 janvier 2027. Ce qui change d'un pays : le superviseur financier qui applique DORA, l'autorité qui contrôle le Data Act, et les exigences nationales de souveraineté. | Pays | Pourquoi c'est intéressant | Hébergeurs locaux | |---|---|---| | Allemagne | plus gros marché ; BaFin applique DORA ; forte culture souveraineté | STACKIT, IONOS, Open Telekom Cloud | | Pays-Bas | place financière, beaucoup de scale-ups sur Snowflake | Leaseweb | | Italie | banques et assurances nombreuses | Aruba, Seeweb | | Espagne | banques ; Data Act via la CNMC | IONOS, Arsys | | Pologne, pays nordiques | fort usage du cloud, hébergeurs locaux | Scaleway Varsovie, UpCloud | À confirmer avant de vendre : l'autorité compétente désignée dans chaque pays et les listes de qualification nationales (équivalents de SecNumCloud : C5 en Allemagne, ACN en Italie, ENS en Espagne).