Procisely
PSI-owned URL shortener and asset link management platform. Provides persistent, branded short URLs that resolve to customer-facing destinations. Primary physical interface is a QR code sticker affixed to each shipped machine.
Overview
Procisely.com is a two-component platform:
Redirect Engine — A public-facing Node.js service at procisely.com that resolves short URLs to destinations via 302 redirects. Customers scan QR code stickers on machines and are routed to the appropriate portal, documentation, or support page.
Admin Interface — Integrated into the PSI Portal at portal.progressivesurface.com/procisely/. Provides redirect management, pattern rules, templates, QR code generation, bulk import, and analytics.
How It Works
Redirect Flow
Customer scans QR sticker on machine
→ procisely.com/S9820
→ Redirect engine looks up slug "s9820"
→ Match: SPN pattern rule (^S\d{4}$)
→ 302 → portal.progressivesurface.com/assets/s9820
→ Click event logged (slug, timestamp, user-agent, IP hash)
Resolution Precedence
Exact slug match (Redirects table) — highest priority, allows per-slug overrides
Pattern rules (PatternRules table) — regex-based, evaluated in priority order
Default redirect — https://www.progressivesurface.com
Rule Types
Type When It Runs Requires DB Record? Example Exact redirect Redirect time Yes support → progressivesurface.com/supportPattern rule Redirect time No — matches via regex ^S\d{4}$ → portal…/assets/{slug}Rule template Creation time only Produces records Admin shortcut for batch creation
Architecture
Isolation Model
The redirect engine and admin interface are fully isolated. A compromise of the public service cannot reach internal resources.
Layer Redirect Engine Admin (Portal) Compute Dedicated App Service Plan Existing Portal App Service Network Public, no VNet Private endpoint Identity Own managed identity Portal’s managed identity SQL Role procisely_redirector (SELECT + INSERT only)procisely_admin (full CRUD)
Technology Stack
Component Technology Redirect Engine Node.js 22 LTS + TypeScript + Hono Admin UI React 19 + TypeScript + Vite + TailwindCSS (in Portal) Admin API Node.js + Express (Portal backend routes) Database Azure SQL (Procisely on procserv-proddata) QR Generation qrcode + sharp + archiver (server-side)Auth Entra ID / MSAL (admin only) Hosting Azure App Service (Linux)
Infrastructure
Azure Resources
Resource Name Resource Group Details App Service Plan asp-procisely-redirect PS-WEBAPPS B1 Linux, dedicated (compute isolation) App Service procisely-redirect PS-WEBAPPS Node 22 LTS, Always On, public Azure SQL Database Procisely ProcServices-Prod-Data Basic (5 DTU) on procserv-proddata DNS Zone procisely.com PS-RG-01 Azure DNS SSL Certificate procisely.com PS-WEBAPPS Azure-managed (GeoTrust), expires Oct 2026 Policy Exemption procisely-redirect-public-access PS-WEBAPPS Exempts redirect engine from “Deny App Service Public Network Access” policy Application Insights psi-webapps-insights PS-WEBAPPS Shared with other PSI web apps
App Settings (Redirect Engine)
Setting Value SQL_SERVER procserv-proddata.database.windows.net SQL_DATABASE Procisely SQL_AUTH_TYPE azure-active-directory-default WEBSITES_PORT 8080 NODE_ENV production APPLICATIONINSIGHTS_CONNECTION_STRING (from psi-webapps-insights)
DNS Records (procisely.com)
Record Type Value @ A 52.162.107.11 (App Service IP) www CNAME procisely-redirect.azurewebsites.net asuid TXT Domain verification hash
Domain registered at Network Solutions, nameservers delegated to Azure DNS (ns1-05.azure-dns.com through ns4-05.azure-dns.info).
Database Schema
Database: Procisely on procserv-proddata.database.windows.net
Tables
Table Purpose Key Columns Redirects Slug → destination mapping Slug (PK), DestinationUrl, RedirectType, Category, IsActive, HttpStatusCode ClickEvents Per-click telemetry Id (identity), Slug, ClickedAt, UserAgent, RemoteIpHash RuleTemplates Creation-time helpers TemplateId (PK), SlugPattern, DestinationPattern PatternRules Runtime regex rules PatternRuleId (identity), Pattern, DestinationTemplate, Priority (unique)
SQL Roles
Role Permissions Used By procisely_redirectorSELECT Redirects + PatternRules, INSERT ClickEvents Redirect engine managed identity procisely_adminFull CRUD on all tables Portal managed identity
Schema source: procisely-redirect/sql/schema.sql
Security
Redirect Engine Hardening
One route: GET /{slug} plus GET /health. No admin routes, no Swagger.
Slug sanitization: 5-step pipeline — URL decode, null byte rejection, CRLF rejection, alphanumeric+hyphen allowlist (max 128 chars), lowercase normalization.
Parameterized SQL queries only — slug never interpolated into query strings.
No user input in responses — returns only 302 + Location header. No HTML body, no error details.
Security headers on all responses: HSTS, CSP (default-src ‘none’), X-Frame-Options DENY, X-Content-Type-Options nosniff, Referrer-Policy no-referrer.
Rate limiting — in-memory per-IP rate limiter.
Click event IP privacy — SHA-256 hash only, full IP not retained.
Fire-and-forget click logging — click recording never blocks redirect response. Only logs clicks for actual matches (exact or pattern) — default fallback redirects (bot probes, unknown slugs) are not logged.
Pattern Rule Safety
Regex patterns are admin-authored only (never from public input).
Slug input bounded to 128 chars (limits ReDoS surface).
Invalid regex patterns are silently skipped at runtime.
Regex validation at creation time in the admin API.
Admin Features (Portal Integration)
All admin features are at portal.progressivesurface.com/procisely/*, behind Entra ID auth and private endpoint.
Pages
Page Route Purpose Dashboard /prociselyStats, default behavior config, active pattern rules, recent activity Redirect List /procisely/redirectsFilterable table, inline toggle, bulk ops, CSV export Redirect Editor /procisely/redirects/new or /:slug/editCreate/edit with template or custom mode Bulk Import /procisely/bulk-importCSV upload, template import, combined onboard + QR flow QR Generator /procisely/qrSingle/batch QR generation with logo overlay Template Manager /procisely/templatesCRUD for creation-time templates Pattern Rules /procisely/patternsRegex rule management with live tester Analytics /procisely/analyticsClick trends, top redirects, category breakdown
API Endpoints
All endpoints at /api/procisely/*, authenticated via Entra ID bearer token.
Method Path Purpose GET /health Health check (no auth) GET/POST/PUT/DELETE /redirects/* Redirect CRUD + bulk + from-template GET/POST/PUT/DELETE /templates/* Template CRUD GET/POST/PUT/DELETE /patterns/* Pattern rule CRUD + test + reorder GET /analytics/* Click analytics + top redirects GET /redirects/:slug/qr Single QR generation POST /redirects/batch-qr Batch QR ZIP download POST /redirects/bulk-onboard Template import + QR download in one flow
Seed Data
Default Templates
Template Slug Pattern Destination Category Asset Customer Portal {serial}portal.progressivesurface.com/assets/{serial}asset Recalibration Request recal-{serial}portal.progressivesurface.com/recalibration?asset={serial}support Documentation Library docs-{serial}docs.progressivesurface.com/machines/{serial}asset Campaign Landing Page {auto}(user-specified) campaign Internal Tool Link {slug}(user-specified) internal
Default Pattern Rule
Name Pattern Destination Priority SPN Asset Portal ^S\d{4}$portal.progressivesurface.com/assets/{slug}10
This rule means every PSI serial number (S + 4 digits) automatically redirects to the asset portal without creating individual redirect records.
CI/CD
Redirect Engine
Setting Value Trigger Push to main Runner psi-internal (ps-cicd-runner, Ubuntu)Auth az login --identity (managed identity)Deploy az webapp deployment source config-zipTests 70 unit + integration tests (Vitest)
Portal (Admin UI + API)
Deployed as part of the standard Portal CI/CD pipeline. Push to master triggers build + deploy.
Additional tests: 30 API route tests (Vitest + Supertest) via npm run test:api.
Testing
Suite Count Framework Command Redirect engine unit tests 62 Vitest cd procisely-redirect && npm testRedirect engine integration 8 Vitest + Hono app.request() Same Portal API route tests 30 Vitest + Supertest cd psi-portal && npm run test:apiTotal 100
QR Code Specifications
Setting Value Content https://procisely.com/{slug}Formats PNG (raster) and SVG (vector) Sizes 128px to 2048px (PNG), scalable (SVG) Error correction Level M (15%) default, Level H (30%) with logo Logo overlay PSI logo centered at 20% of QR width Print quality 300 DPI minimum for 1” stickers Batch export ZIP archive with filenames matching slugs
Future Roadmap
Feature Priority Trigger Business Central integration High BC go-live — auto-create asset links from shipped machines Geolocation analytics Medium When click volume justifies GeoIP cost Dynamic customer routing Medium Portal queries customer from serial Bot detection Low Click analytics reliability Link expiration Low Campaign use cases grow CPQ integration Medium Auto-generate links at order time Per-redirect 301 option Low SEO benefit for permanent redirects (schema field exists)