Stage 3 · Pixy Platform

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.

Node.js / ExpressSupabase PostgresPaddle webhooksRS256 JWTsRedis rate limiting

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 DEFINER Postgres function had been left executable by the public anon role — 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 pinned search_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: null is 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.