Model Keamanan
Boundary keamanan lapisan data — otorisasi application-layer, tenant scoping, RLS sebagai defense-in-depth, dan canonical billing ownership.
Model Keamanan
Boundary Utama: Application-Layer
Untuk semua ORM dan semua host PostgreSQL, otorisasi dan isolasi tenant ditegakkan di lapisan aplikasi:
- Permission check sebelum query — setiap oRPC procedure memeriksa permission (
requirePermission,adminProcedure) sebelum menyentuh data. - Tenant scoping di dalam query — setiap operasi membawa
tenant_id(ataubilling_account_id) sebagai predicate SQL, di semua variant ORM. - Client hanya lewat oRPC — browser tidak punya jalur langsung ke database;
DATABASE_URLbersifat privileged dan tidak pernah boleh terekspos ke sisi client.
RLS: Defense-in-Divide Berbasis Capability
RLS tetap didukung dan direkomendasikan sebagai lapisan kedua ketika host mendukung (Supabase). Namun akses data tidak pernah bergantung pada:
- impersonasi JWT per-request (
set_config('request.jwt.claims', ...)), auth.uid()di jalur data,- role
authenticated.
Alasannya: dengan koneksi ORM langsung (Drizzle/Prisma), boundary yang andal dan portabel adalah aplikasi — bukan mekanisme spesifik provider. Metadata ketersediaan RLS diekspos lewat resolveDatabaseCapabilities untuk fitur yang ingin memanfaatkannya secara opsional.
Canonical Billing Ownership
Tabel ledger billing (subscriptions, transactions, invoices, payment_orders, refunds, dst.) memakai model canonical ownership:
billing_account_idadalah sumber kebenaran pemilik.tenant_id/user_idtetap ada sebagai mirror kompatibilitas, disinkronkan otomatis oleh triggersync_billing_record_owner— jangan pernah diisi manual oleh kode aplikasi.initiated_by_user_idmencatat aktor aksi.
Idempotensi Webhook
Webhook pembayaran diproses race-safe lewat RPC claim_webhook_event: satu event hanya diproses sekali; event gagal otomatis bisa di-reclaim oleh retry provider. State-nya (processing → processed | failed) dikelola di tabel webhook_events.
Invarian di Database
Operasi dengan risiko konsistensi tinggi — ledger kredit billing (grant/reserve/finalize/release), redeem kupon, pembuatan billing account — hidup sebagai fungsi SQL SECURITY DEFINER dan hanya bisa dipanggil lewat method contract yang typed. Kode aplikasi tidak pernah menulis ledger secara manual.
