Skip to main content

Overview

Charlie is built on security-first principles: no customer PII, no payment data, minimal Shopify API permissions, and full data residency within the Shopify and Cloudflare ecosystem.

Data governance

What Charlie stores

Charlie stores only operational configuration in its own database (Cloudflare D1 — SQLite). The complete database schema contains ten tables, none of which hold customer PII:
TablePurposePII
sessionsShopify OAuth session tokens, app settingsNo
locationsLocation metadata, capacity settings, coordinatesNo
safety_stock_rulesMerchant-defined inventory buffer rulesNo
eventsWebhook deduplication logNo
bulk_operationsShopify bulk operation trackingNo
migration_runsInternal migration stateNo
revenue_attribution_rulesRevenue attribution configuration per locationNo
analytics_settingsAnalytics display preferencesNo
inventory_exportsExport job tracking and delivery statusNo customer PII
activity_logsAudit trail of configuration changesNo
Charlie contains no customer PII, no order content, no payment information. This is enforced at the Shopify API scope level — Charlie does not request read_customers access.

What Charlie writes to Shopify

Charlie computes and writes back operational values to Shopify metafields. All values are merchant configuration or computed inventory data — no customer data is involved. Variant metafields
MetafieldTypePurpose
inventory.levelsJSONPer-location sellable and safety stock quantities
inventory.compactStringOptimized format consumed by Shopify Functions
inventory.fulfillableIntegerTotal fulfillable quantity across locations
inventory.availableBooleanWhether any stock is available
inventory.safety_stockJSONPer-location safety stock overrides
Product metafields
MetafieldPurpose
inventory.safety_stockSafety stock overrides for all variants
inventory.updated_atTimestamp of last inventory sync
inventory.availableBoolean availability across all variants
inventory.first_available_variantReference to first in-stock variant
Location metafields
MetafieldPurpose
location.typeWAREHOUSE or STORE classification
location.tagsMerchant-defined tags for routing rules
location.at_capacityWhether location has reached daily order limit
location.capacity_modeBLOCK or PRIORITIZE
location.daily_orders_quota_left_ratioFloat 0–1, remaining daily capacity
location.can_fulfillWhether location is active for fulfillment
location.shipping_enabledWhether location ships online orders

What Charlie never touches

The following data stays exclusively within Shopify and is never accessed, processed, or stored by Charlie:
  • Customer PII (names, emails, addresses, phone numbers)
  • Payment and billing information
  • Order content and transaction history
  • Customer purchase history

Data ownership and portability

AspectDetail
Rules storageMerchant rules stored in Charlie’s Cloudflare D1 database
Computed valuesInventory and capacity data written to Shopify metafields
Ownership100% merchant-owned — all metafield values accessible via Shopify Admin API
PortabilityNo proprietary formats — standard JSON metafields
UninstallAll Charlie data deleted on shop redact; metafield values persist in Shopify

Shopify API permissions

Charlie requests only the scopes required for omnichannel fulfillment operations. No customer data scope is requested or used.
ScopePurpose
read_inventoryRead stock levels for sellable quantity computation
read_locationsRead merchant location list
write_locationsWrite location metafields (type, tags, capacity state)
read_productsRead variant data for safety stock computation
write_productsWrite inventory metafields on variants and products
read_ordersRead fulfillment order context for routing
read_assigned_fulfillment_ordersAccess orders assigned to locations
write_assigned_fulfillment_ordersUpdate fulfillment assignments
read_merchant_managed_fulfillment_ordersRead merchant-managed orders
write_merchant_managed_fulfillment_ordersRoute and update fulfillment orders
read_fulfillment_constraint_rulesRead existing routing constraint rules
write_fulfillment_constraint_rulesWrite routing and capacity constraint rules
read_shippingRead shipping profiles
write_shippingUpdate shipping profiles for routing rules
read_third_party_fulfillment_ordersRead third-party fulfillment context
read_validationsRead cart validation extensions
No read_customers scope is requested or used at any point. Charlie has zero API access to customer PII.

Security controls

Authentication and session management

  • All admin routes enforce shopify.authenticate.admin(request) before any processing — unauthenticated requests are rejected immediately
  • Sessions stored in Cloudflare D1 using Shopify’s official session storage adapter
  • direct_api_mode = "online" — no long-lived offline tokens
  • Access tokens stored server-side only, never exposed to client-side code

Multi-tenancy isolation

Every database query is scoped by shop domain at the ORM layer. No query executes without an explicit WHERE shop = ? filter derived from the authenticated Shopify session. This is enforced in code on every data access function — not by convention alone.

Webhook security

ControlImplementation
HMAC verificationAll Shopify webhooks verified via Shopify SDK before processing — invalid signatures return HTTP 401
Replay attack preventionWebhook IDs stored in events table with UNIQUE constraint — duplicate events are rejected
Async processingWebhooks enqueued to Cloudflare Queues — prevents timeout-based abuse
Dead-letter queuesFailed jobs automatically routed to charlie-background-queue-dlq and charlie-webhook-queue-dlq

HTTP security headers

Applied globally to all requests via Hono secureHeaders() middleware:
HeaderValue
X-Content-Type-Optionsnosniff
X-XSS-Protection1; mode=block
Referrer-Policystrict-origin-when-cross-origin
X-DNS-Prefetch-Controloff
X-Download-Optionsnoopen
X-Permitted-Cross-Domain-Policiesnone
Content-Security-Policy and X-Frame-Options are intentionally omitted. Charlie runs as a Shopify embedded app inside an iframe — Shopify manages these restrictions at the platform level.

Rate limiting

Charlie implements a token-bucket rate limiter for all outbound Shopify API calls:
  • Max 20 requests/second toward Shopify API
  • Max 10 concurrent requests per worker instance
  • Queue-based backpressure — requests wait rather than fail under load

Encryption

LayerImplementation
In transitTLS enforced by Cloudflare on all traffic
At restCloudflare D1 encryption at rest
Access tokensStored in D1 only, never exposed client-side

Error monitoring

Sentry is used for operational error monitoring. Error payloads contain stack traces and request context only — no customer PII, no access tokens, no session content. Sentry is disabled in development environments.

GDPR compliance

Charlie implements all three mandatory Shopify GDPR compliance webhooks:

Shop redact (shop/redact)

Triggered when a merchant uninstalls and requests data deletion. Charlie deletes all shop-specific data atomically across all database tables.

Customer data request (customers/data_request)

Charlie acknowledges this webhook and returns no data — by design. The app stores no personal customer data to return. This is documented explicitly in the codebase: This app does NOT store any personal customer data. We only store shop-level configuration (locations, safety stock rules, etc.). No customer PII, order details, or personal identifiers are persisted. If this app ever stores customer data in the future, this service must be updated.

Customer redact (customers/redact)

No-op by design — Charlie stores no customer-level data to redact.

Infrastructure and sub-processors

ProviderRoleCertification
ShopifyCommerce platform, data source of truthSOC2 Type II
Cloudflare WorkersServerless computeSOC2 Type II
Cloudflare D1Primary database (SQLite)SOC2 Type II
Cloudflare KVCache — shop info and location metadataSOC2 Type II
Cloudflare QueuesAsync background job processingSOC2 Type II
Cloudflare R2Object storage — database backups, inventory exportsSOC2 Type II
Cloudflare Analytics EngineFulfillment analytics dataSOC2 Type II
SentryError monitoringSOC2 Type II
PostHogProduct analyticsSOC2 Type II
ResendTransactional email (export notifications)SOC2 Type II
BetterStackUptime monitoring and status pageSOC2 Type II
MantleSubscription billing management
Google Maps Time Zone APITimezone resolution from location coordinates
Charlie does not currently hold SOC2 certification. All compute and storage infrastructure runs on Cloudflare (SOC2 Type II certified).

Risk summary

RiskAssessment
Data breach exposureMinimal — no customer PII in Charlie systems
Payment data riskNone — all transactions remain within Shopify
API over-permissioningLow — no customer scope; write scopes scoped to inventory and fulfillment metafields
Vendor lock-inLow — merchant-owned metafields in standard JSON format
Uninstall riskLow — complete data deletion via shop redact webhook
IT burdenZero — no on-premise component, no integration maintenance

Last modified on April 23, 2026