Skip to content

CapacityLens threat model

Version: 2026-08-18. Review this model after changes to authentication, tenancy, imports, offline storage, deployment topology or external services.

Security objectives

  1. A user can read or change only accounts, operations, records and protected fields allowed by their current server-side membership and role.
  2. Passwords, session tokens, reset/invite tokens, MFA seeds/recovery codes and provider secrets are not disclosed or stored in recoverable form where a safer representation is possible.
  3. Tenant writes remain valid, atomic and attributable; corrupted relational state prevents startup.
  4. Browser-delivered code cannot silently turn an authenticated browser into a cross-site write primitive, and sensitive API responses are not cached.
  5. Operators can contain a compromised identity, restore authoritative data and investigate typed security/audit events without relying on the compromised host alone.

CapacityLens is not a safety-critical, payments, classified-data, real-time media or anonymous public-upload system. Availability is important but confidentiality and tenant integrity take priority over keeping a misconfigured production process running.

Assets and trust boundaries

AssetPrimary protectionBoundary
Account schedule and private client/project namesServer membership, action/field authorization, SQLite constraintsBrowser/API and tenant boundary
Identity, password, MFA and provider-link stateBetter Auth, versioned scrypt, encrypted recovery/tokens, explicit verified linkingAuth/provider/API and database boundary
Session, reset and invite bearer valuesHttpOnly cookies or one-time values; hashes where supported; expiry/revocationBrowser/API and operator delivery boundary
Offline snapshotOpt-in, role-filtered, AES-256-GCM, seven-day expiry, viewer-onlyBrowser-origin/device boundary
Database, WAL, audit and snapshots0600 files, 0700 backup directory, optional encrypted-volume attestationProcess/host boundary
Audit and security eventsData-minimised JSON, local restrictive file plus optional separately forwarded streamProcess/log-collector boundary
Build and release inputsLockfile, pinned images/actions, dependency review, SBOM, scans and provenanceContributor/CI/registry boundary

The packaged production flow is browser → public TLS proxy → unprivileged web container → verified per-install internal TLS → unprivileged HTTPS API container → local SQLite/backup volumes. A bare-metal deployment may instead use HTTP only over a same-host loopback hop. In both shapes the API must not be publicly reachable and the proxy must overwrite rather than append forwarding headers.

Actors

  • Viewer, editor, admin and owner, all potentially malicious within their legitimate account.
  • An authenticated user attempting cross-account or higher-role access.
  • An unauthenticated internet attacker, automated credential attacker or cross-site origin.
  • A compromised browser profile or device.
  • A malicious/compromised identity provider, dependency, build input or container base.
  • A self-hosting operator who makes an accidental or unsafe configuration choice.
  • A host operator misusing stopped-server SSO repair or sole-Owner recovery authority.
  • A host-level attacker. Host compromise is not fully preventable in-process; encrypted storage, secret management, isolation and off-host logs/backups limit consequences.

Abuse cases and controls

ThreatPrincipal controlsVerification
BOLA/IDOR or cross-tenant mutationMembership fetched server-side for each operation; every scoped entity has accountId; row-addressed generic writes make absent and foreign ids response-indistinguishable; row/reference validation fails closed when a project-bound allocation cannot resolve its project in the same account; cross-account testsapp.authz, app.members, tenant-store, route and shared mutation tests
Function/field privilege escalationCentral action matrix; protected-name projection/preservation; owner-only import; fresh session for privileged actionsaccess, privacy and route tests
Credential stuffing/password crackingPositive global/API throttling; five-attempt MFA lock; 15–128 Unicode code points; HIBP range check; scrypt N=2^17,r=8,p=1; no default passwordpassword/auth/rate-limit tests
Password-only account takeoverOpt-in required TOTP wall before tenant data; otherwise long passwords, HIBP by default, scrypt, throttling and bounded/revocable sessions; one-time MFA recovery codesreal auth integration and UI tests
Session theft/fixationSecure HttpOnly SameSite __Host- cookies; new token on auth; fixed 12-hour and 30-minute idle limits; revocation/reset invalidation; session inventoryauth and member revocation tests
CSRF and cross-origin data useUnsafe-method Origin/Sec-Fetch-Site rejection; exact configured or trusted-proxy-derived same origin; SameSite cookie; safe HTTP methodsCSRF/CORS and packaged-proxy tests
Injection/XSS/mass assignmentReact text rendering; no untrusted HTML; parameterized SQLite; explicit table/column codecs; sanitisation and structural limits; CSPserver/shared/CSP tests
Malicious, stale or oversized importJSON-only, 5 MiB/200,000-record caps; Owner-only access; bounded/cancellable worker preparation; schema migration/sanitisation; tenant remap; reference validation; exact-snapshot recheck and atomic replacementimport, worker, transaction and mutation tests
Offline cache disclosure/tamperingRole-filtered input; non-extractable device key; AES-GCM with random IV/AAD; tamper/expiry deletion; viewer-onlyoffline cache tests
Database corruption/partial writeStartup foreign-key check; WAL; transactions; optimistic concurrency; sync-session ordering/provenance; atomic imports/backupsmigration, ordering, transaction and restore-drill tests
Log erasure/injection or invisible attackStructured serialization, no values/credentials, restrictive modes, health degradation latch and optional separate JSON forwardingaudit/log/production-guard tests
SSRF/provider substitutionOperator-only exact issuer/discovery; endpoints validated before redirect or secret use; HTTPS outside loopback; no credentials/redirects; 10-second and 1 MiB provider-response bounds; signed ID-token issuer/audience/timestamp verification; JWKS rotation and user-info subject bindingstrict OIDC crypto/exchange tests and pinned Dex browser conformance
Federated identity takeover or unsafe cutoverVerified matching email for explicit links; implicit linking disabled; provider/subject uniqueness; durable admission evidence; preflight/repair; SSO-only startup interlock and atomic incompatible-state revocationidentity-port, cutover, migration and strict-OIDC tests
Operator recovery misuseStopped server and exclusive SQLite lock; unique sole-Owner eligibility; ordinary expiring single-use reset; rollback on partial failure; token-free audit recordOwner-recovery CLI and audit tests
Resource exhaustion512 accepted-socket ceiling; per-IP application/CSP rate limit with constant-work health exempt; bounded scrypt, HIBP and import queues; 5,000-operation batch and 200,000-record import caps; request/queue/provider timeoutsresource-queue/rate-limit/health/import/CSP tests
Supply-chain compromiseExact lockfile, pinned action/base-image commits/digests, Dependabot, CodeQL, Gitleaks, dependency review, SBOM, Trivy, ZAP and tagged provenancelocal gates and public/manual workflows

Residual and accepted risks

  • Strict OIDC is first-class. Better Auth owns state, PKCE and cookies; the account adapter owns issuer-pinned endpoint selection, the bounded no-redirect code exchange, ID-token signature/audience/timestamp checks, JWKS rotation and user-info subject equality. Named social providers remain experimental and every IdP still needs staging interoperability, MFA-policy and logout/session-lifetime testing.
  • IdP disablement does not revoke already-issued local sessions. The accepted maximum is the remaining twelve-hour absolute lifetime or thirty minutes inactivity; local revocation is an incident-response requirement and back-channel logout must be reconsidered before hosted GA.
  • Identity masquerade is a session-scoped, account-confined read projection for Owners and Admins. A root unsafe-method guard and a separate Better Auth proxy guard reject changes while it is active; actor-dependent reads use the target member's role and redaction. The process-local registry deliberately disappears on restart, which ends access rather than restoring uncertain state. Start and end events are audited, with the start event carrying the session expiry that bounds a record if the process stops before an end event can be written.
  • SSO-only cutover retains dormant password credentials for the documented mixed-mode rollback. Protecting the host/database and using the stopped-server repair tooling carefully remain operator responsibilities.
  • The sole-Owner recovery command is deliberate host-operator authority. It cannot be contained from an attacker who already controls the application database and process environment.
  • Required TOTP is optional. Password-only deployments do not meet ASVS 5.0 Level 2 requirement V6.3.3; when enabled, TOTP meets L2 but remains phishable and insufficient for L3.
  • Existing legacy Better Auth scrypt hashes use the former weaker profile until the user changes or resets the password. They are verify-only; new material never uses that format.
  • The application has no IP/device-risk engine, anomalous-login user notification, global administrator “revoke everyone” control or HSM/full-memory encryption.
  • Public TLS, encrypted host volumes, secret-manager/HSM use, clock synchronization, log retention and off-host collection are deployment controls. Internal service TLS is application-packaged for Compose and optional for a same-host bare-metal hop; startup warnings cannot verify external controls.
  • An unlocked or compromised application origin can invoke its non-extractable offline key. Device encryption, patching and profile access control remain necessary.
  • A single-process SQLite service can still be denied service by sufficient network or tenant-valid load. Edge rate limiting, connection limits and resource monitoring remain operator controls.

CapacityLens is open source under AGPL-3.0.