Last updated: May 31, 2026
ScrambleSync Vendor Security Questionnaire and Trust Overview
A CAIQ/SIG-style response for prospective customers' security reviewers. Operated by Coyote Valley Technology Solutions. Grounded in codebase review; controls not currently held are stated honestly.
How to read this document
ScrambleSync is a cloud SaaS for running golf scramble tournaments. In data-protection terms, ScrambleSync acts as a processor: the tournament organizer (our customer) is the controller and decides what data is collected and why. We process that data solely to provide the tournament service — we do not sell it, and we do not use it for advertising, profiling, or any secondary purpose. We practice data minimization (only what is needed to run an event) and purpose limitation.
Each answer below is grounded in our actual architecture and source code. Where a control is not currently in place, we say so plainly and describe compensating controls rather than overstating our posture. File paths in evidence lines refer to our application repository and are available for review under NDA.
1. Company & Governance
Q: Who is the vendor and what is the product? A: ScrambleSync, a SaaS tournament-management platform for golf scrambles, operated by Coyote Valley Technology Solutions. It serves two audiences: organizers (who set up and run events) and golfers/registrants (who join, score, and pay).
Q: What is the data-protection role of ScrambleSync? A: Processor. The organizer is the controller and is responsible for lawful basis, notice/consent to participants, and responding to data-subject requests. ScrambleSync processes personal data only on the organizer's documented instructions to deliver the service.
Q: Do you have a formal information security program / ISMS, security policies, and a named security owner? A: We operate a documented set of technical controls (enforced in code and CI, evidenced throughout this document) and the company principals own security decisions. We do not currently maintain a formal, independently audited ISMS (e.g., ISO 27001-style) with a separate CISO function. Compensating controls: security is enforced programmatically (middleware auth gates, RLS, CSP, CI security gates) rather than relying on procedural policy alone, which reduces drift.
Q: Do you perform security awareness training and background checks on staff? A: As a small team, formal recurring security-awareness training and standardized background-check procedures are not yet formalized. Compensating controls: least-privilege access, MFA on the organizer dashboard and admin area, and a strict separation between the admin surface (passkey + HMAC-signed session) and the customer surface.
Q: Do you use subcontractors / sub-processors, and do you flow down obligations? A: Yes. Sub-processors are listed in Section 2; each is a reputable provider with its own published DPA. Customers should review and accept those sub-processor DPAs as part of onboarding.
Q: Is there a documented SDLC with change control? A: Yes, in practice: all changes flow through Git and a CI pipeline that must pass `npm audit --audit-level=high`, lint, type-check, unit/integration tests, a production build, and a Lighthouse accessibility audit before merge. Evidence: `.github/workflows/ci.yml`.
2. Data Hosting & Sub-processors
Q: Where is customer data hosted? A: Primary data store is Supabase managed PostgreSQL on AWS in us-west-1. The application runs on DigitalOcean App Platform (default US West region). Traffic is fronted by Cloudflare (CDN, DNS proxy, DDoS protection, TLS termination).
Q: Who are your sub-processors and what do they receive? A:
- Supabase (AWS us-west-1) — primary database, authentication, row-level security, real-time, and file storage (logos/images). Receives all personal-data categories except card data.
- Stripe — payment processing. Receives payment amount, currency, Stripe session/payment-intent IDs, product descriptions, and customer email if captured. Card data (PAN/CVV) is handled entirely by Stripe and never touches ScrambleSync servers (PCI SAQ-A scope).
- Resend — transactional email (confirmations, reminders, password/MFA, invites). Receives recipient email and name plus event/team context inside the message template.
- Cloudflare — edge/CDN/DNS. Receives HTTP request metadata (IP, user agent, path). Does not decrypt origin HTTPS by default.
- Sentry — error and performance monitoring. Receives stack traces, request context (request ID, user ID, URL), exception details, and breadcrumbs.
- Upstash (Redis, us-east-1 default) — distributed rate-limit counters and optional circuit-breaker state. Stores ephemeral rate-limit window state keyed by IP; no durable personal data.
- DigitalOcean — application hosting and log capture (stdout/stderr JSON lines with pseudonymized user IDs).
_Note: distributed tracing / external metrics export is not currently enabled. A custom OpenTelemetry pipeline was removed for stability; no trace or metric data is sent to any external APM today. Observability relies on structured application logs (DigitalOcean) and error capture (Sentry) — see Section 7._
Q: Do you transfer data internationally? A: Primary storage and compute are US-based (AWS us-west-1, DigitalOcean US West). Cloudflare, Sentry, Resend, and Stripe operate global infrastructure under their respective DPAs. Customers with EU/UK transfer obligations should rely on the sub-processors' SCCs/DPAs.
Q: Is customer data logically isolated between tenants? A: Yes. Organizer data is isolated by row-level security keyed to the authenticated owner (see Section 3). Cross-tenant isolation is verified by automated end-to-end tests.
3. Access Control & Authentication
Q: How do you authenticate organizers, and is MFA enforced? A: Organizer authentication is handled by Supabase Auth. MFA is enforced on all dashboard routes (`/dashboard`, `/t/*`, `/settings/*`, `/metrics`) via an AAL2 (Authentication Assurance Level 2) gate. A registered passkey (WebAuthn) satisfies MFA; otherwise TOTP is required and users without either are forced into MFA setup. Evidence: `middleware.ts` (AAL2 check via `supabase.auth.mfa.getAuthenticatorAssuranceLevel()`, forced setup at `/settings/mfa?required=1`).
Q: How is the internal admin surface protected? A: The `/admin*` area is gated separately by passkey (WebAuthn) authentication with an HMAC-signed session cookie, independent of organizer Supabase auth. Evidence: `middleware.ts` admin block verifying the `admin_session` cookie; `lib/admin-session.ts`.
Q: How is database access controlled? A: Row-level security (RLS) is enabled on all application tables (tournaments, holes, teams, scores, sponsors, messages, audit_events, and others). Organizers can only access tournaments they own (`owner_id = auth.uid()`). Player- and sponsor-facing flows run through code-authenticated server route handlers using a service role, not broad client access. Evidence: `supabase/migrations/0001_init.sql` (RLS enabled on every table; owner-scoped policies).
Q: How do you prove tenant isolation works? A: Automated e2e security tests confirm that User 2 receives 404 (not data) when attempting to access User 1's tournament page or messages API via GET and POST. Evidence: `e2e/security.spec.ts` (cross-user isolation describe block; `expect(res.status()).toBe(404)`).
Q: Do you follow least privilege for service credentials? A: Yes. The service-role key is used only server-side in route handlers; client/browser code uses the anon key constrained by RLS. Admin actions require the separate passkey-gated session.
Q: How are secrets managed? A: All secrets (API keys, webhook secrets, tokens) are supplied via environment variables only; none are hardcoded. Production secrets are set in the DigitalOcean dashboard as secret env vars. Code fails fast when a required secret is absent (e.g., `getStripe()` throws if `STRIPE_SECRET_KEY` is missing).
4. Encryption (In Transit & At Rest)
Q: Is data encrypted in transit? A: Yes. HTTPS is enforced end-to-end. HSTS is set with `max-age=63072000` (2 years), `includeSubDomains`, and `preload`. TLS is terminated at Cloudflare/the platform edge with encrypted connections to origin. Evidence: `next.config.mjs` (`Strict-Transport-Security` header).
Q: Is data encrypted at rest? A: Yes. Supabase PostgreSQL uses AWS encryption at rest by default. MFA TOTP secrets are stored and encrypted internally by Supabase Auth. Evidence: Supabase managed encryption; `supabase/migrations/0017_organizer_mfa.sql`.
Q: How are cardholder data and payment secrets protected? A: Card data never reaches ScrambleSync — it is entered into Stripe-hosted elements and processed by Stripe (PCI DSS Level 1 provider). We store only non-sensitive payment metadata (session/intent IDs, amounts, item descriptions). Our scope is PCI SAQ-A.
Q: Do you verify webhook authenticity? A: Yes. Stripe webhooks are verified via HMAC-SHA256 signature against `STRIPE_WEBHOOK_SECRET` using `Stripe.webhooks.constructEvent()` before any processing. Evidence: `app/api/webhooks/stripe/route.ts`.
Q: Do you manage your own encryption keys? A: No customer-managed-key (BYOK/CMK) option is offered today. Encryption keys are managed by the underlying platforms (AWS/Supabase). This is not currently configurable by customers.
5. Application Security
Q: What HTTP security headers are applied? A: A strict set on all responses: Content-Security-Policy (`default-src 'self'`; `script-src 'self'` with inline/eval needed by the framework; `img-src`/`connect-src` limited to trusted origins such as Supabase, Stripe, and specific map tile providers; `object-src 'none'`; `base-uri 'self'`; `frame-src` limited to `js.stripe.com`), X-Frame-Options: SAMEORIGIN (clickjacking protection), X-Content-Type-Options: nosniff, Referrer-Policy: strict-origin-when-cross-origin, and Permissions-Policy disabling camera/microphone and restricting geolocation to self. The `x-powered-by` header is disabled. Evidence: `next.config.mjs` (`securityHeaders` array; `poweredByHeader: false`).
Q: How do you protect against injection and XSS? A: Database access is via the Supabase client (parameterized queries, no string-built SQL). Email templates HTML-escape interpolated values (`esc()` for `& < > "`). React escapes rendered output by default, and CSP constrains script origins. Evidence: `lib/email.ts` (`esc()`).
Q: How is input validated at boundaries? A: User input is validated server-side; for example the erasure endpoint enforces an RFC-style email pattern and a 254-char max before acting. Uploads use safe filename handling. Evidence: `app/api/organizer/erase/route.ts` (email regex + length cap).
Q: Is CSRF mitigated? A: Yes, via SameSite cookies (Lax default) in the Supabase SSR cookie configuration; state-changing actions require an authenticated session and same-origin context.
Q: How do you rate-limit and protect against abuse/brute force? A: General limit of 60 requests/minute per IP (sliding window) and a stricter 10/minute per IP on auth endpoints to deter brute force. Backed by Upstash Redis when configured, with an in-memory fallback for single-instance/dev. Cloudflare provides upstream DDoS protection. Evidence: `lib/ratelimit.ts`; `middleware.ts` enforcement.
Q: How resilient is the app to failing dependencies? A: Circuit breakers wrap external calls — Stripe opens after 3 failures (15s cooldown), Supabase after 5 failures (30s cooldown) — with exponential-backoff retries for transient errors and a 15s Supabase fetch timeout to avoid pinned workers. Evidence: `lib/circuit-breaker.ts` (`stripeBreaker`, `supabaseBreaker`); `lib/retry.ts`; `lib/supabase/timeout-fetch.ts`.
Q: Do you have a WAF? A: Cloudflare provides edge protection (DDoS, basic filtering). A fully tuned managed-rules WAF policy is not something we currently represent as a contractual control; we rely on Cloudflare's baseline plus application-layer rate limiting and CSP.
6. Vulnerability & Patch Management
Q: Do you scan dependencies for known vulnerabilities? A: Yes. CI runs `npm audit --audit-level=high` on every push and pull request and fails the build on high/critical advisories. Dependabot opens grouped weekly PRs for npm dependencies and GitHub Actions. Evidence: `.github/workflows/ci.yml`; `.github/dependabot.yml`.
Q: Do you run static analysis / type checking / tests in CI? A: Yes — every change must pass lint (ESLint with `eslint-plugin-jsx-a11y`), TypeScript type-check (`tsc --noEmit`), the Vitest unit/integration suite, a production build, and a Lighthouse accessibility audit before merge. Evidence: `.github/workflows/ci.yml`; `package.json` scripts.
Q: Have you had an independent third-party penetration test? A: No — not currently performed. Compensating controls: a Playwright security test suite that asserts auth walls and cross-user isolation/RLS enforcement, automated dependency scanning that gates merges, and CSP/security headers verified in config. A formal third-party pen test is a candidate for the future as the business scales; we will not claim it until performed.
Q: What is your patch SLA for critical vulnerabilities? A: We do not publish a formal numeric SLA today. In practice, CI blocks merges on high/critical npm advisories and Dependabot surfaces patches weekly (or sooner for security advisories), and we apply framework security updates promptly. This is operational practice rather than a contractually committed SLA at this stage.
7. Logging, Monitoring & Incident Response
Q: What do you log, and is it free of sensitive data? A: Structured JSON logs capture timestamp, level, message, request ID, pseudonymized user ID, action, duration, and HTTP status. Passwords and raw PII are not logged. Logs go to stdout and are captured by DigitalOcean; errors flow to Sentry. Evidence: `lib/logger.ts`; per-request `x-request-id` set in `middleware.ts`.
Q: Do you maintain an immutable audit trail of sensitive actions? A: Yes. The `audit_events` table records actions such as tournament create/delete, team generation, score deletion, and data erasure, with actor ID/email, IP, request ID, timestamp, and anonymized metadata. It is insert-only via the service role — there is no insert policy for anon/authenticated roles, so clients cannot write or tamper with it directly, and read access is RLS-restricted to the tournament owner. Evidence: `supabase/migrations/0008_audit_log.sql`; `lib/audit.ts`.
Q: How do you monitor availability and health? A: A `/api/health` endpoint checks Supabase connectivity, Stripe configuration, and circuit-breaker states, returning a JSON `{status: 'ok'|'degraded'}` body. It intentionally always returns HTTP 200 (the platform edge masks 5xx into an opaque 504), so monitors branch on the `status` field. Sentry provides error/performance alerting. Evidence: `app/api/health/route.ts`.
Q: Do you have an incident response plan and breach-notification process? A: We have monitoring and alerting to detect issues — error capture (Sentry), a deep `/api/health` dependency probe, structured application logs, and operator push alerts (Pushover) fired when a scheduled background job (reminders, retention, tournament-status, webhooks, announcements) reports a failure — and the audit trail supports forensic review. A formal, documented IR runbook with committed customer breach-notification timelines is not yet formalized. Compensating controls: small team with direct ownership enabling fast response; sub-processors (Supabase, Stripe, Cloudflare) have their own mature IR and breach-notification programs that feed our response. As controller, the organizer remains responsible for notifying their own data subjects/regulators; we will assist as processor.
Q: What telemetry runs in production today? A: Structured JSON application logs (stdout → DigitalOcean) and error capture via Sentry. Distributed tracing and external metrics export are not currently enabled — a custom OpenTelemetry trace/metric pipeline was removed for stability and has not been re-introduced. We state this plainly rather than imply live dashboards that do not receive data.
8. Data Privacy & Handling
Q: What categories of personal data do you process? A: Organizer account data (name, email, MFA/passkey credentials, session tokens); registrant PII (names, emails, team names, optional phone/rosters); player/golfer data (names, team assignments, hole-by-hole scores, optional handicaps, cart assignments); payment transaction metadata (amounts, Stripe session/intent IDs, item descriptions — no card data); sponsor/advertiser data; tournament/event configuration; immutable audit logs; communication/messaging content; integrity/compliance flags; usage/performance telemetry (web vitals, metric samples, pseudonymized logs); and technical/security identifiers (IP, user agent, request IDs, session/admin cookies, WebAuthn attestations).
Q: Do you sell personal data or use it for advertising/profiling? A: No. Data is processed solely to provide the tournament service. No sale, no advertising, no profiling, no secondary use. We apply data minimization and purpose limitation.
Q: How do you support data-subject rights (e.g., right to erasure)? A: Organizers can invoke a self-serve erasure endpoint that anonymizes registrant PII (name and email set to `[erased]`) across the tournaments they own and records the erasure in the audit trail without storing the raw email (only an erased count and email length are recorded). Evidence: `app/api/organizer/erase/route.ts`. Organizers can also export their data (see below) and delete a tournament, which cascades to related records.
Q: Can customers export their data? A: Yes. Authenticated organizers can export registrations (names, emails, teams, amounts) and results/scores via `/api/export/registrations` and `/api/export/results` (JSONL). Evidence: those route handlers exist under `app/api/export/`.
Q: What are your data-retention periods? A: Registrant PII: tournament duration + ~90 days (organizer-controlled; erasure available anytime). Player/score data: retained with the tournament; removed on tournament deletion or erasure. Payment metadata: retained as financial records (Stripe retains its own per Stripe policy). Audit logs: immutable, retained indefinitely (no raw PII). Web vitals and command-metric samples: auto-pruned after 30 days via pg_cron (guarded, since pg_cron is absent on local/dev). Rate-limit state: ephemeral. App logs: ~30 days on DigitalOcean (platform default); Sentry ~90 days (plan-dependent). Evidence: `supabase/migrations/0073_command_metric_samples.sql` (30-day delete job).
Q: Do you offer a DPA? A: Yes — a template DPA is available describing these practices. It is provided in good faith and should be reviewed by qualified legal counsel before execution.
9. Business Continuity & Backups
Q: How is data backed up? A: Supabase managed PostgreSQL performs automated daily backups. Recovery objectives: RPO ~24 hours, RTO target ~4 hours.
Q: Is point-in-time recovery (PITR) enabled? A: Not currently. PITR is available as a Supabase option but is not enabled at this time. Compensating control: automated daily backups provide a ~24h recovery point; PITR is a planned upgrade as scale warrants.
Q: Do you perform regular restore tests? A: Not yet. We do not currently perform documented quarterly restore-validation tests. We rely on Supabase's managed backup tooling and will introduce periodic restore validation as the program matures. We state this honestly rather than claim tests that have not occurred.
Q: What is your availability/DR posture? A: The application runs on DigitalOcean App Platform with Cloudflare edge protection; circuit breakers, retries, request timeouts, and graceful shutdown reduce the blast radius of dependency failures. A formal multi-region DR plan with a published availability SLA is not currently offered; underlying providers (AWS/Supabase, DigitalOcean, Cloudflare) provide their own resilience.
Q: How do you handle deployment safety? A: Deploys are gated by the full CI pipeline (audit, lint, type-check, tests, build, accessibility) and the health endpoint confirms dependency readiness post-deploy.
10. Programmatic API access (REST API)
Q: Do you offer a public API, and how is it secured? A: Yes. ScrambleSync exposes a versioned REST API at `/api/v1` so organizers can integrate their own systems (CRM sync, registration/roster export, leaderboard displays). It uses OAuth 2.0 Client Credentials: an organization registers an API client (Settings → API access), receives a client secret shown once and stored only as a scrypt hash, and exchanges it for short-lived (1-hour), opaque, revocable bearer access tokens (stored only as SHA-256 hashes). Interactive reference and quickstart are published at `/developers`. Evidence: `app/api/v1/oauth/*`, `lib/api/*`, `public/openapi.yaml`.
Q: What data can an API client access, and is it isolated per organization? A: A token can only read or write the issuing organization's own data — the same data the organizer already sees in the dashboard. Each token is bound to one organization at issuance (never taken from a request parameter), and every API query is filtered to that organization server-side. The API introduces no new sub-processor and no new data egress beyond the access the organization itself authorizes. Cross-organization access is prevented and covered by automated end-to-end tests. Evidence: `lib/api/withApiScopes.ts`; `e2e/api-v1.spec.ts` (cross-tenant test).
Q: How is API access scoped (least privilege)? A: Tokens carry granular scopes (`contacts:read`, `contacts:write`, `registrations:read`, `invitations:read`/`invitations:write`, `tournaments:read`); a client is granted only the scopes it needs and a token may request a subset. Calls missing a required scope are rejected with `403 insufficient_scope`.
Q: Can a compromised API credential be contained quickly? A: Yes. An organization admin can rotate a client secret, revoke an individual token, or disable a client at any time from the dashboard; disabling a client immediately invalidates all of its outstanding tokens (not just future issuance), and all access tokens expire within 1 hour regardless. Evidence: `lib/api/clients.ts`; `app/(dashboard)/settings/api/*`.
Q: Is the API rate-limited, logged, and audited? A: Yes. API calls are rate-limited per client (with a `Retry-After` header on 429); every request is logged server-side with a correlation `request_id`, client, and organization; and client/token lifecycle plus write actions are recorded in the immutable audit trail. Card data and other out-of-scope data are never exposed via the API, and only organization administrators can create or manage API clients. Evidence: `lib/ratelimit.ts`; `lib/api/withApiScopes.ts`; `lib/audit.ts`.
Q: Do outbound webhooks introduce a new sub-processor? A: No. Outbound webhooks deliver events exclusively to the customer's own configured HTTPS endpoint — ScrambleSync does not route webhook payloads through any third-party service, and no new sub-processor is introduced. Payloads are HMAC-SHA256 signed (customers can verify authenticity using the `X-ScrambleSync-Signature` header) and are strictly limited to the event types the customer explicitly subscribed to.
11. Compliance & Certifications
Q: Are you SOC 2 certified? A: No — not currently held. Compensating controls: programmatically enforced access control (MFA/AAL2, admin passkey), RLS with tested tenant isolation, immutable audit logging, CI security gates, and strict security headers.
Q: Are you ISO 27001 certified? A: No — not currently held. See compensating controls above.
Q: What is your PCI DSS status? A: PCI SAQ-A scope. Card data is handled entirely by Stripe (a PCI DSS Level 1 provider) and never touches ScrambleSync servers. We are not PCI DSS Level 1 certified ourselves, nor do we claim to be.
Q: Do you comply with GDPR/CCPA? A: We are designed to support our customers' GDPR/CCPA obligations as a processor: data minimization, purpose limitation, no sale of data, erasure and export tooling, immutable audit trail, and sub-processors that publish GDPR/CCPA-aligned DPAs. The organizer, as controller, holds primary responsibility for lawful basis, notice/consent, and responding to data-subject requests; we assist and provide the technical means.
Q: Do you have HIPAA / FedRAMP / other regulated certifications? A: No. ScrambleSync is not designed for protected health information or government-regulated workloads and holds no such certifications. Organizers should not upload regulated special-category data.
Q: Will you complete custom security questionnaires and support audits? A: Yes, within the limits of a small team. We can complete reasonable questionnaires, share this trust overview and our template DPA, and provide code-level evidence for the controls above under NDA. We will not represent controls we do not have.