Engineering Journal
Ginexys
Ginexys

Postmortem: We Put the Gate in the Wrong Place

2026-06-02

TLDR

Our original paywall implementation tracked guest analysis runs in localStorage and overlayed a blurred canvas gate after 2 runs. This blocked users during exploration while leaving standalone export buttons completely ungated. We refactored the entire flow: deleting ~220 lines of complex run-counting and DOM-blurring code, and replacing it with a clean 2-file architecture (GxGate + GxAuth) that hooks export buttons across all client surfaces.
Component PhaseCode base FootprintTarget Gate LocationConversion Efficacy
Legacy Usage Gate~220 lines (GUEST_KEY, DOM overlays)3rd Analysis Run (Canvas blur)Poor (High bounce rate)
Refactored Output Gate~60 lines (GxGate + GxAuth)Export / Download ButtonHigh (Captures high-intent users)

Early paywalls block exploration while missing export triggers

Our legacy implementation tracked guest usage using a counter key in localStorage (GUEST_KEY). After two runs, _requestAnalysisPermission() intercepted the request, and _renderAnalyzePaywall() covered the canvas with a blurred gradient and sign-up prompt.

This architecture suffered from two major flaws:

  1. Interrupting exploration: Curiosity-driven evaluation triggered the paywall before the user derived clear value.
  2. Ungated exports: Users accessing tools directly on standalone pages could run analysis once and download the results freely without ever seeing a sign-in modal.

Deleting redundant usage counters reduces codebase footprint

Removing the legacy usage counter required stripping extensive tracking logic across multiple files:

Code removed from analyzePanel.js (~80 lines)

Code removed from os-shell.js (~140 lines)


Decoupled modular architecture isolates gates from auth UIs

We replaced the legacy code with two focused, reusable modules in assets/js/:

  1. GxGate (assets/js/ginexys-gate.js): A lightweight gate wrapper attached to export buttons that invokes checkAuth(). It handles the three-way environment resolution (VS Code extension host, OS shell iframe, standalone window).
  2. GxAuth (assets/js/ginexys-auth-modal.js): The extracted autonomous auth modal component. Self-injects into document.body, manages Google/GitHub OAuth, and emits standard custom events (gx:signed-in, gx:auth-modal-closed).
Legacy Monolithic Architecture:
[analyzePanel.js] ---> [os-shell.js] ---> [DOM Blur Overlay]
(Hardcoded GUEST_KEY)  (~140 lines)

Decoupled Modular Architecture: [Export Click] ---> [GxGate (ginexys-gate.js)] ---> [GxAuth (ginexys-auth-modal.js)] (Environment Resolver) (Autonomous Modal) | v [gx:signed-in Event]

// Simplified Export Handler replacing 200+ lines of legacy guest counting
document.querySelector('#btn-export-pdf').addEventListener('click', async (e) => {
  const auth = await GxGate.checkAuth();
  if (auth.signedIn) {
    executeExportPipeline();
  }
});
Rule of thumb: Gate at the moment of value extraction, not during exploration. If an architectural refactor allows you to delete 200 lines of complex state-tracking code in favor of a clean event hook, do it immediately.
Read this post in the full Engineering Journal →