Build Status
Last updated: 2026-11-04. Edit this file directly — it renders live at /status, no rebuild needed.
Production deployment
- done Deployed and live at
offline.getfundity.com— Dockerized, same Contabo box asfundity-django-backend, GitHub Actions CI/CD (.github/workflows/deploy.yml) mirroring that repo's pattern. Confirmed reachable:/api/v1/health,/status,/docs/*,/api/docsall return200. - done Running
TERMII_MODE=livein production with realTERMII_API_KEY/TERMII_WEBHOOK_SECRET/TERMII_SENDER_ID— this is live, not mock; real SMS sends incur real cost. - pending
TERMII_SENDER_IDapproval status in production not yet confirmed — if still pending, outbound sends are automatically using the sharedN-Alertfallback rather than a "Fundity"-branded sender, which works but isn't final. - pending
OFFLINE_SMS_DESTINATION_NUMBER(Flutter-side) confirmed against whichever number/Sender ID is actually live. - pending Real on-device SMS send/receive test against the now-live production server (have a physical device + SIM; not yet run against production, only ever proven against mocks).
Offline SMS Transfer
- done Scaffold — FastAPI + async SQLAlchemy (psycopg3) + Alembic +
/api/v1/health - done Data layer —
accounts+offline_messagestables, seed endpoint, duplicate-constraint proof - done AES-256-GCM encryption round-trip (Python) — proven in isolation
- done AES-256-GCM encryption round-trip (Dart/Flutter) — proven cross-language both directions against Python
- done PBKDF2-HMAC-SHA256 PIN hashing
- done Outbound SMS integration (
POST /api/v1/sms/send) — code complete, live in production - done Full processing pipeline (
/offline/test,/offline/status) — all branches proven (success, wrong PIN, insufficient funds, duplicate) - done Real inbound webhook + Termii signature verification (
X-Termii-Signature, HMAC-SHA512) — proven against a self-signed simulated delivery; not yet proven against a real Termii-delivered inbound SMS in production - done Duplicate protection under retries — regression test passing
- done Flutter:
OfflineTransferScreenwired to real encrypt+send service (Android auto-send/listen, iOS manual compose), pointed at the live production backend - done Sender account sourced from real auth/session instead of the placeholder
0000000001—POST /api/v1/accounts/link+GET /api/v1/accounts/me(both behind the existing OTP+JWT auth,app/services/account_linking.py) provision a real per-phone-number POC account on demand; Flutter calls this once via a new offline-account-setup screen instead of hardcoding0000000001 - done PIN attempt lockout on the offline transfer PIN — 5 wrong attempts locks the account for 30 minutes (
app/services/transfer.py,Account.failed_pin_attempts/locked_until) - pending Real on-device SMS send/receive test (see "Production deployment" above)
Termii (sole SMS/OTP provider)
KudiSMS was evaluated as a second provider (self-managed OTP, primary/ fallback dispatch) but has been fully removed — Termii is the only SMS provider, nothing else branches on provider choice.
- done Dynamic Termii config —
TERMII_MODE=live|sandbox|mock, no hardcoded API key/base URL/sender ID/destination numbers - done Sender ID fallback chain — automatic retry on
SENDER_ID_NOT_APPROVED, zero code change to adopt the approved ID once it lands - done OTP send/verify via Termii's real Token API (
/api/sms/otp/send,/api/sms/otp/verify) — Termii owns PIN generation/expiry server-side - done 6-digit PIN enforced both in the Termii request (
pin_length=6) and server-side Pydantic validation - done Resend cooldown (60s default) + hourly request cap (5 default) per phone number
- done OTP expiry, wrong-PIN, and max-attempts states all distinct and tested
- done Auth/session layer —
userstable, JWT access token (15 min) + opaque rotating refresh token (30 days) stored hashed - done Logout / session invalidation — refresh token revocation proven (revoked token can't refresh again)
- done
GET /api/v1/auth/me— example protected route - done Mock provider (
TERMII_MODE=mock) — full OTP+auth flow tested end-to-end with zero network calls, for CI/offline dev - done
TERMII_MODE=liverunning in production (see "Production deployment" above) - pending Real-device test against production
TERMII_MODE=live - pending Emulator test walkthrough documented and run
Documentation site
- done
/docs/architecture - done
/docs/implementation - done
/docs/security - done
/docs/faq - done
/investors - done
/status(this page)
Not started / explicitly out of scope for now
- pending Rate limiting on
/sms/sendand/webhook/sms/inboundthemselves (only OTP request/verify, and the offline transfer PIN, are rate-limited today) - pending Real bank integrations, real interbank transfers, fraud engine — out of scope per the original architecture doc
- pending Bridging to real customer balances — this POC's
accountstable is still an isolated test ledger, neverfundity-django-backend's realUser.balance. That backend has no debit/credit implementation at all yet (User.update_balance()is an empty stub) — flagged to the backend owner directly, blocks real money movement online or offline, not specific to this repo. This is the main thing separating "deployed and reachable" from "actually moves real customer money."