Private beta. Invites rolling out now

Trust

Security

Last updated: July 29, 2026

Moonlight is where you keep the clearest picture of your money. Protecting it shapes how the product is built, down to the code. Here is exactly what we do, in plain language.

Read-only, always

Moonlight can read balances and transactions. It cannot move money, make payments, or change anything at your bank. There is no code path that could.

We never see your login

Your bank username, password, and any one-time codes are entered inside Plaid's own secure widget and are never sent to, or stored by, Moonlight.

Encrypted end to end

Everything travels over TLS, and the credentials that link your bank are encrypted with AES-256-GCM before they ever touch our database.

Read-only bank access

When you link an institution, we request read access to transactions and balances, and nothing else. Moonlight holds no payment, transfer, or payment-initiation permissions, so even a total compromise of our servers could not move a dollar of your money.

  • Only the Transactions product is requested from Plaid; no funds-movement products are enabled on our account.
  • The app itself only ever reads from your institution. Writes stay inside Moonlight's own database (your categories, budgets, and notes).
  • The Moonlight CLI and the AI assistant integration are read-only by construction: neither exposes a tool that can change data.

Your bank credentials never reach us

Bank connections run through Plaid Link, which loads in Plaid's own hosted frame. You authenticate directly with your institution; Plaid hands us back a token that is useless anywhere else. We never receive, log, or store credentials or multi-factor codes.

  • Plaid is used by thousands of financial apps and is audited against the standards banks require before they permit a connection.
  • The access token that identifies your connection is encrypted at rest and is never sent to the browser.
  • You can disconnect an institution at any time from Settings, which revokes the connection at Plaid and stops all future syncing.

Encryption in transit and at rest

All traffic between your browser, our API, Plaid, and our database is TLS-encrypted. Bank access tokens get a second layer on top of that: authenticated AES-256-GCM encryption with a key held only by the server, so a stolen database backup yields ciphertext rather than working credentials.

  • HTTP Strict Transport Security is enforced, so browsers refuse to talk to us over plain HTTP.
  • The database is managed PostgreSQL with encryption at rest and TLS-only connections.
  • Encryption keys and API secrets live in the server environment only: never in the client bundle, never in the repository.

Your data is isolated to your account

Every read and write is scoped to the signed-in user at the API layer, not in the interface. A request that does not carry a valid session is rejected before it reaches any data, and a request that carries yours can only ever address your own rows.

  • Protected endpoints require an authenticated session; unauthenticated calls fail closed with an error, never with data.
  • Every database query filters on your user ID, so one account cannot read or modify another's records.
  • Uploaded statements are stored under a per-user path with traversal protection, and are served as downloads so a malicious file can never execute in your browser.

Account security

Sign-in is handled by a maintained authentication library rather than anything hand-rolled. Sessions live in signed, HTTP-only cookies marked Secure and SameSite, which keeps them out of reach of scripts and cross-site requests.

  • Optional two-factor authentication using any authenticator app, with one-time recovery codes.
  • Passwords are hashed with a modern algorithm, so we never store them and cannot retrieve them. You can also sign in with Google and skip passwords entirely.
  • Sign-in, sign-up, and password-reset endpoints are rate-limited to blunt brute-force and credential-stuffing attempts.

Connecting an AI assistant

Moonlight can be connected to AI clients such as Claude so you can ask questions about your finances. That connection is read-only, explicitly approved by you, and revocable in one click.

  • Authorization uses OAuth 2.1 with PKCE required; weaker challenge methods are rejected outright.
  • No client is ever granted access silently. You see a consent screen naming the client before anything is issued.
  • Personal access tokens are stored only as SHA-256 hashes, so a database leak cannot be replayed against the API.
  • Every connection is listed in Settings and can be revoked at any time, which takes effect immediately.

Uploads and file handling

Statements you upload are validated before they are accepted: format is checked by inspecting the file's actual bytes rather than trusting its name, size is capped, and filenames are sanitized so they cannot be used to escape their storage directory.

  • CSV and PDF only, verified by content signature, with a 20 MB ceiling.
  • Files are always served as attachments with content-type sniffing disabled, so a disguised file cannot run as a script.
  • Deleting a statement removes the stored bytes, not just the database row.

AI-assisted categorization

We use AI to categorize transactions and summarize spending. Calls are made server-side with keys that never reach the browser, and only the transaction details needed for the task are sent. Your bank credentials and identity documents are never part of a prompt.

  • AI providers act as processors on our instructions and are not permitted to use your data to train models.
  • AI features operate only on your own data, within the same per-user scoping as the rest of the API.

A hardened application surface

The app ships with defense-in-depth defaults so that a single mistake elsewhere is less likely to become an incident.

  • A strict Content Security Policy limits scripts, frames, and network calls to our own origin plus Plaid. Everything else is blocked.
  • Framing is denied, MIME sniffing is off, referrers are trimmed on cross-origin navigation, and camera, microphone, geolocation, and payment APIs are disabled outright.
  • Cross-origin API access is restricted to the Moonlight app's single trusted origin.
  • Incoming Plaid webhooks are cryptographically verified against both signature and body hash, and rejected if they fail. Expensive operations are rate-limited per user.
  • Inputs are schema-validated at the API boundary, and database access goes through parameterized queries, so untrusted input is never interpolated into a query.

Consent, retention, and deletion

You agree to the collection and processing of your data before any account can be connected. That agreement is enforced on the server, not only in the interface, so it cannot be bypassed by calling the API directly.

  • Deleting your account removes your financial data, linked institutions, and uploaded statements, and disconnects your items at Plaid.
  • Deletion is immediate and cascading. We do not keep a shadow copy of deleted financial data.
  • Individual accounts, transactions, and statements can be deleted on their own at any time.

Compliance with Plaid's requirements

Our use of Plaid follows their developer policy and end-user data-handling requirements: we collect the minimum data needed for the features you use, tell you plainly what we do with it, honor disconnection and deletion promptly, and keep bank credentials entirely out of our systems.

Reporting a vulnerability

If you believe you have found a security issue, tell us before disclosing it publicly and we will work with you on a fix. We do not pursue legal action against good-faith researchers who report responsibly and avoid accessing other people's data.

Security questions, or a vulnerability to report? Email us at security@moonlightfi.com. For what we collect and why, see our Privacy Policy.