AllyHub
Security · full whitepaper

Ally cabinet security whitepaper

Rev. 2026.08. For an executive summary see the security overview.

1. Threat model

The Ally cabinet holds three sensitive asset classes: (a) the client's personal notes with a mentor; (b) authentication material that lets a user in; (c) billing data. The threats we design against, in priority order: mass credential stuffing (mitigated by absence of passwords); targeted account takeover via email interception (mitigated by short token life and optional 2FA); insider misuse of cabinet content (mitigated by role-based access, quarterly review, and immutable audit logging); mass data breach via database compromise (mitigated by encryption at rest and defence-in-depth on the application tier).

2. Passwordless authentication

Ally never stores passwords. The sign-in flow generates a cryptographically random 128-bit token, sends it as a link to the user's email, and hashes it with Argon2id before writing to the database. When the user clicks the link, we recompute the hash and compare in constant time; a match resolves to the account. Tokens live thirty minutes and are single-use — a second click gets a hard failure. A same-browser check on the click side defends against email interception: the server records a browser fingerprint on the request that generated the token and compares it to the click; mismatches require a second confirmation step.

3. Two-factor authentication

Optional TOTP per RFC 6238. Shared secret is generated on the server, transmitted to the client's browser once, displayed as a QR for the authenticator app to consume, then stored server-side encrypted at rest. Verification is time-window ±1 (30 seconds each side of the current window). Recovery codes are eight random 12-character strings, hashed with Argon2id, shown to the user once and downloadable as PDF. On use, a recovery code is invalidated and a fresh set is regenerated at the next sign-in.

4. Sessions

A session is a first-party cookie aa_sid holding a 256-bit random identifier tied to a server-side session record. The record stores account ID, issued timestamp, browser fingerprint (rounded to family + major version + OS + locale to reduce entropy and preserve privacy), and last-active timestamp. Sessions are either 30-day sliding (remember-me) or browser-lifetime. Revoking a session marks the server record as invalid; the client cookie is not touched but is rejected on the next request.

5. Video architecture

Ally runs LiveKit on our own Hetzner infrastructure. The signalling server orchestrates the WebRTC connection; the media relay handles NAT traversal and multi-participant routing but does not decrypt payload. Media is end-to-end encrypted between browsers via the browser's built-in WebRTC SRTP with an added layer of Insertable Streams AES-GCM keys distributed via the E2EE key-management protocol. AllyHub servers do not have access to the plaintext media. Recording, when enabled by both parties, is captured client-side, encrypted with a session key derived from the meeting key, and uploaded to Cloudflare R2 tagged with the cabinet ID; only the two participants can decrypt.

6. Data at rest

Primary database: PostgreSQL 16 with transparent AES-256 encryption on the underlying block storage (Hetzner-managed encryption keys) plus application-level encryption on the most sensitive columns (magic-link hashes, 2FA secrets, recovery-code hashes). Object storage: Cloudflare R2 with server-side encryption. Backups: pg_basebackup + WAL, encrypted with a rotating GPG key, retained 35 days rolling on a separate Hetzner project. Backup restore drills conducted quarterly.

7. Data in transit

TLS 1.3 with HSTS preload on all client-facing endpoints; TLS 1.2+ on internal service-to-service calls. Certificate authority: Let's Encrypt with 60-day rotation. Static Diffie-Hellman removed; only ephemeral key exchange. CAA record restricts issuance to Let's Encrypt.

8. Application security

The application is written in TypeScript on Node.js 20 LTS. Framework: Hono + Zod for input validation. Rate limiting at the edge (Cloudflare) and per-account (application). CSP with nonce-per-request; no inline scripts except our own signed ones. SameSite=Strict on the session cookie; HttpOnly and Secure flags always set. CSRF handled via double-submit token pattern.

9. Staff access

Access to production data is role-based. Support engineers can read a cabinet only when the account holder has opened a support ticket referencing it; access auto-expires when the ticket closes or after 72 hours, whichever comes first. All admin actions write to an append-only audit log stored on a separate Hetzner project. Quarterly access review: two directors verify each active grant.

10. Vulnerability management

Dependencies scanned daily by Snyk; high or critical CVE requires patch within 72 hours. Annual independent penetration test by a CREST-registered UK firm; latest report available under NDA. Bug-bounty programme via HackerOne (private, invite-only) with rewards up to £5,000 for critical.

11. Incident response

Incident commander rotation: one director on-call at all times. On confirmed personal-data breach we notify the ICO within 72 hours per UK GDPR Article 33 and affected users without undue delay. Post-incident review is public within 30 days on the changelog, with root cause and remediation.

12. Sub-processors

Full list in the DPA section 9. Notified 30 days in advance of any change.

13. Compliance

UK GDPR + DPA 2018 + PECR + Consumer Rights Act 2015. SOC 2 Type II audit programme in progress (target report Q1 2027). ISO 27001 planned for 2027.

14. Backup and disaster recovery

PostgreSQL primary is replicated synchronously to a hot standby in a different Hetzner failure zone. Nightly encrypted pg_dump plus continuous WAL archiving to Cloudflare R2 in London. RTO target 30 minutes; RPO target 5 minutes. Quarterly failover drill involving both directors and the on-call engineer, results published to the audit log.

15. Coordinated vulnerability disclosure

We welcome security research. Rules of engagement: no automated scanning beyond passive checks; no denial-of-service; no social engineering of staff or clients; no testing against another user's cabinet without their written consent; give us 90 days to remediate before public disclosure. Rewards range from a thank-you and a bottle of wine (low severity) to £5,000 for a critical remote-code-execution finding. Contact: security@allyhub.org.

16. Contact

Security questions and coordinated disclosure: security@allyhub.org. PGP fingerprint available on request. DPO: dpo@allyhub.org.