SECURITY

YOUR DATA.
VERIFIED, NOT ASSUMED.

A self-hosted app that touches your tasks, finance, and family's schedule has to earn trust with specifics, not a badge. This page is those specifics.

WHAT THE AI CAN ACTUALLY ACCESS

DEFAULT-DENY, NOT DEFAULT-TRUST.

// 01
Reads are scoped to you

The AI agent binds every Brain operation to your own authenticated account — a prompt can't make it read another user's tasks, finance, or contacts. Verified directly in code, not just documented: there is no code path that resolves a store by anything other than the caller's own identity.

// 02
Writes need your approval by default

Approve mode (the default) lets the AI read freely, but every write — creating a task, editing a note, sending an automation — pauses for your explicit approval first. Only a small allowlisted set of read-only tools ever runs without asking.

// 03
Brain content can't hijack the AI

Content read from your Brain (notes, shared files) is wrapped and escaped before it reaches the model, specifically to contain prompt-injection attempts — a malicious note shared with you can't quietly instruct the AI to act against your data.

// 04
Export is self-scoped

The "download my Brain" export only ever zips your own folder — there's no parameter that lets it target anyone else's data, and it's rate-limited.

THREAT MODEL

WHAT WE ACTUALLY TESTED.

A full audit (2026-07-19) traced two specific threats directly through the code, not just the design intent:

Another user stealing your data

Every data module (Finance, Assets, Contacts, Notes) resolves a requested ID only within your own store plus what's explicitly shared with you — there's no code path that scans across all users for an ID. A stranger's book/asset/contact ID simply resolves to nothing. Verified sound.

An outsider breaking in

Two stored-XSS-shaped issues have been found and fixed to date — both in features that render a URL as a clickable link (the automation inbox, and AI chat's markdown renderer), both closed by validating the link's scheme before rendering. Session cookies are httpOnly. No password-reset flow exists to abuse — recovery is admin-mediated by design.

HARDENING SHIPPED (v0.4.0)

WHAT CHANGED, AND WHY.

Account lockout

10 failed attempts against one account within 15 minutes temporarily blocks further logins — stops distributed credential-stuffing that per-IP rate limiting alone misses. Never a permanent lock; only counts genuine failures.

Constant-time login

An unknown email takes exactly as long to reject as a known one — closes a timing side-channel that could otherwise reveal which addresses have accounts.

Content-Security-Policy

Ships a real CSP (script-src 'self', tight defaults) so any future injected script has nowhere to execute from.

Docker socket no longer exposed to the app

The app used to bind-mount the host Docker socket directly — an app compromise was roughly equivalent to host root. It now talks to Docker through a locked-down proxy that only permits the specific operations the updater/tunnel actually need.

Refuses to start insecurely

A deploy left on the default signing key or wildcard CORS now fails fast at startup instead of running silently exposed.

Signed-update verification

Optional ('UPDATE_REQUIRE_SIGNATURE=true') — the updater verifies a trusted GPG signature before pulling and building an update.

Full detail, every finding, and what's still open (app-level 2FA is the one tracked gap) in the public audit report — nothing here is summarized away from what the repo itself shows.

Since this audit, automated scanning (GitHub CodeQL/Dependabot) caught and closed two more real findings: the second stored-XSS-shaped issue noted above, and an SSRF gap in SimpleFIN bank-connection setup (a submitted setup token's claim URL had no host allowlist, so it could be pointed at an internal-only service instead of the real SimpleFIN bridge). Detail in SECURITY.md.

RESPONSIBLE DISCLOSURE

FOUND SOMETHING? TELL US FIRST.

Please don't open a public GitHub issue for a security vulnerability. Email [email protected] instead, with a description, reproduction steps, and affected versions if known.

We aim to acknowledge within 48 hours and land a fix or mitigation within 7 days for critical issues. Credit in the CHANGELOG.md entry if you want it — anonymous reports are just as welcome. Full scope and design notes in SECURITY.md.

Report a Vulnerability →View Source on GitHub →