Security Whitepaper
Version 1.0 — July 2026 · Operator: JAD Apps (sole trader), Wellington Close, Warsop, Mansfield, Nottinghamshire, NG20 0JL, UK · support@jadapps.app
1. What SecureSend is
SecureSend is a one-time file handover service. A sender encrypts a file in their own browser; the service escrows only a small one-time decryption key and releases it exactly once — to whoever presents the correct link token and PIN — destroying it in the same operation. The encrypted file travels by whatever channel the sender already uses. SecureSend never receives, stores, relays, or can read the file.
2. Architecture
The service runs entirely on Cloudflare Workers and Durable Objects. There is no object storage, no database server, and no file upload endpoint anywhere in the codebase — API request bodies are limited to a few hundred bytes of metadata.
- Client-side encryption: AES-256-GCM, chunked, in the browser (or CLI). The content key is generated server-side, returned to the sender exactly once at seal time, and stored only in wrapped form (AES-GCM under a master key held as a Worker secret).
- FileDO (one per transfer): a single-threaded Durable Object holding the wrapped key, hashed link token, hashed PIN, and transfer metadata. The atomic burn: verification of token + PIN, removal of the wrapped key from storage, and the state change to OPENED happen in one uninterruptible transaction. Exactly one opener can ever win.
- Lockout: five failed PIN attempts destroy the key permanently (state ASH). There is no recovery, reset, or admin override.
- Expiry: unclaimed transfers burn automatically at their TTL (organisation-configurable cap, default 7 days). Transfer records are fully erased 90 days after reaching a terminal state.
3. What the service holds, and for how long
| Data | Where | Retention |
| Wrapped one-time key | FileDO | Until first open, lockout, or TTL expiry — destroyed atomically |
| Transfer metadata (filename shown to recipient, size, hashes of token/PIN) | FileDO | Erased 90 days after terminal state |
| Account/subscription records (email, tier, Stripe customer id) | AccountDO / OrgDO | Life of the account |
| Business audit events (see §5) | AuditDO (one per organisation) | 24 months, then swept — the sweep is itself an audit event |
Never held anywhere, in any form: file contents; unwrapped content keys after seal; PINs or link tokens in plaintext (hashes only).
4. Threat model
- Service compromise / malicious operator: the service holds no file bytes, so a full compromise of our infrastructure cannot disclose file contents. Wrapped keys plus the master secret could release one-time keys — but the corresponding ciphertext never passed through us.
- Link interception: a link alone is insufficient — the PIN travels separately (optionally by a second channel, e.g. SMS) and five wrong attempts destroy the key.
- Replay / double-open: impossible by construction; the key release and its destruction are one write in a single-threaded Durable Object.
- Malicious recipient with the correct credentials: out of scope — that is the intended recipient by definition (see §7).
- Denial of service on one transfer: anyone with the link can burn the key by five wrong PINs. This is a deliberate fail-safe bias: destruction beats disclosure.
5. Business audit trail — exact schema
For Business organisations only, every lifecycle transition is recorded in an append-only, per-organisation store. The complete schema — there are no other fields:
| Field | Content |
| timestamp | UTC, at event emission |
| type | One of: org_created, send_created, send_refused, delivery_dispatched, open_success, open_denied, lockout, burn_expired, burn_cancelled, seat_invited, seat_accepted, seat_revoked, seat_role_changed, settings_changed, export, retention_sweep, org_offboarded |
| actor | Seat email, "recipient", "billing", "operator", or "system" |
| outcome | Short status code (e.g. ok, wrong_pin, locked_out, revoked_seat) |
| recipient | Delivery contact exactly as the sender entered it (org sends only) |
| size | File size in bytes |
| label | Optional sender-supplied label — never the filename |
| file_id | Opaque random UUID for correlation |
| detail | Short whitelisted context (e.g. TTL, attempt count) |
Events are written after — never inside — the atomic key transaction, and no field permits reconstruction of file contents. Filenames are excluded from the trail by construction: the event constructor accepts only the fields above. The trail has no update or delete API for anyone, including us; the only removals are the 24-month retention sweep and offboarding deletion, both recorded as events. Every export is itself an audit event.
6. Sending infrastructure
Delivery emails are sent via Resend from the verified jadapps.app domain. Organisation branding changes only the display name, reply-to, and body of the email — the sending address and domain never change, so SPF, DKIM and DMARC alignment are structurally identical for every organisation. See the subprocessor list.
7. Honest limits
Consistent with our public claims, and stated here for your assessor:
- No control after open. Once a recipient decrypts a file we cannot prevent copying, forwarding, or printing. "Burn on read" is a guarantee about the one-time key, not digital rights management.
- No content scanning — by design. We cannot scan files for malware or data-loss because we never have them. Your endpoint controls remain your responsibility.
- Jurisdiction, not residency. The service runs on Cloudflare's global edge. Durable Objects storing keys and audit data reside in Cloudflare's network with the operator based in the UK; we do not currently promise UK-only data residency.
- Metadata is visible to your admins. The Business audit trail deliberately records who sent what size of file to which contact, and when. Senders are told this in the product.
- Anyone with the link can destroy the transfer (five wrong PINs). We consider this the correct failure mode for sensitive handovers.
- The sender's browser matters. Encryption happens client-side; a compromised sender or recipient device defeats any transfer service, including this one.
8. Operational security
- Secrets (master wrapping key, API credentials) are Cloudflare Worker secrets, never in code or version control.
- The operator admin panel is gated by Cloudflare Zero Trust (identity-verified at the edge and re-verified in the Worker on every call).
- All traffic is TLS; the decryption key fragment in an open link lives in the URL fragment, which browsers do not transmit to the server.
- Dashboards authenticate via Google Sign-In with short-lived, HMAC-signed sessions; org seat checks are live, so revocation takes effect on the next request.
Contact
Security questions or disclosures: support@jadapps.app. See also: data-flow diagram · template DPA · subprocessors · privacy notice.