Security

A scheduler holds the keys to your audience. Here is, concretely, how ScheduleLater holds them. Built to the OWASP ASVS 5.0 Level 2 baseline, self-assessed.

Isolation the database enforces

Every tenant table carries forced row-level security in Postgres, keyed to the workspace. The application connects as a role that cannot bypass it, so a query missing its workspace filter returns nothing rather than someone else’s data. Isolation is a property of the database, not a habit of the code.

Tenancy
Workspace-scoped row-level security, forced, fail-closed.
Runtime role
Non-superuser, no RLS bypass, data-access grants only.
Migrations
Run separately as the owner role; the app cannot alter schema.

Tokens treated like the credentials they are

Platform OAuth tokens and two-factor secrets are envelope-encrypted with AES-256-GCM: a unique key per row, wrapped by a versioned master key that rotates by re-wrapping, never by re-encrypting your data. Tokens never appear in logs, and disconnecting an account revokes the platform grant before deleting the token.

At rest
AES-256-GCM envelopes, per-row keys, versioned rotatable master key.
Passwords
argon2id with a versioned pepper, checked against known breaches at entry.
Disconnect
Revokes the platform grant, deletes the token, purges cached platform data.

Accounts that are hard to take over

Sign-in is email and password with mandatory email verification. TOTP two-factor is available to everyone and required for owners and admins. Sensitive actions, like connecting a platform, minting an API key or disabling two-factor, require fresh re-authentication even inside a valid session.

Two-factor
TOTP with single-use recovery codes; required for owner and admin roles.
Sessions
12-hour idle window, 30-day absolute cap, revoke-everywhere on password reset.
Rate limits
Per-account and per-address on auth endpoints, failing closed on outage.

Publishing that cannot double-post

A post is claimed for publishing by one atomic database operation; a second worker gets nothing to publish. A crash mid-publish leaves a claim that recovery reclaims safely, and platforms that confirm late are tracked until they answer. Reliability here is a security property: the one thing worse than a missed post is the same post published twice.

Idempotency
Database-level atomic claim; YouTube uploads carry a resumable cursor on top.
Recovery
Stuck posts, stalled transcodes and unanswered publishes are swept automatically.
Verification
A YouTube upload is confirmed accepted after processing, not assumed from a 200.

The perimeter

Every page ships a per-request nonce Content-Security-Policy. Every state-changing request is origin-checked and rate-limited. Server-side fetches of user-supplied URLs resolve and pin addresses to defeat rebinding. Logs are redacted by a single shared policy that knows every secret’s shape. This very site runs the same strict CSP with zero third-party requests.

Questions we did not answer here?

The app’s terms and privacy pages carry the formal commitments.