SkuWatch AI Visibility Agent Scan your store or site

Data isolation

Protecting Store Context in a Multi-Tenant Shopify App

Short answer

Tenant-scoped keys, jobs, evidence, caches, and logs that prevent one merchant's products or prompts from entering another report.

By skuwatch editor

Every SkuWatch AI Visibility object must carry tenant identity from request to storage.

primary key: (shop_id, object_id)
job key: (shop_id, scan_id)
cache key: shop_id:market:canonical_url:version
evidence key: shop_id:observation_id

A canonical URL alone is not a tenant key. Two shops can reference the same competitor URL; their prompts, annotations, and approvals remain separate.

Boundaries

  • derive shop from verified session, never request body alone
  • scope every database query by shop ID
  • include shop ID in queues and idempotency keys
  • encrypt tokens separately from public evidence
  • redact customer/account paths from scans
  • prohibit cross-shop cache fallback

Failure test

Seed two shops with identical product handles and different domains. Run concurrent scans and assert that findings, prompts, exports, and restore payloads never cross.

Public corpus cases live outside merchant tenant data and carry independent-public-analysis; they must never appear as a merchant’s customer record.

Request boundary

Tenant identity begins with a verified Shopify session or server-side installation record. The shop domain, shop ID, scan ID, or object ID in a request body is never sufficient authority. Resolve the tenant once, attach it to request context, and require repository methods to receive that context.

type TenantContext = {
  shopId: string;
  installationId: string;
  actorId: string;
  requestId: string;
};

getFinding(ctx: TenantContext, findingId: string)

A repository API that accepts only findingId makes an unscoped query easy to write. The safer interface makes tenant scope mandatory.

Queue and worker isolation

Queue payloads include an opaque job ID and tenant ID; workers reload authorization and installation state before processing. A job cannot select its tenant from a URL contained in its payload. Retries preserve tenant scope, and dead-letter records remain tenant-scoped.

Concurrency locks use (shop_id, resource_id, operation) so two merchants with the same product handle cannot block or overwrite one another. Rate limits should also separate platform-wide provider limits from merchant-specific quotas.

Evidence and cache keys

Public evidence may reference the same competitor URL across merchants, but annotations, prompts, findings, approvals, and reports stay separate. Shared raw retrieval can be stored only when its provenance and access policy permit reuse; tenant-derived interpretation cannot fall back across shops.

Cache tests must cover missing prefixes, malformed keys, key truncation, and serialization changes. Treat an unscoped cache hit as a security failure, not a performance issue.

Logs and exports

Structured logs include tenant ID for access control but redact access tokens, customer data, signed parameters, and full administrative URLs. Merchant exports query by tenant at the database boundary and are tested with deliberately colliding handles, SKUs, prompt IDs, and scan IDs.

Isolation test matrix

Seed two shops with identical handles and identifiers, then run simultaneous scans, explanation jobs, task generation, approvals, exports, and restore requests. Assert that each response, queue message, cache entry, object-store path, and audit event contains only its tenant’s records. Add a negative test that submits shop B’s object ID through shop A’s valid session and expects a not-found response without revealing that the object exists.

The public website scanner uses the separately bounded /public/* interface. It does not inherit a merchant tenant, Shopify write capability, or access to private app records.

Community discussion

Add to the article

Ask a technical question, share a storefront result, or challenge a conclusion with evidence.

Comments are public. Do not post customer data, credentials, private store information, promotional spam, or unsupported accusations. Comments may be moderated.