Skip to content
Security

Security you can read.

opendesk handles your customers’ email, so security is a property of the code, not a page of promises. The code is open, and these are the rules every change follows.

Assessment
ADA CASA Tier 1
Standard
OWASP ASVS 4.0.3, Level 1
Applies to
opendesk Cloud and every self-hosted install
Source
Apache-2.0, public on GitHub

CASA Tier 1

opendesk connects to Gmail with restricted Google scopes, so its Google OAuth client is assessed under the App Defense Alliance Cloud Application Security Assessment, Tier 1 (OWASP ASVS 4.0.3 Level 1).

Encrypted in transit and at rest

TLS for every connection, encryption at rest by our providers, and application-level AES-GCM for every stored credential.

Open to inspection

The code that runs the cloud is public. You, your security team or anyone else can audit it.

§ 1

What the code guarantees.

Each control is a rule every change follows, held by the code that runs both the cloud and every self-hosted install. Here are some of them at work.

Two-factor that cannot be replayed

A code from an authenticator app at every sign-in. A code that was already used is refused, and recovery codes are hashed and single use.

Sessions you can see and end

Refresh tokens rotate on every use and are stored hashed. Signing a device out ends its session and closes its open sockets at once.

An audit log of every security event

Sign-ins and failures, two-factor and password changes, member and role changes, API keys, webhooks and mailbox connections, with who, when and from where.

Secrets sealed by the app

Mailbox passwords, OAuth tokens, webhook secrets and two-factor secrets are encrypted with AES-GCM before they are stored, on top of encryption at rest.

Webhooks that only reach the internet

Webhook and other outbound URLs must be HTTPS, and are refused when they point at private, loopback or link-local addresses.

Every workspace walled off

Every read and write is scoped to the caller’s workspace on the server, and any record it references is checked to belong to the same one.

Logs that keep secrets out

No secret, token, password or request body ever reaches a log line.

"msg": "http",
"method": "POST",
"path": "/v1/auth/login",
"status": 200,
"ms": 182

Scanned on every change

Dependencies and images are checked for known vulnerabilities in CI.

govulncheckGo
pnpm auditweb
mix deps.auditrealtime
Trivyimages

Deleted means deleted

Users can delete their account, and owners their workspace. Data is removed, not hidden.

Delete your account
Removes your account and every workspace you are the only member of.
Delete account
§ 2

Every control, written down.

The invariants behind the scenes above, numbered and grouped the way an assessor reads them.

2.1Authentication

  1. 2.1.1Passwords are hashed with Argon2id, 12 to 128 characters, and checked against a list of breached passwords.
  2. 2.1.2Sign-in, sign-up and password reset are rate limited per IP and per account, and protected by Turnstile.
  3. 2.1.3Sign-in answers the same, in the same time, for an unknown email and a wrong password.
  4. 2.1.4Two-factor with TOTP refuses a code that was already used. Recovery codes are hashed and single use.
  5. 2.1.5Sensitive owner actions, such as deleting a workspace or disabling two-factor, need a recent sign-in and two-factor.

2.2Sessions and tokens

  1. 2.2.1Access tokens live 15 minutes. Refresh tokens rotate on every use and are stored hashed.
  2. 2.2.2Every token carries a purpose and is accepted by exactly one consumer.
  3. 2.2.3Signing out, changing a password or removing a member ends sessions and closes open sockets.
  4. 2.2.4Cookies are HttpOnly, Secure and SameSite=Lax, scoped to the path that needs them.
  5. 2.2.5API keys are shown once, stored hashed, scoped and bound to one workspace.

2.3Access control

  1. 2.3.1Every read and write of workspace data is scoped to the caller’s workspace on the server.
  2. 2.3.2Any referenced record, such as a contact, team or mailbox, is checked to belong to the same workspace.
  3. 2.3.3Roles are owner, admin, agent and viewer. Viewers read only, and settings need an admin.

2.4Data protection

  1. 2.4.1Traffic is encrypted in transit with TLS. Data at rest is encrypted by our storage providers.
  2. 2.4.2Mailbox passwords, OAuth tokens, webhook secrets and two-factor secrets are also encrypted with AES-GCM by the application.
  3. 2.4.3Email and agent HTML is sanitized before it is stored.
  4. 2.4.4Webhooks and other outbound requests go to HTTPS addresses only, never to private or internal networks.
  5. 2.4.5Logs never contain secrets, tokens, passwords or request bodies.

2.5Accountability

  1. 2.5.1Security events are kept in an audit log: sign-ins and failures, two-factor and password changes, member and role changes, API keys, webhooks, and mailbox connections.
  2. 2.5.2Users can delete their account, and owners their workspace. Data is removed, not hidden.
  3. 2.5.3Dependencies and images are scanned for known vulnerabilities in CI.

2.6Where the cloud runs

The API and realtime gateway run on AWS. The web app, this site and inbound email routing run on Cloudflare. Data is stored in Postgres hosted by PlanetScale on AWS, reached only over AWS's private network. Product analytics run without tracking cookies on this site.

Self-hosted installs send nothing to us. You choose where they run and what they connect to.

Responsible disclosure

Found a vulnerability?

Email [email protected] with what you found, how to reproduce it and the impact you expect. Please do not open a public issue, and give us a reasonable time to fix it before you share it.

  • We acknowledge reports within 3 business days.
  • We keep you updated until it is fixed, and credit you if you want.
  • We will not pursue research done in good faith: test only accounts you own, do not access other people’s data, and do not degrade the service.
[email protected]

More detail for self-hosters is in the security docs.

Give every customer a calm, good answer.

Start on the cloud in a minute, or self-host it tonight. It is the same code either way.

Start free
$curl -fsSL https://opendesk.sh/install.sh | sh