Information Security Policy

System: Koda Finance (Android app + Firebase backend) · Owner: Ivy, Founder, Koda Digital · Contact: contact-us@kodadigital.ai · Effective: 2026-09-03 · Review: every 6 months

1. Purpose and scope

This policy governs how consumer financial data obtained through Plaid is protected in Koda Finance. It covers the mobile app, the backend (Firebase Cloud Functions + Firestore in Google Cloud project koda-finance-e1c74, region us-central1), the Plaid integration, and the operator's workstation and accounts.

2. Context

Koda Finance is a personal net-worth tracker in a private beta. One person is its sole developer and administrator (the Operator); the app is sent directly to a small number of invited testers and is not in the app stores. There are no employees, contractors, or third-party integrators. Each tester has their own account and sees only their own data; none has any administrative access.

3. Architecture (the basis for every control)

Android app ──TLS──▶ Cloud Functions (api) ──TLS──▶ Plaid API
   │ sign-in: password     │ ID token check: verified,   ▲
   │   + authenticator     │   not revoked, MFA; uid scopes data
   │ biometric lock        ├──▶ Firestore (tokens, cache, manual assets, history)
   │ keystore: session,    └──▶ Secret Manager (Plaid keys)
   │   last NW summary,
   │   ledger key
   │ SQLCipher DB: transaction ledger (never on the server)
   ├── Firebase Authentication (Identity Platform, TOTP MFA)
   └── Plaid Link SDK (bank credentials never touch Koda code)
  • Bank credentials are entered only inside Plaid Link; Koda never sees them.
  • Plaid access tokens exist only in Firestore, readable only by the backend service account.
  • The only financial data on the device is the last net-worth summary (totals and per-account balances, no transactions), encrypted in the iOS Keychain / Android Keystore so the app opens offline, and erased on sign-out and account deletion. Every other screen loads from the backend.
  • Crash reports go to Sentry only when a DSN is configured, and carry no account data, balances, transactions, email, screenshots or IP address.
  • The account holder can delete their account and all data from inside the app; see data-retention-and-deletion.md.
  • All transport is HTTPS/TLS 1.2+ (Google-managed certificates on cloudfunctions.net; Plaid's on plaid.com).

4. Zero-trust access architecture

No network location is trusted. There is no VPN, office network, or IP allow-list that grants access; every request to every component is authenticated and authorised individually:

  • App → backend: each HTTP request carries the signed-in user's Firebase ID token, checked by middleware (functions/src/firebaseAuth.ts) for validity, revocation, a verified email, and a completed authenticator (TOTP) step. Anything else receives 401/403 and no data. The uid in that token is the only key used to reach Firestore (users/{uid}), so one user can never read another's data, whatever the request asks for.
  • Plaid → backend: none. Plaid never calls the backend; every Plaid request is made by the backend on a signed-in user's behalf.
  • Backend → Firestore / Secret Manager: Google-issued short-lived service-account credentials, scoped by IAM; Firestore security rules deny all direct client access (firestore.rules).
  • Backend → Plaid: client ID + secret from Secret Manager, per request, over TLS.
  • Operator → Google Cloud / Firebase / Plaid dashboards: Google account with 2-Step Verification (phishing-resistant method), Plaid dashboard with MFA, Sentry (if crash reporting is enabled) with MFA. No standing sessions on shared machines.
  • Device: the session is stored in the Keychain/Keystore, excluded from backups. As a second layer on an open session, the app requires device biometric authentication on every launch and re-locks when backgrounded (app/src/components/lock-screen.tsx).

5. Risk identification, mitigation and monitoring

Risk Mitigation Monitoring
Backend URL discovered and scanned Every route except the public health and minimum-version checks requires a valid, MFA-verified session, which reaches only its own data; 401/403 otherwise Cloud Functions logs; Cloud Monitoring error rate
App bundle copied or decompiled The bundle contains no credential — only the Firebase project identifiers — so a copy grants nothing without the account password and an authenticator code Firebase Authentication sign-in activity
Operator password stolen Authenticator (TOTP) code also required; sessions opened without it are refused by the backend Firebase Authentication sign-in activity
Plaid credential leak Secrets only in Google Secret Manager; never in git (.gitignore), never in the app Secret Manager access audit logs
One user reaching another's data Data is stored per uid and routes read only the caller's own tree; Items are looked up under the caller's uid, so an Item id belonging to someone else is simply not found Code review of every route that touches Firestore
Lost/stolen phone Biometric lock on the open session; no offline data; "sign out of every device" (or disabling the user in Firebase Authentication) revokes the phone's session on its next request; device can also be wiped remotely Firebase Authentication sign-in activity
Operator account compromise 2SV with passkey/security key on Google; MFA on Plaid; unique passwords in a password manager Google security alerts
Data over-retention Cache TTLs and automatic purge; unlink endpoint revokes Plaid Items (see data-retention policy) Scheduled job logs

6. Change management

Code lives in a private GitHub repository. Changes are deployed only by the operator via firebase deploy after a TypeScript build passes. Dependencies are updated on a monthly cadence or immediately on a published critical vulnerability.

7. Incident response

If compromise of the backend, secrets, or a device is suspected: (1) sign out of every device (POST /revoke-sessions, or disable the user in Firebase Authentication), change the account password and re-enroll the authenticator if either may be exposed, rotate the Plaid secret in Secret Manager, and redeploy; (2) remove affected Plaid Items via /item/remove (the app's unlink function) and re-link; (3) review Cloud audit logs for the window; (4) notify Plaid via the dashboard support channel if Plaid data may have been exposed. As the only affected consumer is the operator, no external consumer notification is required; this section will be expanded before any multi-user release.

8. Related policies

See access-control-policy.md, authentication-and-secrets.md, data-retention-and-deletion.md, privacy-policy.md.