The License Server
A monolithic, deliberately boring Express service on Render talking directly to Supabase Postgres — with focused helper modules for the parts that deserve isolation and their own tests.
Core endpoint
Validation, signing and entitlements
POST /api/validate-license is the core endpoint. It matches tool (including comma-separated multi-tool licences), product and expiry; auto-expires and deactivates lapsed licences; then resolves device activation against a device_activations table keyed by licence plus fingerprint hash. Responses are RSA-signed.
Lease and cloud tokens are RS256 JWTs signed with the same key, with the public half derived and memoized so key rotation is picked up automatically, and constant-time secret comparison throughout.
The grandfathered “all” bundle
A subtle entitlement rule shows the domain complexity: an all-tools licence bought before a cutoff date gets the core tools only, while newer purchases include the extended tools (Image to Blocks, Space Frame, Fin, Kinetic). The server computes this from purchase_date and stamps a flag into the signed payload; the add-in greys the extended tools accordingly.
Paddle Billing
Payments
POST /webhook/paddle verifies a Paddle-Signature (HMAC-SHA256 over ts:rawBody, a 300-second replay window, timing-safe compare) and dispatches through an ordered pipeline — refunds, then upgrade campaign, trial conversion, AI-Suite, and finally generic purchase or cancellation — so a tagged event is always handled by exactly one path.
A purchase resolves tool and tier from the Paddle price_id, creates or activates the licence, and emails the key via Resend. The product catalogue — Ultimate Suite, Generate Bundle, Modify Bundle, per-tool and AI-Suite plans across Solo, Team and Enterprise tiers — is seeded into a products table and mirrored in code.
Database
Self-healing schema
Schema management is idempotent and self-healing. At boot a single cheap probe (schemaLooksCurrent) checks sentinel tables and columns; if anything is missing it runs the additive migration set (CREATE TABLE IF NOT EXISTS / ALTER TABLE ADD COLUMN) even when auto-migration is disabled, and treats any uncertainty as “stale” rather than skipping.
Structural, riskier migrations run through a standalone runner that snapshots prior state for reversibility.
Hardening
Security posture
Security is treated as a first-class, continuously-tested property rather than a checklist.
-
RLS lockdown. A SECURITY DEFINERPostgres function had been left executable by the publicanonrole — a licence-enumeration and seat-burning oracle callable with the public key. A migration revokes public, anon and authenticated execution, rebuilds the body with input validation and a pinnedsearch_path(closing a temp-schema hijack), and aborts if verification shows anon can still call it. -
Pinned database TLS. The Postgres connection verifies Supabase’s certificate against a pinned CA rather than trusting the system store. -
Layered rate limiting — Redis-backed with an in-memory insurance limiter — on the API, licence keys, admin login, OTP and cloud auth, plus a fingerprint blocklist. -
Log hygiene. Wrapped console methods redact emails, licence keys and ids; a dedicated test fails the build if a secret can reach the logs. -
Isolated CORS policy with a frozen canonical-origins allowlist. The Revit add-in sends no Origin and is allowed; Origin: nullis blocked; there is no wildcard. -
Boot guards that refuse to start the service with unsafe configuration — for example unsigned webhooks outside development.
Services
Integrations
Resend
Licence delivery, purchase receipts, OTP codes and marketing campaigns from a verified domain.
Telegram admin bot
The support desk and ops console. Customers open tickets from inside Revit; admins reply with /reply <ticket> <text> or a native Telegram reply, and error-level server logs fan out to admins in real time. Built on a compatibility shim over a major node-telegram-bot-api version bump, with its own test suite.
AI proxy & PixyCloud
Covered in Stage 4.