This blueprint specifies the Flip 360 commission platform at three depths: system context (who interacts and what external systems we depend on), containers (the deployable units and how they relate), and data model + non-functional requirements (the schema and the SLAs we commit to). Every architectural decision is anchored to a real-world system your CIO already trusts — ASX CHESS, SWIFT, NPP, AUSTRAC, Visa/Mastercard, CBA NetBank biometric, Certificate Transparency. We did not invent any of these primitives. We assembled them, deliberately, for one specific market: professional referral settlement.
Every other architectural decision flows from this one diagram. Five events. Each signed by the correct actor on hardware-bound keys. Each hash-linked to the one before it. When the chain is complete and only when the chain is complete, Stripe Connect raises a Transfer, the RCTI is issued, and the referring member is paid. Nothing about that sentence depends on the goodwill or memory of any participant — the chain either completes or it doesn’t, and the missing signature is visible to all parties in real time.
The world around the platform. Six actor classes interact with Flip 360, and the platform depends on a set of named external systems. No actor has direct database access; no external integration runs without a defined contract.
The deployable units. Six containers, each with a clear technology choice, a named runtime, a defined purpose, and the routes / surfaces it owns. The platform is intentionally small: every line of code lives in one of these six.
Server-side-rendered application. Serves member portal, admin ERP, investor dashboard, FAQ, marketing surfaces. Stateless. Reads/writes via D1 binding and Stripe SDK.
/ (marketing)/me (member portal)/app (admin ERP)/app/payouts/chain/faq/blueprint/architecture/business-case/engageTakes a chain-complete referral event. Resolves the Rate Card. Computes split between referrer, recipient, and 1% platform fee. Emits a settlement intent. Pure function, deterministic, fully testable.
Internal module — invoked on outcome-confirmed handshakeTranslates settlement intent into Stripe PaymentIntent + Transfer + ApplicationFee. Idempotency-keyed on chain-event ID so a retried call cannot double-charge. Webhook listener verifies every state change and writes back to D1.
POST /api/stripe/webhook — idempotentPOST /api/payouts/initiate — admin onlyPrimary store. Append-only event chain (handshakes), members, communities, rate-cards, RCTIs, payouts, disputes, audit_log. Schema is migration-managed. Row-level immutability enforced at app layer via insert-only access paths to chain tables.
Bound to Worker as env.DB — no direct external accessEvery hour, computes the Merkle root of all chain events written since the previous anchor. Submits the root to an independent notary log. Any later tampering with chain history breaks the root chain and is externally provable.
Cron: 0 * * * * — hourly anchorGET /api/chain/proof/:event — returns inclusion proofSends handshake-required nudges, RCTI receipts, payout confirmations, dispute alerts. Idempotency-keyed on chain-event ID — same event never sends two emails.
Internal queue — invoked from C2 on state transitionsSix core tables. handshakes and audit_log are insert-only — the row-level immutability that lets us claim "tamper-evident" with a straight face. members, rate_cards, rctis, anchors are reference / settlement state. All money is stored as integer cents — no floats. All timestamps are UNIX ms.
handshakes
SQLite · D1
The append-only chain. Every event in a referral’s lifecycle is one row. Insert-only. Each row carries a SHA-256 hash of (previous_hash + row_payload), forming the chain.
| Column | Type | Notes |
|---|---|---|
id |
TEXT PK | ULID, lexicographically sortable |
referral_id |
TEXT FK | Groups the 5-event chain |
event_type |
TEXT | intent | acknowledgement | intake | settlement | outcome |
actor_id |
TEXT FK | Which party signed this event |
previous_hash |
TEXT | SHA-256 of the prior chain row |
payload_hash |
TEXT | SHA-256 of the canonical event payload |
row_hash |
TEXT | SHA-256(previous_hash || payload_hash) — the chain link |
signature |
TEXT | WebAuthn / Secure-Enclave signature over payload_hash |
signed_at |
INTEGER | UNIX ms timestamp from signing device |
received_at |
INTEGER | UNIX ms timestamp server-side (clock skew detection) |
anchor_id |
TEXT FK NULL | Set when this row is included in a notary anchor |
members
SQLite · D1
Vetted professionals. One row per person. ABN-validated against ABR on join. GST registration status drives RCTI treatment.
| Column | Type | Notes |
|---|---|---|
id |
TEXT PK | ULID |
abn |
TEXT UNIQUE | Validated against ABR |
gst_registered |
INTEGER | 0 | 1 — drives RCTI GST treatment |
community_ids |
TEXT JSON | Memberships in geo-polygon communities |
honour_points |
INTEGER | Reputation score — confirmer-awarded |
tier |
TEXT | founder | standard | senior |
status |
TEXT | applied | vetted | active | suspended | resigned |
stripe_account_id |
TEXT | Stripe Connect connected-account ID |
pub_key |
TEXT | WebAuthn / Secure-Enclave public key for signature verification |
rate_cards
SQLite · D1
Founder-defined rate card per community per professional category pair. The single source of truth for what a referral chain is worth.
| Column | Type | Notes |
|---|---|---|
id |
TEXT PK | ULID |
community_id |
TEXT FK | Which 50km community this applies to |
referrer_cat |
TEXT | e.g. accountant, broker, lawyer |
recipient_cat |
TEXT | e.g. broker, lawyer, conveyancer |
pct_to_referrer |
REAL | Share of net commission to referring member |
pct_to_recipient |
REAL | Share retained by receiving member |
pct_to_platform |
REAL | Always 1.00 for the platform fee in v1 |
effective_from |
INTEGER | Versioned — rate cards are immutable once chain-referenced |
rctis
SQLite · D1
Recipient-Created Tax Invoice register. One row per settled commission. Each row chain-anchored to a settlement handshake.
| Column | Type | Notes |
|---|---|---|
id |
TEXT PK | RCTI number — sequential per AU financial year |
referral_id |
TEXT FK | The chain this RCTI settles |
supplier_member_id |
TEXT FK | The referring member (the supplier) |
recipient_member_id |
TEXT FK | The receiving member (the buyer of the referral) |
amount_excl_gst |
INTEGER | Cents — always store integer cents |
gst_amount |
INTEGER | Cents — 0 if supplier not GST-registered |
platform_fee |
INTEGER | Cents — 1% Stripe application_fee_amount |
stripe_transfer_id |
TEXT | The Stripe Transfer object ID |
issued_at |
INTEGER | UNIX ms |
pdf_object_key |
TEXT | R2 object key for the immutable PDF |
anchors
SQLite · D1
Notary anchor log. Each row = one hourly Merkle-root submission to an external log. Used to prove non-tampering of chain rows after the fact.
| Column | Type | Notes |
|---|---|---|
id |
TEXT PK | ULID |
merkle_root |
TEXT | SHA-256 root of all chain rows since previous anchor |
first_event_id |
TEXT FK | First chain row included in this anchor |
last_event_id |
TEXT FK | Last chain row included in this anchor |
notary_response |
TEXT | Notary log inclusion proof + signed receipt |
anchored_at |
INTEGER | UNIX ms — submission timestamp |
audit_log
SQLite · D1
Append-only admin action log. Every change a Platform Admin makes (vetting decision, dispute resolution, rate-card edit) is one row. Insert-only, hash-chained.
| Column | Type | Notes |
|---|---|---|
id |
TEXT PK | ULID |
admin_id |
TEXT FK | Acting admin |
action |
TEXT | vetting.approve | dispute.resolve | rate_card.publish | ... |
target_id |
TEXT | The entity acted on |
before |
TEXT JSON | State before — for diff |
after |
TEXT JSON | State after — for diff |
previous_hash |
TEXT | Chain link to previous audit row |
row_hash |
TEXT | SHA-256 of this row + previous_hash |
acted_at |
INTEGER | UNIX ms |
Every external system, the direction of data flow, the protocol, the auth posture, and what it does. Nothing is integrated by accident. No surprise dependencies; no shadow IT.
| System | Direction | Mode | Auth | Purpose |
|---|---|---|---|---|
| Stripe Connect (AU) | Out + In | REST + Webhooks |
Restricted API key + webhook secret | KYC onboarding, PaymentIntent, Transfer, ApplicationFee, payout. Webhooks verified via signature header. |
| Cloudflare D1 | In-process | Binding |
Worker binding (env.DB) — not network-exposed | Primary persistence. SQLite over HTTPS internally to Cloudflare. No SQL injection surface (parameterised statements only). |
| Cloudflare R2 | Out + In | S3-compatible |
Worker binding (env.R2) | Immutable RCTI PDF storage. Object-lock equivalent enforced at app layer. |
| ABR (ABN lookup) | Out | REST |
Bearer token | Member ABN validation at join. Cached for 30 days. |
| WebAuthn / passkeys | In | Browser API |
Hardware-bound key | Member signature on each handshake event. Public key stored in members.pub_key. |
| External notary | Out | REST (signed) |
Signed request | Hourly Merkle root submission. Notary returns inclusion proof. |
| Resend / SES | Out | REST |
API key | Transactional email — handshake nudges, RCTI receipts, payout confirmations. |
| Twilio (fallback) | Out | REST |
Account SID + token | SMS fallback for handshake nudges if email bounces twice. |
| AUSTRAC (future) | Out | REST (manual v1) |
Designated business credentials | Threshold reporting hooks. Manual submission in v1; automated when AUSTRAC API access is granted. |
The SLAs the platform commits to, and the architectural choice that makes each one achievable. Availability, latency, durability, residency, idempotency, auditability, privacy, security, observability, DR — every one of these is a measurable commitment, not a slogan.
| Category | Target | How we hit it |
|---|---|---|
| Availability | 99.9% monthly | Cloudflare Workers global edge — multi-region by default. D1 replicas. No single-region failure domain. |
| Latency | p95 < 200ms TTFB | Edge SSR — compute runs at the user’s nearest Cloudflare PoP. No origin round-trip. |
| Durability | 11x9s on D1 + R2 | Cloudflare’s underlying storage durability. Chain anchored hourly to external notary as extra evidence layer. |
| Data residency | AU only | D1 region pinned to APAC-AU. R2 region pinned to APAC-AU. Webhook ingress through AU edge. |
| Idempotency | Exactly-once settlement | Stripe idempotency key = chain-event ID. Duplicate webhook → same outcome, no double-charge. |
| Auditability | 7-year retention | Insert-only chain tables + hourly notary anchor. Any row can be proved to have existed at a given point in time. |
| Privacy | Privacy Act 1988 APPs 1–13 | AU residency, encryption at rest (Cloudflare default), purpose limitation in app layer, access via APP 12 endpoint. |
| Security | OWASP top 10 + dependency audit | CSP headers, HSTS, parameterised SQL only, hardware-bound auth, secret rotation discipline (Wrangler secrets). |
| Observability | p99 error rate visible in < 60s | Cloudflare Logs → structured JSON → error-rate dashboard. Payout SLA dashboard. Chain-health dashboard. |
| Disaster recovery | RPO < 1 hr, RTO < 4 hr | D1 point-in-time restore + hourly notary anchor allows chain reconstruction from external proofs. |
For each architectural primitive Flip 360 uses, the named real-world system that already runs at scale on the same pattern. This is not a marketing analogy; it’s the engineering parallel a CIO would draw on a whiteboard.
| Named real-world system | What it guarantees | Flip 360 equivalent |
|---|---|---|
| ASX CHESS | Immutable trade register, T+2 settlement | Hash-linked handshake chain + RCTI register |
| SWIFT | Non-repudiable cross-bank settlement messages | Stripe Connect + signed handshake events |
| NPP (New Payments Platform) | Real-time, irrevocable, idempotent settlement | Idempotent settlement on outcome confirmation |
| AUSTRAC reporting | Tamper-evident transaction record | Append-only hash-linked event chain |
| Visa / Mastercard auth | Idempotent debit — no double-charge | Idempotent webhook handling with deduplication keys |
| CBA NetBank biometric | Hardware-bound signature (Secure Enclave) | Apple Secure Enclave / Android StrongBox / WebAuthn |
| Certificate Transparency | Externally-anchored append-only audit log | Hourly Merkle root anchored to independent notary |
These are the six engineering outcomes a clearing house delivers to a market participant. Flip 360 delivers the same six to a referring member. The asset class differs; the engineering does not.
The conversation gets very short. Every primitive in this blueprint is mature, broadly deployed, and audited at scale by parties far larger than us. Show me which decision you’d make differently — and we’ll talk about it.