Skip to main content

Bilko Demo Gate (TrialGatePlugin)

Bilko Demo Gate (TrialGatePlugin)

Šta je TrialGatePlugin

TrialGatePlugin je Ktor ApplicationPlugin koji se aktivira na AuthenticationChecked lifecycle hook-u i implementira dva paralelna guarda:

  1. Demo read-only guard: Demo sesije (gdje je principal.isDemo == true) dobijaju 403 DEMO_READ_ONLY na svim non-GET zahtjevima osim eksplicitno izuzetih putanja.
  2. Trial expiry gate: Organizacije čiji je trial_expires_at < now() i nemaju aktivnu pretplatu dobijaju 403 TRIAL_EXPIRED na GATED_PREFIXES rutama (revenue-blocking feature gating).

Plugin radi na SVAKOM autentikovanom endpointu — nema per-ruta bypass osim early-return provjera na vrhu plugina.

Izuzeci (exemptions)

Plugin definira tri vrste izuzeća:

  • EXEMPT_EXACT: Tačne putanje koje preskačaju OBJE provjere (demo read-only I trial expired).
  • EXEMPT_PREFIXES: Prefiks putanje koji preskače OBJE provjere.
  • DEMO_CHATBOT_EXEMPT_PATH: Samo /api/v1/chatbot/message — preskače SAMO demo read-only guard unutar demo grane; trial-expired gate i dalje radi normalno. Korišten za MC #105459 da omogući AI chat na demo sesijama bez otvaranja rupe u trial enforcement-u.

Early-return logika se izvršava PRIJE demo guarda i trial gatea — ako putanja nije ni u jednom EXEMPT/GATED listi, guard se ne izvršava uopšte (implicitni allow).

Demo AI Chatbot Izuzetak (MC #105459)

Problem: Demo sesije koriste dijeljeni org (bez chat_conversations tablice, bez org-context-builder rute) i ne perzistiraju chat kontekst. TrialGatePlugin je blokirao POST /api/v1/chatbot/message na demo sesijama sa 403 DEMO_READ_ONLY.

Odluka: Dodati named-path izuzetak SAMO unutar demo-guard grane (if (principal.isDemo && method != GET && path != DEMO_CHATBOT_EXEMPT_PATH)), ne u EXEMPT_EXACT na vrhu plugina, jer EXEMPT_EXACT bi također izuzeo rutu od trial-expired gate-a (što nije poželjno — chatbot je GATED_PREFIXES feature i treba biti blokiran za organizacije sa isteklim trial-om).

Implementacija (commit e22af78f, PR 130):

  • TrialGatePlugin.kt: DEMO_CHATBOT_EXEMPT_PATH = "/api/v1/chatbot/message" konstanta + provera u demo-guard grani.
  • ChatbotService.kt: isDemo parametar, stateless path za demo sesije (preskače chat_conversations read/write i org-context-builder), novi checkDemoChatRateLimit (per-IP 10/min, nezavisno od per-user limita).
  • ChatbotRoutes.kt: Wiring isDemo + checkDemoChatRateLimit; baca TooManyRequestsException za 429 (ne call.respond() — StatusPages bug klasa #104962).
  • ChatWidget.tsx: Demo-mode-aware error fallback (isDemoSession iz auth-store).
  • Integracioni testovi: ChatbotDemoIntegrationTest.kt (6 test case-ova).

Kanonski izvor u repo-u

  • BUILD-BLUEPRINT.md sekcija "TrialGatePlugin / Demo Gate"
  • apps/api/src/main/kotlin/no/alai/bilko/features/TrialGatePlugin.kt

Liveness Testing Lekcija

Svaki gate/guard plugin zahtijeva integracioni liveness test koji dokazuje da guard radi kroz CIJELI Ktor pipeline (ne samo unit test ili izolovani route test). TrialGate je jednom bio mrtav od shipovanja — aplikacija se pokrenula ali guard nije radio — što je otkriveno tek nakon deploy-a. ChatbotDemoIntegrationTest.kt je primjer: testira protiv real configureForTest() wiringa, sa live StatusPages handlers (koji su uhvatili latentni bug u 429 obradi tokom test-writing-a).

Evidence: ~/system/evidence/105459/implementation-report.md

Memo: technical_bilko_e2e_green_sprint_2026-07-10.md (TrialGate mrtav-od-shipovanja incident)