How the Stryke platform protects dealer and consumer data, mapped to the AICPA SOC 2 Trust Services Criteria. Prepared for dealers, partners, and auditors. Last updated September 8, 2026.
Stryke ("the platform") receives sales leads for auto dealerships (via CRM webhook, ADF email, or manual entry), opens SMS conversations with consenting consumers, and drives them toward appointments and offers. An AI sales agent drafts replies; dealership staff supervise, approve, and can take over any conversation at any time from the web dashboard.
Serverless: AWS Lambda per API route group, Fargate container tasks for AI agents and their tools, EventBridge cron jobs. No servers to patch.
Amazon DynamoDB (single-table, org-partitioned), Amazon S3 (documents, inbound lead emails), Neptune Analytics (analytics knowledge graph).
Amazon Cognito: one user pool for dealer users, a separate Google-federated pool for Stryke staff. JWT authorizers at the API gateway.
Anthropic Claude via AWS Bedrock (agents), Anthropic API (draft-review judge, inventory extraction). See §5.
Twilio Programmable Messaging with automated A2P 10DLC carrier registration per dealer. See §6.
Everything is infrastructure-as-code (SST v4, infrastructure/) deployed by GitHub Actions. Production deploys only on versioned releases.
The platform runs in AWS region us-west-2. Production and development are separate SST stages with separate resources, domains (*.strykeauto.com vs dev.*.strykeauto.com), and secrets; production resources are protected from deletion (sst.config.ts protect/retain).
We maintain a typed catalog of every record the platform stores (packages/core/src/entities.ts). The categories relevant to a dealer's risk assessment:
| Category | Examples | Contains | Where |
|---|---|---|---|
| Consumer identity | Customers, leads, lookups | Name, phone, email, address, lead notes | DynamoDB |
| Conversations | SMS/chat threads, messages | Full message bodies, delivery status | DynamoDB; previews (≤200 chars) projected to the analytics graph |
| Appointments | Test drives, service visits | Customer name, phone, email, notes | DynamoDB |
| Dealer business data | Inventory, pricing rules, deal history | VINs, pricing, gross figures, lender names | DynamoDB |
| Carrier registration | A2P 10DLC business profile | Legal name, EIN, business address, authorized representative contact | DynamoDB → Twilio TrustHub |
| Dealer users | Accounts, memberships, invites | Name, email, phone, per-org role | Cognito + DynamoDB (invite tokens stored as SHA-256 hashes only) |
| Raw lead emails | ADF intake (production) | Full inbound email MIME | S3 |
| Operational telemetry | Agent turn metrics, daily rollups | Token counts, latencies, confidence scores — no message content | DynamoDB |
Tenant isolation. Tenant data is keyed under ORGANIZATION#{organization_id} in the database partition key, so every organization-scoped query is tenant-bound by construction. Every authenticated business-data request re-resolves the caller's membership in the target organization on the server — the client-supplied organization ID is never trusted without an active membership row (packages/core/src/auth/context.ts requireActiveContext; covered by unit tests in tests/unit/auth-context.test.mjs). Records not keyed by organization (e.g. conversation messages) are only reachable through a parent lookup that is org-checked first.
Encryption. All data stores encrypt at rest with AWS-managed keys (DynamoDB, S3, Neptune Analytics defaults). All traffic — browser, API, webhooks, model calls — is TLS; public endpoints use ACM certificates on strykeauto.com domains. Customer-managed KMS keys are on the roadmap.
Third parties that process dealer or consumer data on Stryke's behalf:
| Subprocessor | Purpose | Data shared |
|---|---|---|
| Amazon Web Services | All hosting, storage, and model inference (Bedrock) | All platform data |
| Twilio | SMS delivery, phone numbers, A2P 10DLC carrier registration | Consumer phone numbers and message bodies; dealer business profile (legal name, EIN, representative contact) for carrier registration |
| Anthropic | Draft-safety judge; inventory-feed text extraction | Recent conversation excerpts, customer first/last name, and the draft reply (judge); public dealer-website inventory text (extraction). No consumer data is used to train models under our API terms. |
| MarketCheck | Vehicle valuation and market comps | VIN, year/make/model, mileage, ZIP code — never consumer name or contact info |
| OAuth sign-in (optional for dealer users; required for Stryke staff) | Email, name, profile photo of the signing-in user | |
| Resend | Transactional email to dealer staff (invitations, notification digests) | Dealer user email address and name; notification subject and body, which may include a customer name and a message preview |
| Browser push services (Apple/Google/Mozilla) | Dealer-staff web notifications | Notification payloads (may include customer name and a message preview) |
Status legend: In place implemented and verifiable in code Partial implemented with known limits Planned tracked in the roadmap.
| Control | Implementation | Status |
|---|---|---|
| No self-service signup | Cognito pre-signup trigger rejects all signups except admin-created accounts and Google federation onto pre-created accounts (handlers/triggers/pre-signup.ts). Organizations and their first admin are provisioned only by Stryke staff. | In place |
| API authentication | API Gateway JWT authorizers validate signature, issuer, audience, and expiry against Cognito before any handler runs (infrastructure/api.ts). | In place |
| Staff/customer identity separation | Stryke staff use a separate Cognito pool, federated to Google only, with signup restricted to approved corporate email domains (handlers/triggers/employee-pre-signup.ts). Customer tokens cannot reach /admin routes and vice versa. | In place |
| Per-request tenant authorization | Membership + active status re-checked server-side on every business-data request; org-management actions additionally gated by role (admin/manager) with last-admin protection (packages/core/src/auth/context.ts, handlers/api/organizations/…). | In place |
| Invite lifecycle | Invites carry a random token stored only as a SHA-256 hash, expire automatically (7 days), are revocable, and only activate for the invited email (packages/core/src/organizations/invites.ts; unit-tested). | In place |
| Webhook authentication | Twilio webhooks are HMAC signature-validated with constant-time comparison and rejected otherwise (packages/core/src/sms/twilio.ts validTwilioSignature; unit-tested). CRM/ADF intake endpoints require a pre-shared secret and fail closed when unset. | Partial |
| Least-privilege IAM | Workload roles are scoped to specific resources, including DynamoDB partition-key-prefix conditions on the AI agents' role (infrastructure/agentcore.ts). Some wildcard grants remain and are being tightened. | Partial |
| Secrets management | Secrets held in SST secret store / SSM SecureString; container workloads hydrate from SSM at startup; no secrets committed to the repository. | In place |
| MFA enforcement | Not yet enforced on either identity pool. | Planned |
| Tenant lifecycle & suspension | Each dealership carries an explicit lifecycle state (invited / onboarding / live / suspended). Suspending an account blocks it at every entry point: dealer API requests, organization-management actions, and outbound messaging — the last of these matters because inbound SMS does not pass through the request-authorization path (packages/core/src/auth/context.ts requireActiveContext, requireOrgMembership; packages/core/src/sms/delivery.ts evaluateSendGates; unit-tested). | In place |
| Staff role tiers | Stryke staff hold one of four internal roles (superadmin / ops / support / readonly) resolved from identity-provider group membership, and every staff endpoint declares the capability it requires. Only superadmin may create or suspend a dealership. The internal role namespace is separate from dealer roles and never bridges them (packages/core/src/auth/employee-capabilities.ts, packages/core/src/auth/context.ts requireEmployee; unit-tested). Until group assignment is complete, a staff member with no assigned group is treated as superadmin and the event is logged — see §7. | Partial |
| User/member deprovisioning | Roles can be changed, invites revoked, and an entire dealership suspended. A per-member removal + session-revocation path is not yet built. | Partial |
| Control | Implementation | Status |
|---|---|---|
| Infrastructure as code | Every AWS resource is declared in infrastructure/ (SST); no console-built resources. Changes ship only through version control. | In place |
| CI quality gate | Pull requests run full TypeScript typechecking across the API, shared libraries, and every web app, Python compilation checks, and the unit-test suite (.github/workflows/ci.yml, tests/). | In place |
| Automated tests | Unit tests pin the security-critical contracts: webhook signature validation, tenant-isolation middleware, invite token lifecycle, AI autonomy defaults, consumer consent and send-gate ordering, staff capability enforcement, and audit-record structure (tests/unit/). Three of these are enforced by tests that scan the source tree and fail the build if a new code path bypasses a control — the single outbound message path, staff capability gating on every administrative route, and route registration. They run on every PR and block both development and production deploys. Coverage is expanding with each feature. | Partial |
| Gated production deploys | Production deploys only when a semantic release is cut from main (.github/workflows/production.yml); deploys are serialized, and a deploy-marker system prevents partially-registered releases (scripts/ci-deploy-marker.sh). | In place |
| Peer review | Work lands via pull request; formal branch-protection and code-owner enforcement is being turned on. | Partial |
| Dependency hygiene | Lockfile-pinned installs (npm ci) in CI; automated dependency updates and vulnerability scanning are planned. | Partial |
| Control | Implementation | Status |
|---|---|---|
| Message-delivery monitoring | Every outbound SMS gets a delivery status from the carrier; failures are stamped onto the message and surfaced to the dealer in the conversation thread with a plain-language reason (handlers/api/sms/status, handlers/workers/invoke-agent.ts). | In place |
| Structured logging | All workloads log to CloudWatch; jobs emit structured error records per failed item and continue (no all-or-nothing sweeps). | In place |
| Alerting & on-call | No automated alerting on error rates or job failures yet; operational review is manual. | Planned |
| Administrative audit trail | Every sensitive administrative action is written to an append-only log that is never updated or deleted: dealership provisioning and lifecycle changes, member role changes, invite issue/revoke/accept, messaging-number and carrier-registration changes, autonomy changes, and lead-intake address changes. Each record carries the actor, actor type (staff / dealer user / system), source IP, target, and before/after values, and is retrievable platform-wide or scoped to a single dealership, with CSV export (packages/core/src/admin/audit.ts recordAuditEvent, listAuditEvents; unit-tested). A test prevents credentials from being written into audit detail. | In place |
| AWS account audit trail | CloudTrail / GuardDuty configuration is being formalized as code. This is separate from the application audit trail above. | Planned |
| Log retention limits | CloudWatch retention policies not yet set (currently unlimited). | Planned |
| Control | Implementation | Status |
|---|---|---|
| Managed, multi-AZ services | Lambda, DynamoDB, S3, API Gateway, Cognito are AWS-managed and span availability zones within us-west-2 by default. | In place |
| Production deletion protection | Production stacks are protected and retained on removal (sst.config.ts); the analytics graph has deletion protection in production. | In place |
| Self-healing jobs | Recurring jobs (inventory sync, carrier-registration sweep, attention sweep) are idempotent, claim work with conditional writes, and heal transient failures on the next cycle (handlers/jobs/). | In place |
| Point-in-time recovery | DynamoDB PITR and a documented backup/restore procedure are not yet enabled. | Planned |
| Deferred message durability | A message that cannot be sent immediately because of quiet-hours rules is not dropped: it is persisted to a work registry and retried by a recurring job, which re-checks consent and refuses to send anything still awaiting human approval (packages/core/src/sms/delivery.ts enqueueDeferredSend, flushDueDeferredSends; unit-tested). | In place |
| Capacity limits & headroom | All compute is on-demand and managed. A low account-level concurrency limit was found to be causing request throttling under normal use; an increase has been requested. Automated alarming on capacity headroom is not yet in place — see §7. | Partial |
| Guaranteed capture of failed async work | Background work (AI agent turns, notification fan-out) is invoked asynchronously and retried automatically by the platform, and recurring jobs heal transient failures on the next cycle. Work that still fails after its retries is delivered to a dedicated failure queue with 14-day retention rather than discarded, so it can be inspected and replayed (infrastructure/async-failures.ts, infrastructure/workers.ts, infrastructure/notifications.ts). Automated alerting on queue depth is not yet in place — see §7. | In place |
| Control | Implementation | Status |
|---|---|---|
| Human approval before send | See §5 — new dealers start fully supervised; approval uses atomic conditional writes so a draft can never be sent twice or sent after being replaced (handlers/api/conversations/…/approve). | In place |
| Single outbound message path | Every outbound text — AI-generated, human-approved, or typed by the dealer — leaves the system through one function, which is where consent, carrier-registration, and quiet-hours checks live. A test scans the codebase and fails the build if any other module can send a message, so a new feature cannot bypass the checks (packages/core/src/sms/delivery.ts deliverSms). | In place |
| Carrier-compliance gating | Outbound SMS is blocked until the dealer's A2P 10DLC registration is carrier-approved; blocked sends are recorded with the reason rather than silently dropped (packages/core/src/sms/delivery.ts evaluateSendGates). | In place |
| Inbound de-duplication | Inbound SMS is deduplicated by carrier message ID so retries never create duplicate conversations or double agent replies (handlers/api/sms/inbound). | In place |
| Contract tests | The signature-validation, tenant-authorization, invite, autonomy-default, consent, send-gate, audit-record, and staff-capability contracts are pinned by the unit suite run on every change (tests/unit/). | In place |
| Control | Implementation | Status |
|---|---|---|
| Tenant data separation | Organization ID in the partition key; per-request membership checks (see §2). | In place |
| Staff access boundary | Cross-tenant administration is restricted to the corporate-domain staff pool. Per-staff role tiers and access audit logging are now in place (see CC6 and CC7); what remains is completing group assignment so that an unassigned staff member is denied rather than defaulted to full access. | Partial |
| Analytics graph scoping | The knowledge graph is read-only to agents at the IAM level with write verbs blocked; per-tenant query enforcement inside the graph is being hardened. | Partial |
| Consumer consent records | Texting consent is recorded per consumer phone number per dealership, so one dealer's consent never implies another's, and the record is partitioned under that dealership like all other tenant data. Every state change appends an immutable event capturing the previous and new state, what caused it, and the consumer's own words where applicable — the record of whether and when an opt-out was honored (packages/core/src/sms/consent.ts setConsent, listConsentEvents; unit-tested). | In place |
| Data retention schedules | Short-lived operational records already expire automatically (invites, dedupe and lookup rows). Retention schedules for consumer PII (customers, leads, conversations, raw lead emails) are not yet defined. | Planned |
| Deletion & offboarding | Conversations are deletable with cascading message deletion. A full tenant-offboarding purge and consumer data-subject deletion (CCPA/GDPR-style) are not yet built. | Planned |
| PII minimization in logs | Happy-path logs record lengths/counts, not message content; a sweep to remove remaining PII from error logs is in the roadmap. | Partial |
Dealers evaluating Stryke consistently ask one question first: can the AI text my customers something wrong? The autonomy system is designed so the honest answer is: not without a human seeing it first, unless you have explicitly earned and enabled a higher autonomy stage.
packages/core/src/conversations/autonomy.ts; unit-tested).handlers/workers/invoke-agent.ts, packages/core/src/conversations/confidence.ts).held → approved) that happens before the send; double-clicks and stale drafts are rejected, and a failed send reverts the draft to held.packages/core/src/sms/a2p.ts, packages/core/src/sms/a2p-registration.ts).packages/core/src/sms/delivery.ts evaluateSendGates).packages/core/src/sms/delivery.ts evaluateSendGates, packages/core/src/sms/consent.ts).packages/core/src/sms/consent-rules.ts matchConsentKeyword, packages/core/src/sms/opt-out-classifier.ts).packages/core/src/sms/consent-rules.ts quietHoursVerdict).Maintained honestly and reviewed as part of our SOC 2 readiness program. "Near-term" items are actively being scheduled; "mid-term" items follow. Items are removed from this register only when the corresponding control appears in §4 with a citation. Platform-enforced SMS opt-out suppression, per-consumer consent records, and quiet hours were previously the highest-priority item here and have shipped — see §6.
| # | Gap | Criteria | Horizon |
|---|---|---|---|
| 1 | Enforce MFA for dealer users and Stryke staff; explicit password/account-recovery policy | Security | Near-term |
| 2 | Complete staff role-group assignment so an unassigned staff member is denied by default rather than granted full access | Security / Confidentiality | Near-term |
| 3 | DynamoDB point-in-time recovery + documented backup/restore procedure | Availability | Near-term |
| 4 | Per-member removal/deactivation with session revocation (dealership-level suspension is in place) | Security | Near-term |
| 5 | CloudWatch alarms and error alerting (including failure-queue depth and capacity/concurrency headroom), and an on-call/incident-response runbook | Monitoring / Availability | Near-term |
| 6 | Data-retention schedules and TTLs for consumer PII; tenant-offboarding purge; DSAR deletion. Consent records are deliberately excluded from deletion, as they are the evidence that an opt-out was honored | Privacy | Mid-term |
| 7 | Eliminate long-lived deploy credentials in favour of short-lived role assumption, and tighten remaining wildcard IAM grants | Security | Near-term |
| 8 | CloudTrail/GuardDuty as code; CloudWatch log-retention policies; PII log-scrubbing sweep | Monitoring / Privacy | Mid-term |
| 9 | Per-tenant enforcement inside the analytics graph; per-tenant intake credentials | Confidentiality | Mid-term |
| 10 | Branch protection + required review enforcement; automated dependency/vulnerability scanning | Change mgmt | Mid-term |
| 11 | Inbound rate limiting and abuse/cost-exhaustion controls on public message and lead-intake endpoints | Security / Availability | Mid-term |
| 12 | AI content guardrails beyond the reviewer; prompt-injection hardening of lead free-text; adversarial testing program | PI / Security | Mid-term |
| 13 | Expand unit/integration test coverage beyond the security-critical core | Change mgmt | Ongoing |
| 14 | Customer-managed KMS keys; WAF on public endpoints; multi-region resilience assessment | Security / Availability | Mid-term |
This document is only useful if it stays true. Conventions that keep it maintainable:
Prepared from a direct review of the platform's infrastructure-as-code and application code. Questions, evidence requests, or security reports: engineering@strykeauto.com.