← Security

Access control policy

Last updated 2026-09-25. Owner: founder. Review: yearly.

Covers (1) access by CheckTrail personnel to production systems and (2) access by customer users inside the app.

1. Principles

  • Least privilege. Every person and every machine identity gets only what its job needs.
  • MFA everywhere. Every account that can reach production or customer data uses multi-factor authentication. Hardware security keys or an authenticator app; SMS only where nothing else is offered.
  • No shared accounts. Each person has their own login. Machine credentials are separate from human ones.
  • Deactivate, don't delete. App users are deactivated so their history stays attributable.
  • Everything logged. App actions go to the hash-chained audit log; provider consoles keep their own logs.

2. CheckTrail personnel and production access

Today CheckTrail has one person, the founder, who is the only person with production access.

System Who How MFA Status
AWS (CheckTrail account) Founder IAM Identity Center or IAM user with an admin role; root user not used day to day, root has MFA and no access keys Required To be set up with the Terraform apply
Neon Founder Neon console / owner role for migrations Required Confirm MFA enabled
Netlify Founder Console; holds production secrets Required Confirm MFA enabled
GitHub Founder Repository admin Required Confirm
Stripe, Resend, Sentry Founder Consoles Required Confirm on each
Google Workspace (email) Founder Admin Required Confirm
CheckTrail app (customer data) Founder does not have a user in customer firms Support access only at a customer's written request, through a time-limited account the firm creates, audited like any other user App 2FA Policy

Rules:

  • Production data is accessed only to operate the service, fix a problem, or at a customer's request. Direct database queries against production are avoided; when needed, they are read-only and noted in an operations log (date, reason, what was queried).
  • Being the AWS account administrator technically allows decrypting stored images. This is a known concentration of access for a one-person company; it is controlled by MFA, by CloudTrail logging (to be enabled), and by this policy, not by technical separation.
  • The founder's laptop: full-disk encryption, screen lock, OS updates on, no customer data stored locally, development uses fake data only.
  • Secrets live in Netlify environment variables and the founder's password manager, never in the repository (.env* is git-ignored).

When a second person joins: named accounts, the narrowest role that works, MFA from day one, a signed confidentiality agreement, and removal of all access on their last day (checklist in the quarterly review).

3. Machine identities

Identity Can Cannot
Postgres role checktrail_app (used by the app) SELECT/INSERT on record tables; SELECT/INSERT/UPDATE on state tables; DELETE only on rate-limit counters, holidays and branch assignments UPDATE/DELETE records; alter schema; drop triggers
Postgres owner role (migrations only) Run migrations Used by the app at runtime (never)
AWS IAM user checktrail-app PutObject / PutObjectRetention / GetObject on the records bucket; Encrypt/Decrypt/GenerateDataKey on the records key; Textract AnalyzeDocument in one region List, delete, change bucket or key settings (explicit deny)
Stripe, Resend, Sentry API keys Their own service only —
CRON_SECRET Trigger the 15-minute job Anything else

Production setup check: the checktrail_app role must be created before running migrations (otherwise grants are skipped). After every migration, confirm SELECT has_table_privilege('checktrail_app', '"AuditLog"', 'UPDATE') returns false.

Machine credentials are rotated at least yearly and immediately after any suspected exposure.

4. Customer users inside the app

Firms manage their own users. Enforcement is server-side on every action (src/lib/server/rbac.ts).

Permission Rep / assistant Supervising principal Home office compliance Firm admin
See check data Own branch Assigned branches All branches No (unless also compliance)
Log checks, record forwarding/holds, correct fields ✓ ✓ ✓
Review, approve, comment, resolve exceptions ✓ ✓
Confirm receipt manually ✓ ✓
Import deposit reports (CSV) ✓
Exam exports ✓ (own exports, own branches) ✓
Examiner links ✓
Firm settings and rules ✓ ✓
Invite / deactivate users, change roles ✓ ✓
Grant the firm admin role ✓ only
Branches ✓
Billing, SSO ✓
View audit log ✓ ✓
Examiner (link only) One export package, read-only, ≤ 30 days, revocable

Other rules enforced by the app:

  • Every query is limited to the user's firm; tests prove no cross-firm access.
  • A user can hold several roles. Reps belong to exactly one branch; principals can cover several.
  • 2FA is required for every password sign-in. Firms using SSO rely on their identity provider's MFA; a firm can make SSO mandatory for all its users.
  • Lockout after 5 failed attempts for 15 minutes; sessions end after 30 minutes idle or 12 hours.
  • Deactivating a user ends their sessions at once. Users are never deleted.
  • When a subscription lapses, all users become read-only; exports still work.
  • Every role change, invite, deactivation and settings change is audited.

5. Access reviews (quarterly)

Every quarter, the founder:

  1. Lists every account with access to AWS, Neon, Netlify, GitHub, Stripe, Resend, Sentry, Google Workspace; confirms each is needed and has MFA on.
  2. Lists IAM users/roles and access keys in AWS; confirms the app key is the only access key and is under a year old.
  3. Confirms checktrail_app grants are unchanged (query above) and triggers are present.
  4. Reviews the operations log of any direct production data access.
  5. Reminds each customer's compliance contact to review their own users (the app lists users, roles, branches and last sign-in).
  6. Records the review: date, what was checked, what changed. Keep records 6 years.

No quarterly review has been done yet. The first is due at go-live.

6. Break-glass

For when the founder is unavailable and the service needs urgent action, or normal MFA is lost.

  • MFA loss: recovery codes for every production account are kept in the password manager and a printed copy in a sealed envelope in a secure place at home.
  • Founder unavailable (planned, not in place): a named trusted person gets, in a sealed envelope, instructions to (a) contact customers using a prepared message, (b) contact counsel, and (c) put the site into maintenance mode using a limited Netlify account. They do not get access to customer data. Nobody has been named yet.
  • Any use of break-glass is logged, the credentials used are rotated afterwards, and customers are told if their data was touched.