Features Pricing Services
Compare
Resources
Contact Start Free Trial
Home/Docs/Security & GDPR

Security & GDPR

Soryk's security model is grounded in one design choice: your data lives in Shopify, not in our database. There's no app-side store of your customers, agents, products or orders to breach. What we do hold (sessions, transient OTPs, audit mirror, push subscriptions) is documented below.

Stateless Architecture

Soryk is stateless by design. We do NOT host:

We DO host:

Result: GDPR-coherent by design. No DPA dance for B2B customer data.

Architecture

Data residency

Hosted Soryk runs on Vercel's Frankfurt (FRA1) region by default. Upstash Redis is provisioned in EU regions. Resend is configured with the EU sending region. All processing of EU customer data happens within the EU.

If you self-host, you control where your deployment runs. We recommend matching your Shopify store's data region for compliance.

GDPR & DPA

Authentication

JWTs are signed with HS256 against JWT_SECRET. Verification supports a previous-secret via JWT_SECRET_PREVIOUS for graceful rotation (see JWT rotation).

Rate limiting

All login endpoints and the bulk-payment endpoint are protected by Upstash sliding-window rate limiting.

BucketLimitWindow
agent:login / buyer:login (per email)55 min
agent:login:ip / buyer:login:ip (per IP)305 min
admin:payments (per shop)601 min

Excess returns HTTP 429 with a Retry-After header. Without Upstash configured, the limiter degrades open (dev convenience).

Audit log immutability

Every privileged admin action (create agent, approve discount, record payment, set override, etc.) writes a soryk_audit_log metaobject and mirrors it to an append-only Upstash list (LPUSH + LTRIM 5000). The mirror is the tampering guard:

Tag injection prevention

Order tags can carry user-supplied values (agent email, manager email, buyer email). Without sanitization, a comma or quote in an email could split or corrupt tags downstream. Soryk sanitizes every value through buildTag(namespace, value) + sanitizeTagValue:

Read-side, the commission engine indexes the email→agent map both raw and sanitized so attribution stays consistent even when the email contains + aliases or dots.

Buyer ownership enforcement

Every buyer-side API call verifies that the requested resource belongs to the session's companyId. The catalog endpoint, in particular, looks up the requested CompanyLocation → Company chain server-side before running contextual pricing. Cross-tenant requests get HTTP 403, never silent fallback.

JWT secret rotation

Zero-downtime rotation in 4 steps:

  1. Issue new secret. Generate a fresh strong random string.
  2. Set previous. Move the current JWT_SECRET value to JWT_SECRET_PREVIOUS, then set the new one as JWT_SECRET. Deploy. verifyWithRotation() accepts both during this window.
  3. Wait one TTL. Default JWT TTL is 90 days. After that, every active session has been re-issued under the new secret.
  4. Drop previous. Unset JWT_SECRET_PREVIOUS. Done.

For a compromise scenario, skip step 3 and force-logout everyone immediately by also bumping a session-version field — sessions become invalid on next request and users re-auth.

What happens on uninstall

You uninstall Soryk from Shopify Apps:

To explicitly purge everything: uninstall, then email us with your shop domain — we'll wipe the Upstash side immediately.

Was this helpful?
Edit this page on GitHub