Skip to main content
Trust

security & vendor-risk overview

This page is the canonical answer to the security questionnaires partner publishers and enterprise listeners send. We aim to make due-diligence a 5-minute read instead of a 5-week PDF round trip. Anything missing? Email story@storyflo.com.

Last updated: 2026-05-08 · Version 1.0

quick facts

Company stagePrivate alpha
Production environmentVercel (frontend) + Fly.io (backend) + Cloudflare R2 (audio)
Primary data locationUS (sjc); EU listener data on Cloudflare R2 EU jurisdiction (WEUR)
Encryption in transitTLS 1.2+ everywhere; HSTS preloaded
Encryption at restFly Postgres (AES-256), R2 (AES-256), listener BYO keys (Fernet)
Authentication for listenersToken in URL (magic-link distribution); no passwords
Authentication for publishersPer-tenant access_token; magic-link OAuth on roadmap
Authentication for operators/admin/login + cookie + 30-day remember-me
SOC 2Type 1 audit planned within 12 months of public launch
GDPRDPA at /legal/dpa; DSR self-service at /legal/gdpr
Pen-testInternal red-team only during alpha; external pen-test at GA

data residency

storyflo's primary infrastructure runs on Fly.io in San Jose (sjc). Multi-region scaling to iad (US East) and fra (Frankfurt, EU) is on the immediate roadmap; until then EU listeners hit sjc with a higher latency.

Audio cache (R2): three buckets — storyflo-audio-cache (default global), storyflo-audio-eu (Cloudflare WEUR jurisdiction), and storyflo-audio-apac (Cloudflare APAC). Newly-rendered audio auto-promotes to all three; the region-aware url_forpicks the closest bucket based on the listener's CF-IPCountry header.

Postgres: Fly Managed Postgres, primary in sjc. Read replica in fra for EU latency is on the short-term roadmap. Daily encrypted backups; 30-day retention. Point-in-time recovery (PITR) being enabled this quarter.

encryption

  • In transit: TLS 1.2+ enforced everywhere. HSTS with max-age=63072000; includeSubDomains. Strict-Transport-Security preload list inclusion.
  • At rest (Postgres): Fly Managed Postgres uses AES-256 encryption at the volume level.
  • At rest (audio cache): Cloudflare R2 encrypts every object with AES-256. Bucket access is keyed (no public ACLs except for the read-only audio CDN).
  • Listener-supplied API keys (BYO-TTS): encrypted with Fernet (symmetric AES-128-CBC + HMAC) at rest using the BYO_TTS_ENCRYPTION_KEY secret. Plaintext never logged, never returned via the API; only the last 4 characters surface in the UI for identification.
  • Webhook signatures:all outbound webhooks signed with HMAC-SHA256 using the publisher's dedicated webhook secret. Receivers verify with X-Storyflo-Signature.

access controls

  • Listener data is keyed by listener_token. The token IS the credential. No cross-tenant lookup paths exist.
  • Publisher dashboardrequires the publisher's access_token on every request. Tokens are 64-hex secrets shown ONCE at onboarding.
  • Admin endpoints require the X-Storyflo-Email-Token header. Token lives on Fly secrets, never on disk.
  • Operator accessto the production database is via Fly's SSH-tunneled fly postgres connect. SSH access requires hardware-key 2FA on Fly account.
  • Provider creds (OpenAI, ElevenLabs, Circle, Stripe) live exclusively on Fly secrets. Rotation: documented in the on-call runbook (linked from /admin).

sub-processors

Full list maintained on the DPA page. Material changes communicated by email to all affected publishers + listeners with 30-day notice.

incident response

Detection: Sentry (errors), Telegram alerts (cost-guardrail breaches, error-budget burn, reward-worker stalls, dead-letter queue depth). All alerts route to the on-call human within seconds.

Triage: 24h ack target; full SLA on /security. Severity classification: Critical (RCE, full-DB read, auth bypass) → 7-day fix; High (privilege escalation, data leak) → 30-day; Med/Low → 90-day.

Notification: Affected publishers / listeners notified within 72 hours of confirmed breach (per GDPR Art. 33). Public post-mortem published on /changelog within 30 days for any incident affecting more than 10 listeners or any single publisher's data.

business continuity + disaster recovery

  • RTO (recovery time objective): 4 hours for full restoration from a region-wide Fly outage, assuming Postgres backup is replayable.
  • RPO (recovery point objective): 24 hours currently (daily encrypted backup). Drops to ~5 minutes once PITR ships.
  • Audio durability: R2 stores objects with 99.999999999% (eleven nines) durability. Multi-region promotion provides additional redundancy.
  • Audio renderability: render-job rows are idempotent on retry; if a worker crashes mid-render the job re-claims from queue. Maximum 3 retry attempts before the dead-letter queue surfaces it.

platform posture

Storyflo is a proprietary platform with an intentionally open API surface — agents, models, and integrators can reach us through public endpoints, but the platform itself isn't a self-hostable codebase.

  • Open by design: the MCP server at api.storyflo.com/mcp/v1, the OAuth 2.1 flow, the REST API, the OpenAPI tool spec, and the published SDK packages (storyflo-sdk on npm, storyfloon PyPI, both MIT) — that's the agent / model integration surface.
  • Closed by design: the rendering router, the newsletter parser, the audio cache topology, the x402 pricing logic, the publisher Stripe Connect flow, and all platform infrastructure stay private.

Publisher security teams who need a deeper look-under-the-hood for vendor-risk review can request read-only audit access via security@storyflo.com.

open questions / not yet there

Where we're honest about gaps:

  • No SOC 2 audit yet. Type 1 planned within 12 months of public launch; vendor-risk teams comfortable accepting this lifecycle stage during alpha can use this page as the equivalent control narrative.
  • No external pen-test yet. Internal red-team only. External pen-test scheduled at GA.
  • No paid bug-bounty program yet. See /security for our pre-bounty commitments and the 6-month timeline to launch one.
  • No HIPAA / PCI-DSS scope. We don't process health data or store payment-card numbers (Stripe handles all card data).
listen anywhere

create your storyflo · everywhere you listen.

create your private feed →