<!-- Generated by scripts/generate-aeo-artifacts. Do not edit. -->

# Security

Version 1.2 · Last updated 9 Nov 2026 · Source: https://eturns.app/policy/security/

Eturns is an AI agent that takes after-sales decisions on a merchant’s behalf. Security is the load-bearing dependency for that autonomy: if merchants cannot trust what the agent is doing, they will not let it act. This page summarizes the controls we have implemented.

## Platform edge

Public traffic hits Render web services with Render’s built-in edge: automatic DDoS protection (L3/L4 and platform L7), managed TLS certificates, HTTP→HTTPS, and an optional static edge cache. We do not operate a third-party CDN or WAF zone; DNS for *.eturns.app points directly at Render.

## Authentication

- Every Shopify webhook is HMAC-SHA256 verified before any side effect, using constant-time compare. Mismatch returns 401.
- Customer portal actions verify a short-lived HttpOnly server-side portal session (minted after 6-digit email verification, buyer gift approval, or Shopify Customer Account OAuth 2.0 PKCE S256); post-purchase checkout extensions verify Shopify’s signed checkout token (HS256) and ignore client-supplied customer IDs.
- Merchant admin routes verify the App Bridge session token; a JTI revoke list rejects logged-out tokens.
- Installation uses Shopify Managed Installation (no legacy /auth redirect loop), exchanging validated session tokens via Shopify token exchange.

## Authorization & data isolation

- Row-Level Security is enforced on every shop-scoped Postgres table via FORCE ROW LEVEL SECURITY and a per-request shop_id GUC.
- Every shop-scoped database transaction drops privileges via SET LOCAL ROLE to a restricted tenant role (NOSUPERUSER, NOBYPASSRLS); explicit system contexts are limited to install bootstrap, cross-shop webhook routing, and compliance/retention sweeps.
- Shop access and refresh tokens are encrypted by the application before they are stored (AES-256-GCM with a fresh random nonce per token). The key is held in the hosting platform’s secret store, never in the database, and production refuses to store or read a token that is not encrypted this way.
- Supabase Storage uses private buckets with short-TTL signed URLs that include a random token; nightly encrypted logical backups use RSA-4096 + AES-256-GCM with the private key held offline.

## AI autonomy gates

- Confidence + dollar thresholds gate every autonomous action. Each store’s dollar limit is set from its own order values (or by the merchant); no autonomous action above it without merchant approval.
- The never-auto-block principle: no block-customer action is exposed to the AI tool catalog. Merchants decide; the AI surfaces signals.
- Every model call goes through the Vercel AI Gateway; there are no direct calls to xAI, OpenAI, or Google.
- Order contact fields never enter language-model prompts; structured case facts use sentinel IDs and customer messages pass through automated PII redaction.
- Per-shop AI cost cap (circuit breaker) prevents runaway spend.

## Decision trail & idempotency

- Every AI decision writes a row to agent_decisions with model, prompt hash, context summary, tool calls, action, dollar amount, and reasoning trace. Kept 7 years.
- Every webhook is keyed on X-Shopify-Webhook-Id plus (shop_id, topic, event_id, shopify_resource_id); succeeded deliveries are idempotent no-ops, unfinished rows re-dispatch safely, and out-of-order updates are rejected by (shopify_updated_at, shopify_triggered_at) watermarks.
- Financial routes (approve, inspect, refund) require an Idempotency-Key header and an active staff seat.
- Every state transition and merchant action is recorded in append-only audit_log_entries, backed by database-level DDL audit logging (pgaudit).

## Rate limiting

Two inbound layers behind Render’s edge: an Express coarse IP rate limit on /api/admin, /webhooks, and /customer, and a shop-scoped Redis-backed limiter keyed on (shop_id, action_class, plan_tier). OTP, upload, financial, and admin-write action classes fail closed when Redis is unavailable; those surfaces never degrade to unlimited.

## Supply chain

- Secret scan with gitleaks on every push.
- High+ CVEs block the build via pnpm audit:ci.
- Weekly dependency audit job.
- Trivy filesystem + IaC scan.
- Dependabot version PRs.

## Content Security Policy

A per-surface CSP is applied to every customer-facing route. Violations are reported to /api/csp-report, which is itself rate-limited per IP to prevent report flooding.

## Reporting a vulnerability

If you believe you have discovered a security incident or vulnerability, contact contact@eturns.app. We will provide notices of security incidents as required by applicable law.
