Quill is a hackathon build that uses Duro; it is not part of the Duro monorepo. The backend lives in
quill-backend, the frontend in quill-frontend. For the demo, DURO_MODE selects between a MockDuroAdapter (deterministic, offline) and an HttpDuroAdapter that hits a real Duro instance in sandbox with an sk_test key. See Duro Integration.What Quill owns vs. what Duro owns
The boundary is the whole point. Quill keeps product state; Duro keeps money state. Quill mirrors just enough of Duro’s state (ids and status) to render its own UI and gate content.Quill owns
Users and roles (
reader / creator / admin), publications, posts (TipTap/Markdown), premium-gated video (HLS), comments, the creator dashboard, and an append-only ledger_entries table that records recognised revenue for its own earnings/recovery views.Duro owns
The customer, the plan, the checkout session, the subscription state machine, the renewal + dunning engine, the recovery decisions, bank transfers (payouts), and VAS/bill vending. Quill never charges a card or moves money itself.
Why it is a strong reference integration
Exercises the managed-subscriptions path end to end
subscribe → checkout → verify → webhook activation → renewal → past_due → recover all run through Duro’s public REST surface (/customers, /plans, /checkout/sessions, /subscriptions/*), with Idempotency-Key on every POST.Consumes the recovery-first product, not just billing
Quill surfaces a creator recovery view (at-risk MRR, recovered revenue, recovery rate) driven by Duro’s
subscription_past_due / subscription_payment_recovered webhooks. Recovery is a feature of the host product, so it becomes a feature of Quill for free.Uses Duro for money-out and bills too
Creator payouts route through Duro transfers; airtime, data, betting, cable TV, and electricity route through Duro VAS; bank lists and name resolution come from Duro. One processor, four money flows.
Same-origin BFF, clean trust boundary
The Next.js app never talks to the API directly from the browser. A catch-all proxy (
app/api/[...path]/route.ts) forwards to the NestJS backend and relays the httpOnly session cookie, mirroring Duro’s own BFF pattern.The lifecycle in one glance
Two independent activation paths converge on the same result: the synchronousverify poll (for the reader who is still on the page) and the asynchronous signed webhook (the source of truth). Both are idempotent on the ledger, so activation happens exactly once. This dual-path design is detailed in Subscriptions & Recovery.
How to read this section
1
Architecture
Architecture is the whole system at altitude: NestJS modules, the Drizzle schema, the same-origin BFF, BullMQ workers, and R2 media.
2
Duro Integration
Duro Integration is the core: the
DuroClient interface, the HTTP adapter’s exact calls, idempotency, and the signed webhook receiver.3
Subscriptions & Recovery
Subscriptions & Recovery walks the status machine and the recovery-first creator metrics.
4
Payouts & VAS
Payouts & VAS covers money-out (bank accounts, OTP, T+1 settlement) and bill vending, both via Duro.