[ Security // threat_model ]

Threat model & data flow

A credential manager should be judged on specifics, not adjectives. This page states what SPCTR encrypts, where keys come from, what our servers can see, what happens when a device is lost, and what the browser extension is allowed to touch.

Beta testers: please use dummy accounts. Full engineering detail lives in docs/SECURITY.md in the codebase; this page and that document are kept in step.

1 — What is encrypted

Every vault item — logins, TOTP seeds, secure notes, cards, identities, SSH/API keys, and passkeys SPCTR stores for other sites — is encrypted on your device with AES-256-GCM and a fresh random 96-bit IV before it is sent anywhere. Each item is its own record.

What is not encrypted, because the system needs it to route data: your account email, the item type label (item_kind), schema version, byte size, revision counter, and created/updated timestamps.

2 — Where keys are derived and stored

  • Your passphrase is turned into a key with Argon2id (64 MiB memory, 3 passes, 1 lane) against a random 16-byte salt. A legacy PBKDF2-SHA-256 (250,000 iterations) path is retained so older clients keep opening existing vaults.
  • That key is a key-encryption key. It unwraps the data key that actually seals your items — which is why changing your passphrase does not re-encrypt every item, and why the Argon2id rollout was reversible.
  • Keys live in memory for the session only. Optional convenience caching keeps the key in browser session storage for at most one hour, and you can turn that off in Settings.
  • The passphrase itself is never transmitted, never logged, and cannot be reset by us.
  • Passkey unlock wraps the data key with a secret derived from your authenticator's WebAuthn PRF output. Authenticators without PRF fall back to a device-local key held in IndexedDB that never leaves that device. A FIDO2 security key (YubiKey 5.2.3+) produces a portable wrap that works on any machine.

3 — What the server can see

Everything our backend holds, exhaustively:

  • Account email address and authentication events (time, coarse location, device label).
  • Opaque ciphertext blobs, their type label, size, revision, and timestamps.
  • Public key-derivation parameters: salt, algorithm, and cost settings.
  • The verifier — the ciphertext of a fixed constant, used to check a passphrase.
  • Email mask addresses and their forwarding target (that is what makes them work).
  • Registered passkey metadata: credential ID, public key, counter, device label.
  • Plan/entitlement state and support tickets you send us.

Never present, anywhere: plaintext item contents, your passphrase, a derived key, or your recovery phrase (only a hash of it is stored).

4 — Data flow

  YOUR DEVICE                          SPCTR BACKEND
  ─────────────────────────            ───────────────────────
  passphrase
     │ Argon2id (64 MiB, 3 passes)
     ▼
  key-encryption key ──unwraps──▶ data key (AES-256-GCM)
                                        │
  item (title, username, secret)        │
     │ encrypt on device                │
     ▼                                  ▼
  { v, iv, ct } ─────── https ────▶ sync_blobs row
                                    ( ciphertext + item_kind
                                      + schema_v + timestamps )

  The key never crosses the line. The server stores blobs it
  cannot open, and returns them unchanged on the next sync.

5 — Recovery, and what is unrecoverable

  • A 12-word recovery phrase is issued at first sign-in and stored server-side only as a hash. It authorises step-up and account recovery; it does not reveal the data key.
  • If an account has a passkey on this domain, a magic-link session must clear a passkey step-up before anything account-critical.
  • Losing the passphrase, every unlock key, and the recovery phrase means the data is gone. We cannot recover it, and any service that claims it could is not zero-knowledge.
  • Because of that, an account must hold one backup factor beyond the passphrase — a recovery phrase, a second device, or a security key — before it may store website passkeys.

6 — Reproducible export / restore test

In the app, Vault → Backup & restore writes a single .spctr file containing the same ciphertext the server holds, plus the public key-derivation parameters. No plaintext, no key material, no upload.

  1. Export a backup and note the SHA-256 fingerprint shown.
  2. Open the file in a text editor: confirm you can see only iv / ct blobs and KDF parameters.
  3. Use Verify backup with the correct passphrase — it decrypts everything in memory and reports item counts per type without writing anything.
  4. Try a wrong passphrase, and try altering one character of a ciphertext: both must fail.
  5. Restore into an empty test account and compare item counts and contents.

7 — Lost or stolen device

Documented response, every step doable by you without contacting support:

  1. Sign in on another device → Settings → Security & sessions → Sign out all devices. This clears sessions and trusted devices.
  2. Settings → Passkeys: revoke the credential registered on the lost device. Unlock keys: remove that device's vault unwrap key so the vault can no longer be opened there without the passphrase.
  3. Settings → Browser extension: revoke the pairing for that machine.
  4. Settings → Change vault passphrase: this re-seals every item under a new key. Note that backups taken earlier still open with the old passphrase — destroy or re-take them.
  5. Settings → Recovery: issue a fresh 12-word phrase, which invalidates the previous one.

What an attacker holding the device can do: nothing without the passphrase, unless the vault was left unlocked with convenience caching on (≤ 1 hour window) or the device biometric/PIN protecting the local unlock key is also compromised. What they cannot do: derive your passphrase from anything stored on the device, or read blobs pulled from the server.

8 — What the browser extension can access

  • Host access to all sites, required for autofill, save-on-submit prompts, one-time-code filling, and acting as a passkey provider. It reads form fields on the page you are interacting with; it does not scrape or transmit page content.
  • Storage, clipboard write, active tab, scripting, tabs, alarms, notifications. No analytics SDK, no remote code execution, no telemetry endpoints.
  • The extension holds no long-lived plaintext: it decrypts with the same session key model as the web app and wipes keys on lock, on browser restart, and when the popup is dismissed for a locked vault.
  • A stolen account session alone is useless to it — the vault stays sealed until the passphrase or an unlock key is presented.

9 — Out of scope / accepted risks

  • A compromised endpoint: malware, a keylogger, or another hostile extension with host permissions.
  • Takeover of your email account: it can start a magic-link sign-in. It still cannot decrypt anything, and passkey step-up blocks account-critical actions.
  • Metadata correlation: item counts, sizes, and sync times are visible to the server by construction.
  • Physical coercion, and rubber-hose attacks generally.
  • Email masks: forwarded mail transits our relay in plaintext, because SMTP is not end-to-end encrypted. Treat masks as anti-tracking, not as confidential mail.

10 — Reporting

Send findings to security@spctrmail.com with reproduction steps and the affected version. We reply to anything reproducible and credit reporters who want it. No bounty programme yet; we say so rather than implying one.

Request beta access · Privacy policy