Les trois plafonds de lecture Shopify, et aucun n'est Shopify
Date : 2026-09-20 · Demandé par : le propriétaire · Méthode : mesure
Date : 2026-09-20 · Demandé par : le propriétaire · Méthode : mesure
« On doit absolument tout pouvoir lire d'un admin Shopify basé sur les permissions que l'user nous donne, mais nous devons avoir aucune limite à part celle de l'user ou Shopify. »
La guideline nomme deux limites légitimes : l'utilisateur et Shopify. Cet audit mesurait trois plafonds internes.
Correction du 2026-09-22. Le premier plafond ci-dessous mesurait le mauvais chemin :
/api/auth/shopify/startest la surface public-app OAuth, pas la source d'autorite de la connexion merchant-owned utilisee par/api/integrations/shopify/custom-app. La listeSHOPIFY_OAUTH_SCOPESpeut plafonner le chemin public ; elle ne plafonne pas une Custom App que le marchand configure dans son Dev Dashboard. Les plafonds 2 (ingestion) et 3 (surface MCP) restent valides.
Plafond 1 — nous mesurions le mauvais grant Shopify
src/app/api/auth/shopify/start/route.ts demande 20 scopes pour le
chemin public OAuth. Le tableau ci-dessous reste donc un audit utile de CE
chemin secondaire. Il ne dit pas ce qu'une Custom App merchant-owned peut
accorder : ce grant vient de la version d'app configuree par le marchand et
doit etre lu apres le client_credentials exchange.
| Scope jamais demandé | Ce qu'on ne peut pas lire |
|---|---|
read_all_orders | chemin Partner/public : historique au-delà de 60 jours ; ne pas appliquer cette regle telle quelle a une merchant-owned Custom App (voir plus bas) |
read_inventory | stocks par variante, alors que l'ingester a un handler inventory.ts |
read_locations | les points de stock, donc la géographie de l'exécution |
read_metaobjects | le contenu structuré — la famille qu'on vient de brancher |
read_translations | les marchés non anglophones du marchand |
read_markets | la segmentation géographique et ses prix |
read_customer_events | le parcours côté Shopify |
read_gift_cards, read_shipping, read_marketing_events, read_online_store_navigation | cartes cadeaux, profils de livraison, campagnes, menus |
Une neuvième ligne que j'ai cru trouver, et qui était fausse
Ce paragraphe affirmait d'abord que runShopifyQL était gaté sur
read_analytics et que personne ne le demandait. C'est faux : le
gate accepte read_analytics OU read_reports, et read_reports était
demandé depuis toujours. L'outil a toujours été offrable.
C'est le garde écrit dans la foulée (sdk/oauth-scopes.test.ts) qui a
refusé la fausse affirmation : il compare la demande OAuth aux gates de
TOOL_SHOPIFY_SCOPES et est resté vert quand read_analytics a été
retiré. Il a donc été vérifié dans l'autre sens — en retirant
read_reports, qui le fait bien échouer. La ligne est conservée ici
plutôt que supprimée : un audit qui efface ses erreurs apprend à se
tromper deux fois.
Les 60 jours : vraie limite du chemin Partner, fausse regle universelle
La documentation generique des access scopes dit bien que read_orders /
write_orders couvrent 60 jours et que read_all_orders est un scope
Shopify a permission pour aller au-dela. C'est le contrat a appliquer aux
apps Partner/public qui passent par ce modele.
Mais Shopify Staff a precise en janvier puis fevrier 2026 que, pour une
merchant-owned Custom App creee par le marchand dans son Dev Dashboard,
read_orders suffit a recuperer l'historique complet et que
client_credentials est le grant attendu. Le depot ne peut donc plus
encoder « pas de read_all_orders = 60 jours » comme une propriete globale
de Shopify.
Sources :
- contrat generique des scopes : https://shopify.dev/docs/api/usage/access-scopes
- clarification Shopify Staff merchant-owned : https://community.shopify.dev/t/read-all-orders-through-admin-api/28964/3
La consequence correcte est plus exigeante : la couverture temporelle des commandes doit etre mesuree par type de connexion et verifiee sur une vraie boutique. RevenueDaily, LTV, cohortes et prediction ne doivent jamais croire qu'une fenetre partielle est l'histoire complete.
Protected Customer Data est une contrainte distincte de
read_all_orders. La doc Shopify impose une review aux public apps pour ces
donnees, alors que les custom apps suivent un regime different ; certaines
donnees peuvent encore dependre du type d'app, du plan ou d'une restriction
Shopify. On expose donc le refus observe, on ne le predit pas avec une regle
« public app » appliquee a tous.
Plafond 2 — l'ingester couvre 4 domaines sur ~20
features/shopify/ingester/handlers/ : order, product, customer,
inventory. Les topics souscrits confirment le même périmètre.
Tout le reste de l'admin — collections, menus, metafields, metaobjects, remises, marchés, locales, traductions, fulfillments, cartes cadeaux, publications, thèmes, fichiers — n'entre JAMAIS dans notre base. Il est au mieux proxyfié vers un agent, jamais ingéré.
C'est la distinction qui décide de tout : proxyfier n'est pas
connaître. Un graphe, une sonde, un algo de prédiction ne lisent pas
un passthrough. Ils lisent nos tables. Un domaine non ingéré est
invisible pour StoreSignalIndex, StoreMetricDaily, le Prediction
Engine et tout ce que la guideline appelle « la couche d'intelligence ».
Plafond 3 — la surface MCP est écrite à la main
15 outils relais, énumérés un par un. shopifyAdminGraphQL rend déjà
techniquement TOUT le lisible atteignable, donc le manque n'est pas la
capacité : c'est la lisibilité. Un outil nommé, typé, documenté est
découvrable par un agent ; un passthrough générique demande à l'agent de
deviner le schéma d'abord.
Mais énumérer à la main est une course perdue : 70 méthodes attendent une surface, et chaque tranche coûte six locales, deux corpus de doc et quatre gardes dérivées. La bonne forme est la dérivation, comme tout le reste de ce dépôt : la surface de lecture doit se déduire du schéma Admin et du grant réel du marchand, pas d'une liste tapée.
Ce que cet audit ne mesure pas
L'extérieur du store. La guideline en nomme quatre couches (intérieur,
extérieur, marché, connaissance mondiale) ; cette page ne traite que la
première. Le scanner, storefront-mcp, le client UCP et le Store Graph
couvrent les autres et n'ont pas été remesurés ici.
Ce qui en découle
- Lire le grant reel du bon chemin. La Custom App merchant-owned est le chemin principal (ADR 0033) : ses scopes viennent du Dev Dashboard du marchand. Le catalogue OAuth public ne doit jamais devenir une limite implicite de ce chemin. Un grant partiel est accepte et ses gaps sont derives.
- Ingérer tout domaine lisible, pas seulement les quatre qui ont un webhook.
- Dériver la surface de lecture du schéma et du grant reel plutôt que l'énumérer.