Credyman encrypts vault contents in the browser before upload. The server stores encrypted data, account metadata, and sign-in verifiers. Passwords and decrypted vault keys stay in the browser. The encryption design has not received an independent audit.
Signing in opens the account; unlocking makes saved items readable. The browser derives a separate sign-in credential, which the server stores as a salted hash. A stolen credential can access the account but cannot directly decrypt the vault. Use HTTPS in production and keep this credential out of logs.
New passwords need 8 to 128 Unicode code points and a strength score of at least two; spaces are preserved. The browser enforces this policy, while API schemas accept derived credentials and reject raw passwords.
WebAuthn verification checks origin, hostname, challenge, signature, ownership, and user verification. Challenges expire after five minutes and work once. Authenticator private keys stay outside the app.
Sessions use random tokens stored as hashes. Cookies are HttpOnly and SameSite Strict, with Secure enabled in production. Sessions expire after 24 hours or 15 minutes of inactivity. Sensitive changes require verification within five minutes. Email and password changes revoke other sessions.
The client rechecks visible sessions every minute, with a ten-second timeout. /api/auth/me does not extend idle time. Expiry clears local state; connection failures return to login without revoking the cookie.
Client cryptography uses Argon2id, HKDF-SHA256, and AES-256-GCM. Authentication and vault protection use separate salts and purposes; the API independently hashes sign-in credentials. Client and server constants define algorithm parameters.
A random root key protects data keys; each write uses a fresh data key and nonce. Key wrappers bind account/vault identity, generation, protection mode, and relevant KDF settings. Records also bind their ID and revision. Unsupported formats and profiles are rejected.
The encrypted catalogue indexes titles, websites, groups, categories, and Trash. Records hold usernames, passwords, and notes. The client verifies membership, revisions, and record/key-bundle digests before showing data. Storage limits bound full-vault key updates.
Vault writes save catalogue and records together, rejecting outdated revisions. Device checkpoints detect known older snapshots, replacements, and conflicting data at the same revision. A fresh device without a checkpoint cannot independently identify the latest valid snapshot.
| Action | Result |
|---|---|
| Password sign-in | Unlocks password-only vaults locally. Account Secret protection also needs the separate secret, entered or remembered. |
| Passkey sign-in or reload | Leaves the vault locked until its unlocking secrets are provided. |
| Recovery | A signed-in user opens the vault with the recovery key. It does not reset the account password or replace the keys. |
| Password change | Updates authentication and vault password protection together. Saved records, recovery key, and protection mode stay the same. |
| Key rotation or protection change | Re-encrypts records with new keys and replaces the recovery kit. Requires recent verification and confirmation that the new kit is saved. |
Setup requires saving the kit before the first upload. Recovery does not sign in to the account. Mailbox verification and provider password reset are unavailable; the provider cannot reconstruct lost recovery material.
Automatic locking defaults to five minutes, with browser settings for 1, 2, 3, 5, 10, 30, or 60 minutes per account. User activity renews the deadline; background requests do not. Locking clears plaintext, stops the worker, discards pending results, and notifies supported tabs. Session expiry remains independent.
Trusted devices can retain an encrypted Account Secret in IndexedDB with a non-exportable browser key, but no password or root key. Scripts on that origin can still use storage. Memory cleanup cannot guarantee physical erasure or clear clipboard copies.
Keep deployment credentials in ignored environment files or a hosting secret store. The environment initializer generates database passwords; example files contain replacement markers only. Never use an example value as a deployed password.
Generate authentication salts, credentials, and account passwords at test runtime with node:crypto. Keep fixed inputs only where a test needs a known cryptographic vector, a password-policy boundary, or a rejected/mock payload. These values must never authenticate a deployed account.
Run a local secret scanner against a snapshot of Git-tracked and non-ignored new files before publishing. Include tests and example configuration in that scan. Separately scan all local Git history with gitleaks git . --log-opts="--all" --redact. Keep reports private and do not suppress entire test directories.
A source fix does not remove an earlier commit or close a GitGuardian incident. For the reported authentication-salt fixture, the Base64 value represented sequential bytes, not an API key. Classify that historical incident as a test-fixture false positive after checking its context. Revoke and rotate any exposed operational credential before addressing its repository history.
Hosting can see account/profile and passkey metadata, record IDs, ciphertext sizes, timestamps, and access patterns. A database attacker can delete data, replace authentication state, or replay valid old snapshots. Password-only ciphertext allows offline password guessing; optional Account Secret protection adds independent secret material.
Compromised devices, extensions, authenticators, or modified clients can expose secrets. Security headers cannot stop a host replacing JavaScript; stronger protection needs an independently verified client. Rotation cannot revoke copied data. Change exposed third-party passwords at their services.
Keep plaintext and keys out of logs, analytics, URLs, and persisted browser state. Avoid icon requests exposing saved domains. See deployment for hosting controls and backups.