Homepage

Handover: KumoHQ → FLIN

This is the dated transfer snapshot for FLIN's in-house engineering and product team. It covers who owns which account, what to transfer and in what order, what is due soon, what is still open with FLIN, and every known issue with a recommended fix. Everything else lives in the permanent docs (see the reading order), which your team should keep up to date. This file is a snapshot and is not meant to be maintained.

Last verified: 2026-10-12 against main @ a270f1d (live infra/EAS/Trigger.dev checks on the same date).

Markers used: TODO(handover) is a fact or decision only KumoHQ or FLIN can supply. ⚠️ marks something taken from earlier team notes that could not be re-verified in code or live.

Contents

Reading order

Who Read
Everyone ../README.md → product.md → architecture.md → this file
Product product.md (status board, features, journeys, pending inputs), ../CONTEXT.md (glossary), data-model.md#gotchas-for-analysts-metabase
Engineers, day one development.md, engineering-rules.md, code-map.md
Engineers, before touching prod deployment.md, operations.md, ../infra/terraform/README.md
Releasing the app mobile-release.md
Vendors integrations.md
Countries / Philippines multi-country.md (§8 is the binding PH spec), deployment.md#philippines-go-live-checklist
Data data-model.md
Why things are the way they are adr/ 0001–0010, specs/, research/

State of the system on 2026-10-12

Indonesia (ID) Philippines (PH)
App 1.1.0 live on App Store (ID storefront only) and Google Play since 2026-10-07. Built from commit 2ac427939 (PR #135): iOS build 31, Android versionCode 4 No shipped binary offers PH: EXPO_PUBLIC_SERVER_URL_PH is not set in any EAS environment
Server https://appapi.flin.co.id, AWS 961341531217 (ap-southeast-3), ECS flin-prod-api on task def flin-prod:282, image a270f1dcfdb2 (= main) https://appapi.flin.ph, AWS 611858318585 (ap-southeast-1), ECS flin-ph-prod-api on flin-ph-prod:4, same image. Runs APP_ENV=staging because the PH profile refuses prod until the CIBI and Onfido adapters exist
Features on Free credit score (AFPI), paid CLIK report (Rp 25.000), Debt Discipline Program, consultations, mediation, notifications All four features off. Prices are placeholders (TODO(FLIN-174))
Jobs Trigger.dev org "Flin", project proj_fqtihpiyjlwzafgdiukm, prod + staging, worker 20261007.1, every prod run in the last 7 days completed No Trigger.dev project. Notifications off
Deploys A merge to main deploys both servers (one CodePipeline per account) and, when apps/jobs/**, packages/**, bun.lock or package.json changed, the Trigger.dev jobs (GitHub Action) (same pipeline trigger)
main app.json is already at 1.2.0. Only two internal Android preview builds of it exist; no store build —
Open work PR #127 ph-languages (English + Filipino, en-PH overlay) is open. Branch onfido-poc holds the Onfido PH KYC POC and is not meant to merge

Since 2026-10-06, production in Indonesia holds only Test users plus whoever signed up after the 1.1.0 release. Every other account was purged that day ⚠️ (from team notes); the manual RDS snapshot flin-prod-pre-user-purge-20261006 is the only copy of the earlier data (see operations.md).

After the handover: who does what

Decided 2026-10-12:

  • FLIN owns and operates everything. That means every account, every vendor contract (CIBI and Onfido included), production, releases and on-call, for both countries.
  • KumoHQ stays on as FLIN's contractor for the Philippines. It builds the PH adapters (CIBI, Onfido), app changes and the PH go-live checklist through pull requests to main that FLIN reviews. Splitting ownership by country is not possible: there is one codebase and one app binary, and a merge to main deploys both servers. Every PH PR runs the Indonesia regression guard in engineering-rules.md.
  • KumoHQ access is scoped, MFA-protected and ends at PH launch, not at the end of the handover. It covers the PH AWS account, the Trigger.dev "Flin" org (to create the PH project), Expo and the stores (to ship the PH-capable build), and error tracking. In the ID AWS account, read-level access is enough for the regression-guard plan check. The PH launch date is TODO(handover).
  • Vendor credentials for PH work (CIBI and Onfido UAT/sandbox) reach KumoHQ through FLIN's password manager, never by email.

Act now

These are live today. They are listed again under known issues.

  1. A legacy Trigger.dev project is still sending notifications. proj_wwkvmnfyahwgpebjuuaz in the KumoHQ Trigger.dev org (the jobs project before 2026-07-27) still runs notification-tick every 30 minutes and due-date-reminders daily against a database that is not ID prod. It sent a notification on 2026-10-11. Someone with access to that org should find which database it uses, disable its schedules, then delete the project (operations.md#88-triggerdev-operations).
  2. The Trigger.dev staging environment of the current project sends real Debt Discipline due-date notifications. Check which server its SERVER_URL points at.
  3. Metabase is reachable from the whole internet. Its role can read every column, including NIK, phone numbers and KTP fields. Put the allowlist back (operations.md#3-metabase-indonesia-only).
  4. Finfra AFPI credentials expire 2026-11-25 ⚠️ (date recorded in team notes when the pair was set; SSM shows both parameters rotated on 2026-09-07, which is consistent). Get the next key pair from Finfra before then. The previous expiry went unnoticed for three days ⚠️ and blocked onboarding (operations.md#63-rotation-notes-per-secret).
  5. The live "upgrade" push series quotes the wrong price. It says Rp29.000 and "Today only"; the real price is Rp 25.000 with no discount (packages/notifications/src/series.ts, copy.ts). Fixing it needs only a server deploy.
  6. Download the 1.1.0 store binaries from EAS before 2026-11-05, when EAS deletes the build artifacts. No git tag marks the release commit; tag 2ac427939 as app-v1.1.0.
  7. Security clean-up. The admin, staging-host and shared-account items under Security. Full detail is in the private report delivered separately (not in this repo).

Accounts and access

Verified facts only. Owner after handover and Transfer action are for KumoHQ and FLIN to fill in during review.

Account / system Used for Verified on 2026-10-12 Owner after handover Transfer action
GitHub KumoHQ/flin (private) Code. Source for both CodePipelines. GitHub Action deploys Trigger.dev KumoHQ org, free plan, 2FA required. Org admins ankitsamarthya, vishal21st. Collaborators: deniprobow (maintain), BhuwanTiwari12 (write). No branch protection (not available on the free plan for private repos). One Actions secret, TRIGGER_ACCESS_TOKEN KumoHQ: the repo stays in the KumoHQ org (decided 2026-10-12) Add FLIN's engineers as collaborators (admin for FLIN's lead). Branch protection stays unavailable while the org is on the free plan, so review discipline is manual. Moving the repo later still means the three rewires in checklist step 4
AWS 961341531217 (Indonesia, ap-southeast-3, CLI profile flin) ID production: ECS, RDS, S3, CloudFront, Metabase, DB tunnel, SES. Also hosts infrastructure that is not in Terraform: the staging API host behind flinapi.kumohq.in, the ID and PH WordPress sites, an SFTP lead-intake host, backup hosts, a company document-drive mirror AWS Organizations management account (single member; management email on a flin.co.id address). Root MFA on. IAM Identity Center in ap-south-1 with one user. KumoHQ works through an IAM admin user FLIN (decided 2026-10-12). Account admin: TODO(handover) FLIN admins get access through IAM Identity Center with MFA first. KumoHQ keeps scoped, MFA'd Identity Center access (read-level is enough for the PH regression-guard plan check) until PH launch; replace KumoHQ's IAM admin user with that now; at PH launch remove KumoHQ and rotate every secret it could read
AWS 611858318585 (Philippines, ap-southeast-1, CLI profile flinph) PH production (flin-ph-*) Not in the ID organization. Shared with a third party's UAT stack (resources named *-uat / flphut, peered to that party's own account); see deployment.md#staging-and-uat for what not to touch. Admins: a KumoHQ user and a third-party user FLIN, keeping the shared account as-is (decided 2026-10-12). Account admin: TODO(handover); third-party owner/contact: TODO(handover) Join the account to FLIN's AWS Organization, MFA on every admin, IAM password policy, and bucket policies that limit the FLIN buckets (uploads, Terraform state) to FLIN roles. The third-party UAT stack stays; don't touch it. KumoHQ keeps MFA'd admin access (to build PH) until PH launch
Expo / EAS account flinmaybright, project @flinmaybright/flin (a9da2bb6-…) Builds, EAS env vars, signing and push credentials The KumoHQ maintainer is Admin, not Owner. Owner unknown FLIN (decided 2026-10-12). Owner: TODO(handover) Confirm or make a FLIN person the Owner; record eas credentials for iOS and Android with them present (mobile-release.md). KumoHQ stays Admin until the PH-capable build has shipped to both stores (PH launch), then is removed
App Store Connect, app 6781172200 ("FLIN - Credit Wellness App") iOS distribution Seller: Maybright Asia Pacific Pte Ltd. Indonesian storefront only FLIN (decided 2026-10-12). Account Holder: TODO(handover) FLIN adds its own Admin/App Manager users and an App Store Connect API key for eas submit (ascAppId 6781172200 in apps/native/eas.json). KumoHQ users stay until PH launch (PH storefront and the PH-capable release), then are removed
Google Play, com.flin.flinapp Android distribution Listing live, 1.1.0 FLIN (decided 2026-10-12). Developer-account owner: TODO(handover) FLIN creates a Play service account for eas submit, confirms Play App Signing is on and where the upload keystore is backed up, and removes KumoHQ users at PH launch
Firebase project behind google-services.json Android push (FCM) EAS file variable ANDROID_GOOGLE_SERVICES_JSON. Project unknown FLIN (decided 2026-10-12). Project ID and current owner: TODO(handover) Keep the existing project (a new one would need a store build and break push on 1.1.0). Add a FLIN Google account as Owner, then remove KumoHQ accounts; record the project ID here
Trigger.dev org "Flin" (flin-8179) → project "Flin App" proj_fqtihpiyjlwzafgdiukm Cron schedules for ID (prod + staging) Only project in that org. Owner and plan unknown FLIN (decided 2026-10-12). Org owner: TODO(handover) Make a FLIN person org owner and move billing to FLIN; mint a new TRIGGER_ACCESS_TOKEN from a FLIN account and replace the GitHub Actions secret; KumoHQ stays a member until PH launch and creates the PH project in this org when PH needs jobs; remove KumoHQ members at PH launch
Trigger.dev org KumoHQ → project "Flin" proj_wwkvmnfyahwgpebjuuaz Legacy, should be dead Still running; see Act now KumoHQ (decided 2026-10-12). Person: TODO(handover) KumoHQ, now: identify which database it points at (and wipe it if it holds a copy of user data), disable its schedules, then delete the project
Bugsink https://bugsink.aws.kumohq.in Error tracking. Server: project 4 (DSN in SSM SENTRY_DSN). App: project 5 (DSN baked into the binary) Self-hosted by KumoHQ on an EC2 instance in ap-south-1, in neither FLIN account. PH SENTRY_DSN is still a placeholder KumoHQ for now; FLIN from the 1.2.0 release (decided 2026-10-12) Now: KumoHQ keeps hosting and gives FLIN engineers accounts; fill the real PH SENTRY_DSN; agree retention and backups. With 1.2.0: FLIN moves to its own instance (self-hosted Bugsink or Sentry SaaS): server DSN via SSM + new deployment, app DSN in the 1.2.0 build. KumoHQ's instance stays up until 1.1.0 users have updated; KumoHQ gets an account on FLIN's instance until PH launch
Staging/UAT server flinapi.kumohq.in EAS preview-uat builds; probably the Trigger.dev staging target A single hand-built EC2 instance inside the ID prod account; the DNS zone kumohq.in is KumoHQ's KumoHQ until retired; FLIN owns the replacement (decided 2026-10-12) Now: restrict its security group, replace its IAM key with a role, stop it writing to the prod bucket. Then FLIN builds a Terraform staging stack (envs/staging, from envs/ph-prod, in its own account), repoints preview / preview-uat in apps/native/eas.json and the Trigger.dev staging environment, and retires this host. See deployment.md#staging-and-uat
Cloudflare: zones flin.co.id and flin.ph DNS. appapi.* and metabase records are DNS-only; the ACM validation CNAMEs must stay for certificate renewal The zones use different nameserver pairs, which suggests two Cloudflare accounts (inference) FLIN (decided 2026-10-12). Account holder per zone and registrars: TODO(handover) Add FLIN admins to whichever account(s) hold the zones, remove KumoHQ users, record each domain's registrar and renewal date. Later, optionally consolidate both zones into one FLIN account (re-add records, change nameservers at the registrar). Never delete the ACM validation CNAMEs
tunnelto (flinapi subdomain) Optional dev tunnel (TUNNEL=id bun run dev) — KumoHQ keeps it (decided 2026-10-12) FLIN sets up its own dev tunnel (any tool, own account) and changes the subdomain in apps/server/scripts/dev.mjs; the KumoHQ key is not shared
Kumo work tracker (work.kumohq.in/flin, FLIN-nnn tickets) Every ticket, decision and sign-off thread Kumo-hosted KumoHQ hosts it (decided 2026-10-12) FLIN's team gets read/write accounts now. When FLIN picks its own tracker, export every ticket keeping the FLIN-nnn id in the title (code and docs cite them, e.g. TODO(FLIN-174)), then Kumo's copy goes read-only
Figma: igRcVhDVxd0vgiPT7BqKxH (FLIN-x-KUMO) and the copy MB64xySEe6NnE7qtr2MesZ Designs; tickets link the original file — FLIN (decided 2026-10-12). Current owner of the original: TODO(handover) The original igRcVhDVxd0vgiPT7BqKxH is the single source of truth, in FLIN's Figma team (KumoHQ moves it if it holds it). Archive the copy MB64xySEe6NnE7qtr2MesZ once FLIN has edit access to the original, and update the Figma note in CLAUDE.md (it tells agents to use the copy)
PR review bots: Greptile (on demand since 2026-09-07), earlier CodeRabbit and Macroscope Code review Greptile config in .greptile/ FLIN brings its own reviewer (decided 2026-10-12). Tool: TODO(handover) FLIN picks and pays for its reviewer; because the repo stays in the KumoHQ org, a KumoHQ org admin has to approve installing that GitHub App. KumoHQ then uninstalls Greptile, CodeRabbit and Macroscope and cancels any remaining subscriptions; delete .greptile/ in the same change
Xendit (ID live; PH wired, placeholder secrets) Checkout for the paid report and Debt Discipline plans; payment becomes paid only via POST /webhooks/xendit (x-callback-token) SSM XENDIT_SECRET_KEY, XENDIT_WEBHOOK_TOKEN (ID, updated 2026-10-06). Registered webhook URL and dashboard admins not visible FLIN (decided 2026-10-12). Dashboard admin: TODO(handover) Confirm admins, remove KumoHQ users; record the registered webhook URL (expected https://appapi.flin.co.id/webhooks/xendit, events payment_session.completed / .expired); after handover rotate the secret key and webhook token together (dashboard + SSM, then force a new deployment); open the PH account and keys before PH payments go live
CLIK (ID credit bureau) SOAP enquiry for the paid report; nightly FTPS upload of consent evidence (regulatory, 5-working-day deadline) SSM CLIK_USERNAME, CLIK_PASSWORD (2026-08-20), CLIK_FTPS_PASSWORD; FTPS user is a Terraform literal. Pinned FTPS TLS intermediate expires 2027-11-02 FLIN (decided 2026-10-12). Contract owner and compliance-mail recipient: TODO(handover) KumoHQ hands CLIK's naming guide and the FTPS credential sheet to FLIN's password manager, then deletes its local copies; rotate the SOAP and FTPS passwords after handover via CLIK's process; confirm with CLIK that CONSENT/Output lists no missing enquiries; route the consent-upload errors in Bugsink to the FLIN owner (the Trigger.dev run shows success even when the upload fails)
Finfra / AFPI (ID) Alternative-credit report behind the free credit score in onboarding SSM FINFRA_API_BASE_URL, FINFRA_API_USERNAME, FINFRA_API_PASSWORD (rotated 2026-09-07). ⚠️ Pair expires 2026-11-25 per team notes FLIN (decided 2026-10-12). Owner and Finfra contact: TODO(handover) Request the next pair before 2026-11-25; verify (POST …/api/auth/token/v2 → 200), put both SSM parameters, force a new deployment (operations.md#63-rotation-notes-per-secret). Add a CloudWatch log-metric alarm on Finfra token fetch failed → the alerts topic
ASLI RI (ID) Biometric KTP-vs-selfie verify (REST verify_professional, prod endpoint) and the on-device liveness SDK (vendor binaries in apps/native/modules/asliri) SSM ASLIRI_VERIFY_TOKEN, ASLIRI_LIVENESS_TOKEN (server-held, served by kyc.getLivenessToken). A stale liveness token is still a plain-text EAS variable in dev/preview FLIN (decided 2026-10-12). Contract owner, quota and price: TODO(handover) KumoHQ hands the vendor SDK/API PDFs to a FLIN document store; rotate both tokens after handover; delete the stale EAS liveness variable; fix the POC-quota comment in apps/server/.env.example
CampaignHQ (ID; PH reuses it) WhatsApp sign-in OTP and early-onboarding lifecycle templates SSM CHQ_API_KEY (⚠️ PH uses ID's key, so PH OTPs come from ID's +62 sender). Service and template ids are hardcoded (integrations/campaignhq/index.ts, packages/notifications/src/templates.ts) FLIN (decided 2026-10-12). Account owner, sender numbers, Meta template approvals: TODO(handover) Rotate CHQ_API_KEY after handover. Before PH launch, decide whether PH gets its own +63 sender and templates; if so, move the hardcoded ids into the profile/env
GoHighLevel / LeadConnector (ID; PH placeholders) CRM contact sync, application-status pipeline (POST /webhooks/ghl), appointments (/webhooks/ghl-appointment), coach-booking widget, in-app support chat; reps open KTP images via /f/:token SSM LEAD_CONNECTOR_API, GHL_LOCATION_ID, GHL_WEBHOOK_SECRET (x-flin-token). ID field/pipeline/stage ids hardcoded in gohighlevel/field-map.ts, pipeline-map.ts; field list in reference/ghl-custom-fields.md FLIN (decided 2026-10-12). CRM admin: TODO(handover) CRM admin owns fields, pipelines, workflows, calendars and the widget's Allowed Domains, and knows a renamed field or picklist value breaks the sync silently (field-map.ts). Confirm the workflow calls /webhooks/ghl with the agreed token. Rotate the API token and webhook secret after handover (SSM and the GHL workflow together). Before PH launch: create the PH location and decide whether it reuses ID's ids
Adjust (all countries) App SDK funnel events; server-to-server paid_report_purchase EXPO_PUBLIC_ADJUST_ENV=production in EAS (preview builds too). SSM ADJUST_S2S_TOKEN exists for ID but is not on the running task, so no S2S events are sent; PH token is a placeholder FLIN (decided 2026-10-12). Dashboard owner: TODO(handover) Wire ADJUST_S2S_TOKEN onto the ID task via the clone procedure (deployment.md#change-an-environment-variable-or-secret) and confirm a purchase event arrives; point preview builds at the Adjust sandbox; finish iOS SKAN verification and FCM uninstall measurement (specs/adjust-integration.md); rotate the S2S token after handover
CIBI (PH credit bureau, not on main) Planned PH bureau; the PH profile's bureau port is unavailable() today ⚠️ Team notes: UAT REST "myScore" JSON works (no-match = 404, UAT scores random, spec drift). Pending FLIN input per multi-country.md §8.6 FLIN (decided 2026-10-12). Contract owner: TODO(handover) FLIN holds the contract; KumoHQ builds the adapter. KumoHQ moves the CIBI documents it holds (API spec, test-data sheet, UAT sign-off form) and UAT credentials into FLIN's document store and password manager and works from there; production credentials go from CIBI straight to FLIN
Onfido (PH KYC, POC on unmerged onfido-poc) Planned PH KYC (ID-document OCR + biometric) for both KYC ports; POC verdict "go" (research/onfido-ph-poc.md) Trial ended ~2026-08-06 ⚠️. Open before contract: classic POST /checks on a paid plan, PhilID/UMID coverage, pricing vs ASLI RI FLIN holds the relationship and contract; KumoHQ builds the integration (decided 2026-10-12). FLIN owner: TODO(handover) FLIN gets the three answers from Onfido sales and signs; sandbox and production keys go to KumoHQ through FLIN's password manager. Keep the onfido-poc branch as reference (it carries the ML Kit manifest plugin the real build needs). KumoHQ deletes the old trial keys from its local .env
Alert inbox kumo@flin.co.id Only subscriber of both SNS alert topics (flin-prod-alerts, flin-ph-prod-alerts) Confirmed in both accounts FLIN on-call (decided 2026-10-12). Address: TODO(handover), ideally a group address Subscribe FLIN's on-call address to both topics (aws sns subscribe --protocol email) and confirm it; keep kumo@flin.co.id subscribed during the transition, then unsubscribe it

Transfer checklist

Order matters. Each step says what breaks if it is skipped or done out of order.

  1. Fill in the accounts table together, KumoHQ and FLIN.

  2. Give FLIN admins access to every account before removing anyone. That means both AWS accounts (with MFA), Expo (Owner), App Store Connect, Play Console, both Trigger.dev orgs, Cloudflare, GitHub, Firebase, Bugsink, Metabase and the vendor portals. If skipped: you can lose the only person able to sign builds, renew certificates or rotate a key.

  3. Rotate every secret KumoHQ set or can read. All prod secrets were put in SSM by KumoHQ, and the Terraform-generated ones have never been rotated (operations.md#6-secrets-and-rotation). Pairs that must change together:

    • JOBS_API_SECRET in SSM and in the Trigger.dev environment. Otherwise every cron call gets 401.
    • BETTER_AUTH_SECRET also keys the /reports/:token links, which are 5-minute HMAC tokens. ⚠️ Rotating it should sign every user out; this has not been tested.
    • Vendor keys: rotate at the vendor first, then update SSM, then force a new deployment.
  4. The repository stays in the KumoHQ org (decided 2026-10-12). If it ever moves out, rewire these in the same change:

    • The ID CodePipeline source, a console-owned GitHub OAuth-app connection on KumoHQ/flin.
    • The PH CodeConnections connection (github_repo in infra/terraform/envs/{prod,ph-prod}/main.tf), which an org admin approves in the console.
    • The TRIGGER_ACCESS_TOKEN Actions secret.

    If skipped: merges stop deploying, silently, because nothing alerts on pipeline failure.

  5. Trigger.dev: decide whether the "Flin" org stays where it is or moves. If the project changes, update project in apps/jobs/trigger.config.ts, mint a new TRIGGER_ACCESS_TOKEN, and set SERVER_URL and JOBS_API_SECRET in the new environments. Kill the legacy KumoHQ project either way.

  6. Bugsink (decided: KumoHQ hosts until 1.2.0, then FLIN's own instance): The server DSN changes with an SSM update and a new deployment. The app DSN is baked into each store build (apps/native/lib/sentry.ts, EAS env), so app errors keep going to the old instance until users update.

  7. Staging (decided: lock down now, replace with a Terraform stack, then retire): flinapi.kumohq.in runs on a KumoHQ domain inside FLIN's prod account. Keep it up until the new staging exists and preview / preview-uat in apps/native/eas.json and the Trigger.dev staging environment point at it (deployment.md#staging-and-uat).

  8. Expo: make a FLIN person Owner of flinmaybright, then record the signing, APNs and FCM credentials and where the Android upload keystore is backed up.

  9. Kumo tracker: FLIN gets accounts now; export later, keeping the FLIN-nnn ids (decided 2026-10-12). Transfer the Figma files.

  10. Remove KumoHQ access at PH launch (KumoHQ is FLIN's PH contractor until then): AWS IAM users and the Identity Center user, flin-prod-db-readers members, GitHub collaborators, Expo members, Trigger.dev members, Metabase users, Bugsink. Then rotate anything they could read.

  11. Add FLIN's on-call address to both SNS alert topics and confirm the subscription.

Deadlines

Date What Action
2026-11-05 EAS deletes the 1.1.0 build artifacts (30-day retention) Download the .ipa and .aab now (mobile-release.md)
2026-11-25 ⚠️ Finfra AFPI key pair expires (date from team notes). Symptom: Finfra token fetch failed: 401 … KEY_NOT_VALID on every pull Ask FLIN/Finfra for the next pair ahead of time and rotate (operations.md#63-rotation-notes-per-secret)
2027-02-10 ACM certificate appapi.flin.co.id Auto-renews only while its DNS validation CNAME stays in Cloudflare
2027-02-19 ACM certificate metabase.flin.co.id Same
2027-04-24 ACM certificate appapi.flin.ph Same
2027-11-02 The TLS intermediate certificate pinned for CLIK FTPS (Thawte TLS RSA CA G1) expires Check CLIK's chain beforehand and update the pin in packages/countries/src/integrations/clik/consent-ftp.ts
Rolling CloudTrail keeps only 90 days of event history (no trail in either account) Enable a trail now if you want an audit record of the handover period
Rolling ECR keeps the last 10 images. The emergency rollback window in ID reached back to 2026-09-25 on the check date See deployment.md#rollback

Open threads with FLIN

⚠️ These come from the team's notes and ticket threads. Check the latest comments on each ticket in the Kumo tracker before acting.

  • FLIN-211 (loan list redesign, shipped in PR #131): four decisions were asked on 2026-09-23: keep "Written off" / "Macet" on closed KOL 5 loans; how manual loans count in "Total Loan Amount"; Home's total outstanding versus the section totals; the year filter. There are also label and copy sign-offs ("Loan Amount" vs "Outstanding", Cash Loan replacing Pinjol/KTA, empty-state copy, Indonesian/Filipino copy review).
  • FLIN-241…249 (feedback sheet, shipped in PR #135): six owner decisions that deviate from the tickets, each flagged on its ticket. Examples: "Cash Loan / Pinjaman Tunai" instead of "Pinjol"; "Overdue since" = the last missed due date.
  • Bank logos (PR #122): FLIN still owes correct logos for several BPRs, a BSN logo and an unidentified file. Logos are cached as immutable, so a replacement needs a new S3 key.
  • Legal copy: the Indonesian and Filipino Terms, Privacy and FAQ text are AI translations that have not had legal review. The FAQ also promises things the app does not do (product.md).
  • Philippines inputs that block PH work (multi-country.md §8, product.md#12-pending-inputs-from-flin): CIBI sample response and access, AI prompts, eligibility rules, pricing (TODO(FLIN-174)), legal text, the KOL/DPD explainer, regulator references, nav-bar tabs.
  • Onfido (PH KYC): the POC verdict was "go" (research/onfido-ph-poc.md). Still open before a contract: whether API orchestration (classic POST /checks) comes with a paid plan, PhilID/UMID coverage, and pricing against ASLI RI.
  • Xendit checkout cancel: untested with real money ⚠️.

Consolidated from every doc in this set and from the live checks. Severity: High = live risk to users, data or deploys; Med = real gap, not urgent; Low = hygiene. Security items name the issue, the component and the fix only.

Security

Sev Issue Where Fix
High Metabase is reachable from the whole internet while its database role can read every column, PII included WAF IP set flin-prod-metabase-allow, infra/terraform/envs/prod/terraform.tfvars Restore metabase_allowed_cidrs, then tofu apply -target=module.flin.aws_wafv2_ip_set.metabase_allow
High Admin IAM principals without MFA in both AWS accounts; no IAM password policy in PH; stale principals remain IAM, both accounts MFA or SSO for every human, password policy, remove stale users, rotate KumoHQ-held keys after handover
High In the shared PH account, third-party principals can reach the PH uploads (KYC) bucket and the PH Terraform state bucket, which holds secrets AWS 611858318585 Bucket policies limited to FLIN roles now; move PH prod to its own account
High The staging host sits in the ID prod account with broader network exposure than it needs, and writes to the prod uploads bucket with a long-lived IAM key Default VPC, ID account Restrict its security group, give staging its own account/bucket, use a role instead of a key
High Hand-built default-VPC hosts (websites, SFTP intake, backups) have broader network exposure than they need and are not in Terraform ID account Restrict their security groups (use SSM Session Manager for admin access), own them in IaC or move them out
High A company document-drive mirror (not app data) is stored in an app-account bucket, written by a hand-built host whose role has S3 full access ID account Owner decision; move it out of the app account; scope the role
High No CloudTrail trail in either account, no GuardDuty in PH; ALB, WAF and S3 access logging off Both accounts Organization trail, GuardDuty, access logs
High Every production secret was set by KumoHQ; the Terraform-generated ones were never rotated and sit in Terraform state SSM /flin/prod/*, /flin/ph-prod/* Rotate after handover (transfer step 3)
Med Unauthenticated scaffold endpoints in production: public todo.* read/write procedures, and POST /ai (a Gemini proxy; neither task definition sets a Google key, so it fails today) packages/api/src/routers/todo.ts, apps/server/src/index.ts Remove todoRouter, the /ai route and @ai-sdk/google; drop the todo table later. No app or job calls them
Med Fixed-code Test-user login accepted in prod in both countries; the code and numbers are committed in Terraform infra/terraform/modules/flin-env/ecs.tf, variables.tf, envs/ph-prod/main.tf Move TEST_OTP_CODE to an SSM secret, rotate it, keep the numbers out of git
Med Credentials stored as plain-text EAS variables (SENTRY_AUTH_TOKEN); obsolete EXPO_PUBLIC_ASLIRI_LIVENESS_TOKEN and unused JITPACK_TOKEN still set EAS env Rotate and re-create as Secret; delete the obsolete ones
Med OTPs are stored in plain text (Better-Auth default), and verification, session and account are readable by the read-only and Metabase roles infra/scripts/db-user.sh, metabase-bootstrap.sql REVOKE SELECT on those tables from the read roles; decide whether plain-text OTPs are acceptable
Med The app connects to Postgres as the RDS master role, with TLS but no CA verification infra/terraform/modules/flin-env/rds.tf Least-privilege app role, sslmode=verify-full with the RDS CA
Med Account-level S3 Block Public Access is off in the ID account; one legacy bucket has its blocks off and CORS *; the uploads bucket's CORS still lists KumoHQ/tunnel origins ID S3 Enable the blocks, tighten CORS, import flin-uploads config into Terraform
Med Identity documents under flin-uploads prefixes the app never writes (ktp/, bank-statement/); staging and dev data in the prod bucket flin-uploads Find the writer, separate non-prod buckets, set retention
Med Metabase runs with the API's task role (S3 read/write, Bedrock) infra/terraform/modules/flin-env/metabase.tf Give it its own empty role
Med Base image floats on oven/bun:1.2-alpine; the PH latest scan shows 2 critical and 10 high CVEs (openssl, musl) apps/server/Dockerfile Pin and bump the base image, rebuild, review scans
Low GHL webhook secret compared with !==, not in constant time packages/api/src/services/ghl-webhook.ts timingSafeEqual, as bearerMatches does
Low /f/:token capability route has no rate limit (ADR-0005 flagged it) apps/server/src/index.ts WAF rate rule scoped to /f/
Low ASLIRI_MOCK_VERIFY has no prod guard, unlike the CLIK and Finfra mock flags packages/countries/src/profiles/id.ts Ignore it when APP_ENV=prod
Low AI features use Bedrock global.* inference profiles, which may process data outside APAC packages/env/src/server.ts Compliance decision; switch to an apac.* profile if required
Low For Test users, sendOTP logs the generated code to CloudWatch packages/auth/src/index.ts Drop the code from the log line
Low The emailed report PDF password is the date of birth (DDMMYYYY), a deliberate choice that review tools flag as guessable ⚠️ packages/api/src/services/report/* Product/security decision

Operations and deploys

Sev Issue Where Fix
High Legacy Trigger.dev project still sending notifications; staging environment sending real Debt Discipline notifications Trigger.dev See Act now
High Nothing alerts on a failed pipeline or CodeBuild run in either country CodePipeline / CodeBuild EventBridge rule on FAILED → the SNS alert topic, in Terraform
High No branch protection on main, no CI tests or type-check, and a merge deploys both production servers plus jobs with no approval stage GitHub, CodePipeline GitHub Action running bun run check-types and the bun test suites; branch protection (needs a paid plan) or a pipeline approval; a required second reviewer
High Finfra key expiry 2026-11-25 with no alert Finfra Rotate in advance; alert on KEY_NOT_VALID
High Disaster recovery: uploads buckets unversioned in both countries, no AWS Backup plan, no cross-region copy, single-AZ RDS, restore never rehearsed, no RPO/RTO RDS, S3 Versioning, a backup plan (check data residency first), a rehearsed restore (operations.md#72-restore-runbook)
Med Two task-definition lineages: Terraform's env list is only a record, and its own revision points at a :bootstrap image (gone from ECR in ID). The infra README used to tell you to deploy the family-latest revision (fixed in this PR) ECS, Terraform Always clone the service's current revision (deployment.md#change-an-environment-variable-or-secret); tofu apply with -target; later collapse to one lineage
Med metabase-bootstrap.sh runs against the family-latest task definition infra/scripts/metabase-bootstrap.sh Resolve the service's task definition, as db-user.sh does
Med Payments rely only on the Xendit webhook; nothing reconciles. A lost or rejected webhook leaves a paying user stuck at pending packages/api/src/services/xendit-webhook.ts A sweep that checks stale pending sessions with Xendit, or an alert
Med The CLIK consent-upload job answers 202, so Trigger.dev shows success even when the FTPS upload fails; only Bugsink sees failures, and CLIK's deadline is 5 working days apps/server/src/index.ts (/jobs/clik-consent-upload) Alert on those errors; confirm acceptance on CLIK's side
Med ADJUST_S2S_TOKEN is in SSM and Terraform but not on the running ID task, so no server-side purchase events reach Adjust ECS flin-prod Add it through the clone procedure (the exec role can already read it)
Med The "server before jobs" deploy-order rule is not enforced: the Action and both pipelines fire on the same merge .github/workflows/trigger-deploy.yml Ship new /jobs/* endpoints in an earlier merge, or behind a dormant flag
Med ECR keeps the last 10 images of any tag, and failed builds also push, so the rollback window is short ECR lifecycle Keep more, or keep by tag
Med Internal preview builds talk to the production API, so testers create prod data, CRM contacts and charges apps/native/eas.json Point preview at staging, or test with Test users only
Med Bugsink is KumoHQ-hosted; PH errors are not captured (placeholder DSN); the app hardcodes environment: "prod" for every build Bugsink, apps/native/lib/sentry.ts Ownership decision; real PH DSN; derive the environment at build time
Med No human read path or one-off runner for the PH database: the tunnel is blocked by the EC2 vCPU quota, which the third-party UAT stack uses up AWS 611858318585, quota L-1216C47A Request a quota increase, then a targeted apply
Low CodeBuild log groups never expire; ALB deletion protection and access logs are off Both accounts Set retention; enable both
Low Stale text: Terraform outputs say "proxied"; a tfvars comment cites Rp20.000 while the price is 25000; .env.example defaults (49000 price, Coolify) infra/terraform/*, apps/server/.env.example Update the text
Low CLIK_CLASSIFY_MODEL_ID absent on the live ID task (same as the schema default); the Trigger.dev hello-world example task is deployed to prod ECS, apps/jobs/src/trigger/example.ts Add it on the next clone; delete the example

App and product

Sev Issue Where Fix
High Live "upgrade" push series says Rp29.000 / "Today only"; the price is Rp 25.000 with no discount packages/notifications/src/series.ts, copy.ts Fix the copy (server deploy only)
Med No account deletion: "Delete account" hands off to support chat, while the Privacy Policy promises deletion within 7 business days apps/native/app/delete-account.tsx Define the back-office procedure now (operations.md#84-user-data-deletion-requests); build a backend later
Med Tapping a push notification opens the app without routing apps/native (no response listener; payload carries only notifId) Add a notification-response listener that follows the inbox deeplink (store build)
Med Version gating is unused: MIN_SUPPORTED_APP_VERSION, LATEST_APP_VERSION and IOS_APP_STORE_ID are unset on both servers, so the iOS update button opens the generic App Store ECS env Set IOS_APP_STORE_ID=6781172200 now; set LATEST_APP_VERSION after each full rollout (mobile-release.md)
Med Debt Discipline procedures are not gated on the server by DEBT_DISCIPLINE_ENABLED; a direct call reaches checkout even where the programme is off packages/api/src/routers/debt-discipline.ts Add a server-side gate
Low /r/verify-id redirects to flin://verify-id, which shows "Page Not Found" apps/server/src/index.ts Point it at flin://verify-identity (server-only fix, reaches 1.1.0)
Low FAQ promises a monthly refresh for paid-report buyers and a Notifications settings screen; neither exists. Legal/FAQ text hardcodes Rp25,000 apps/native/locales/*.json Correct the FAQ; interpolate the price (store build)
Low Dev screens asliri-test and modal can be opened by deep link in store builds apps/native/app/_layout.tsx Remove or guard with __DEV__
Low A vendor outage on face match shows the "NO MATCH" badge beside "temporarily unavailable" apps/native/app/verify-identity.tsx Neutral badge for SERVICE_UNAVAILABLE
Low Email OTP message and the not-found screen are English-only packages/api/src/services/email-otp.ts, apps/native Localise
Low The store binary probably requests the microphone through expo-camera defaults ⚠️ apps/native/app.json plugins microphonePermission: false / recordAudioAndroid: false
Low Store privacy labels and Play Data Safety answers are not recorded anywhere; Purchases must be declared while the paid report is on App Store Connect, Play Console Export and check them
Low main still calls the deprecated danaTalangan.* document procedures, which blocks removing that alias apps/native/lib/use-document-upload.ts Switch to bridgingLoan.*, ship, then raise the minimum version
Low fil.json is maintained in every change but is not loaded on main apps/native/lib/locale.ts Decide: delete it or re-enable it

Data and compliance

Sev Issue Where Fix
High No retention rules: KYC images, application documents, raw CLIK and AFPI responses, biometric vendor responses and booking payloads are kept forever. Only emailed report PDFs expire (365 days) DB, S3 Agree retention with legal (OJK, the CLIK agreement, Indonesia's PDP law, the PH equivalent), then lifecycle rules and a purge job
Med No S3 deletion path: deleted documents are a soft flag only packages/api/src/routers/{dana-talangan,mediation}.ts Delete the object when the document or account is deleted
Med PH data residency (Singapore) has no recorded counsel sign-off multi-country.md §8.7 TODO(handover)
Low The upload size cap applies only to the declared size; confirmUpload does not check the stored object's length packages/storage/src/presign.ts, packages/api/src/routers/files.ts Compare ContentLength on confirm
Low Prices are 32-bit integers (caps a single PH price at about ₱21.47M) report_payment.amount_idr, debt_discipline_purchase.amount_minor Widen to bigint next time those columns change
Low The image defaults to RUN_MIGRATIONS=true, so any ad-hoc container migrates whatever DATABASE_URL it points at apps/server/Dockerfile Default to false; set true only in the Migrate stage
Low Migration 0070 adds an enum value without IF NOT EXISTS; a later migration that uses it would break a fresh-database replay packages/db/src/migrations/0070_odd_mongu.sql Use the 0049/0052 pattern; add a replay check to CI
Low Revenue analysis must exclude synthetic paid rows, and the internal/test rows carry the full price report_payment Saved Metabase views (data-model.md#gotchas-for-analysts-metabase)
Low docs/clik-data-storage.md is out of date in four places and may have been shared with CLIK docs/clik-data-storage.md Banner added in this PR; send CLIK an updated version if it was shared

Code and tooling

Sev Issue Where Fix
Med Multi-country gaps that block PH: money formatting is IDR-only; WIB is hardcoded in jobs, notifications, cron time zones and consent naming; credit-report.tsx pushes /financial-details, a step PH does not have; the app ignores features.freeCreditScore / mediation; support chat uses the ID widget apps/native/lib/money.ts, packages/api/src/services/jobs.ts, packages/notifications, apps/native/app/credit-report.tsx Fix through the multi-country.md §8 mechanisms; checklist in deployment.md#philippines-go-live-checklist
Med PH KYC: the PH mock biometric still requires ASLIRI_LIVENESS_TOKEN, which the PH task does not have, so kyc.getLivenessToken should throw for every PH user (worked out from code, not exercised) packages/countries/src/profiles/ph.ts, integrations/asliri/index.ts Return a dummy token from the mock, or land Onfido
Med PH payments: the Xendit adapter sends amountMinor as amount. PHP has two decimals, and Xendit's expected unit for PHP is unverified, so a charge could be 100× off packages/countries/src/integrations/xendit/index.ts Verify in a PH sandbox before PH payments go live
Med PH reuses ID's hardcoded GHL field/pipeline ids, CampaignHQ service/template ids and the support-chat widget id gohighlevel/field-map.ts, pipeline-map.ts, campaignhq/index.ts, apps/native/app/support-chat.tsx Get the PH accounts; move the ids into the profile or env where they differ
Med Code cites client documents that are not in the repo (Debt Discipline programme docs, the Portfolio Insight prompt PDF, the CLIK classification knowledge base) e.g. packages/api/src/routers/debt-discipline-helpers.ts, routers/portfolio-insight.ts, services/clik/score.ts Collect them into a FLIN-owned store or docs/specs/
Med No shared dev secrets vault, dev S3 bucket or dev IAM user; KYC flows cannot run locally without AWS credentials — TODO(handover); see development.md
Low withUserLock serializes per process only; with up to 4 tasks, two concurrent first AFPI pulls can each be billed by Finfra packages/api/src/services/user-lock.ts, services/finfra/store.ts A database claim row, like the CLIK pull's
Low createContext (a session lookup) runs on every request, including webhooks, /jobs/* and the health check apps/server/src/index.ts Mount the plain routes before the oRPC middleware
Low apps/web is the untouched Better-T-Stack scaffold: never deployed, cannot sign in against today's server; packages/ui is used only by it apps/web, packages/ui Decide: delete or build on it
Low AGENTS.md holds only Trigger.dev vendor rules, so non-Claude agents miss every FLIN rule AGENTS.md Point it at CLAUDE.md and engineering-rules.md
Low Node and Postgres versions are not pinned; test files are excluded from tsc; several workspaces have no check-types script root .nvmrc/engines, a test tsconfig
Low ADR drift: ADR-0001 says WhatsApp goes through the OTP port (code uses integrations.whatsapp); ADR-0004 says no flow writes both inbox and push (three do); notification-center.tsx cites an "ADR-0003 §B" that does not exist docs/adr/ Add drift notes / the missing section
Low packages/api still imports some vendor types and errors directly (e.g. AsliRiVerifyError) packages/api/src/routers/kyc.ts Move behind ports when touched
Low apps/jobs/package.json declares @flin/db, @flin/env and @flin/notifications, which apps/jobs/src does not import apps/jobs/package.json Remove the dependencies

Questions only KumoHQ or FLIN can answer

Collected from every doc. Each also sits next to the claim it affects.

Access and ownership

  • Names for every owner still marked TODO(handover) in the accounts table (ownership and transfer actions were decided on 2026-10-12), and the PH launch date that ends KumoHQ's access.
  • Who holds admin on each AWS account after the handover, how new people get access, and the break-glass procedure. Who should stay in flin-prod-db-readers.
  • Who the third party in the PH AWS account is and how to contact them (decided: PH prod stays in the shared account).
  • Who owns the Expo account flinmaybright, the Firebase project behind google-services.json, App Store Connect and Play Console admin, and the tunnelto flinapi subdomain.
  • Who owns the Cloudflare account(s) for each zone, and the registrars.
  • Who owns the Trigger.dev "Flin" org, its plan and TRIGGER_ACCESS_TOKEN, and which SERVER_URL prod and staging use.
  • Bugsink retention and backups while KumoHQ hosts it, and which tool FLIN moves to with 1.2.0.
  • Whether a local set-prod-secrets.sh (or other record of how secrets were set) exists and should be handed over through a secure channel.

Operations

  • The agreed rollback method, and how many ECR images to keep.
  • RPO and RTO targets per country.
  • Who reads kumo@flin.co.id, who is on call, the escalation path to KumoHQ during the transition, and vendor support contacts.
  • How flinapi.kumohq.in is deployed, from which branch and against which database (decided: lock it down, replace it with a Terraform staging stack, then retire it).
  • Which vendors have the NAT egress IPs allowlisted, which webhook URLs are registered in the Xendit and GHL consoles, and who manages each vendor console.
  • Who has Metabase accounts, who is admin, and which addresses go on the allowlist.

Release

  • How 1.0.0 and 1.1.0 were uploaded (EAS has no submission records), and the signing and push credentials (eas credentials).
  • Which Test-user numbers and instructions were given to Apple and Google review.
  • The current App Store privacy labels and Play Data Safety answers.
  • Whether Adjust's iOS SKAN app verification was completed.
  • Target date for the first PH-capable store build, and whether it ships the en-PH overlay (PR #127).

Data, legal, product

  • Retention required for KYC images, documents, raw bureau and biometric responses.
  • Who handles deletion requests from support chat, and how the 7-business-day promise is met.
  • Whether plain-text OTP storage is acceptable to compliance.
  • Counsel sign-off on PH data residency.
  • Legal review of the Indonesian Terms, Privacy, FAQ and consent text.
  • What writes flin-uploads/ktp/ and bank-statement/.
  • Who owns the Metabase Test-user exclusion and keeps it in sync with TEST_PHONE_NUMBERS.
  • The AsliRI contract (quota/price per call) and where engineers get AsliRI tokens for local testing.
  • Where specs and issues live from now on. Recommendation: write new specs to docs/specs/ and keep only scratch tickets local (docs/agents/issue-tracker.md still describes the local-only .scratch/ tracker).
  • Whether FLIN's CRM team's GHL workflow creates and moves opportunities in the FLIN App pipeline and calls POST /webhooks/ghl (ID and PH).
  • Who at FLIN owns each vendor contract (CLIK, Finfra, ASLI RI, CampaignHQ + WhatsApp sender numbers and Meta templates, GHL locations, Xendit, Adjust, CIBI, Onfido) and who can accept AWS Marketplace / Bedrock model agreements.
  • Where CLIK's naming guide and the FTPS credential sheet go (both were only in a developer's Downloads folder).

Material that is not in git

KumoHQ worked with a local-only spec tracker (.scratch/) and design notes (docs/superpowers/); both are gitignored. This PR promotes into the repo every local file that committed code cites, scrubbed of personal data:

Still local-only on the previous maintainer's machine, for KumoHQ to hand over through a secure channel:

  • Every other .scratch/<feature>/spec.md and its issues: Debt Discipline (FLIN-70…79), notifications and the WhatsApp templates handoff, inbox, consultations, due-day picker, loan dedup (6 AFPI ids still unresolved), CLIK classification, portfolio insight. Their substance shipped and the code is the authority, but the specs record intent and open questions. One example: .scratch/debt-discipline/product-questions.md holds open product questions marked "Blocking Phase 2".
  • Client documents that may exist nowhere else: the Portfolio Insight prompt PDF (the Google Doc link on the ticket returns 401), the CLIK loan-classification knowledge base, CLIK's naming guide. Also email drafts to CLIK, which contain contact names, so keep them private.
  • docs/superpowers/plans|specs (May–July 2026 design history) and .superpowers/sdd/ (agent scratch, low value).
  • Local .env files, the gitignored AFPI/CLIK response fixtures (real PII; do not hand these over in plain form) and google-services.json.

TODO(handover): decide where this archive goes, and check it for personal data first.

This page was last edited on 2026-10-13 04:28

Powered by Wiki|Docs

This page was last edited on 2026-10-13 04:28

FLIN
PT FLIN Consultancy Indonesia

Powered by Wiki|Docs