Kalman Banu · Brisbane
Shipping log
Everything I have shipped on Syrvos, in the words I committed it in. This page is generated from the git history rather than written for the website — which is the only reason it is worth anything.
344 changes across 68 days. Some months are busy and some are nearly empty; that is what building something alone actually looks like, and smoothing it out would defeat the purpose.
Security work is deliberately left out. Naming what was hardened, and when, is not information a pre-launch product should publish.
→ · RSS
September 20268
- the console's views are addressable, its lists actionableplatform
- tenant detail links into the tenant's own entitlements and sessionsplatform
- the console's tables search and sort at volumeplatform
- loading and error boundaries — a slow query no longer looks brokenplatform
- elevation ceremony harness drives a real elevation from a test runtesting
August 202654
- read-only entitlement exposure assessment before promotionops
- the capability gets the interface it never hadcontacts
- a downgrade tells its cost, the export derives from modulesentitlements
- declare core, exports and the contacts surfacesfeatures
- a downgrade tells the customer what it costs thementitlements
- 1195 — tell the tenant before the product changes under thementitlements
- chat_ai can be sold safely — the meter declaration gets a consumerentitlements
- 1171 — every plan grant records the packaging revision it was issued underentitlements
- 1190 — trials get a route and a button; dunning gets a screenentitlements
- 1240 — the suspension lever gets a buttonentitlements
- 1188 — per-tenant suspension: cut one tenant off without punishing the fleetentitlements
- 1186 — the core-capability promise becomes an invariantentitlements
- TenantApp becomes an activation record, the permission axis diesentitlements
- X.6 slice 5 — delete the strangler adapter and the legacy systementitlements
- X.6 slice 4 — the app catalogue is derived, the constants dieentitlements
- count agent AI suggestions, both AI paths now meteredentitlements
- X.6 slice 3 — retire the legacy feature-flag axisentitlements
- production gets a post-deploy smoke test, and both environments share one scriptops
- production gets a post-deploy smoke test, and both share one scriptops
- native canned responses — the /shortcode picker finally has a data sourcechat
- the packaging decisions land in data — catalogue cut to 6 real + 2 probes, chat_ai copy tells the truthstorefront
- Chatwoot provisioning now requires an explicit opt-in, default offchat
- the add-on loop proven end to end against real Stripee2e
- add-on pricing has one source — the module contractcommerce
- a capability 402 is handled at every apiFetch siteclient
- the session-policy screen exists, and onboarding stops linking a page that never didsettings
- the menu cannot claim what the modules do not declarenav
- every declared module field must change somethinggovernance
- bind an add-on's declared terms to the catalogue that prices itentitlements
- close the tenant route-gating hole in both directionsentitlements
- freeze the legacy lists for the migration windowentitlements
- derive the registry, manifests and plan grants from feature modulesfeatures
- flip enforcement, kill a capability and grant a tenant, from one commandcli
- a tenant you can open for every entitlement stateseed
- the FeatureModule contract, designed against what actually shippedfeatures
- per-capability enforcement, a bounded test pool, and a deploy that cannot collideentitlements
- audit-only mode now records what it would blockentitlements
- make "no feature decisions from tenant.plan" un-driftablegovernance
- give the paywall a design system and the telemetry a readerentitlements
- close the two paywalls that lived outside the systementitlements
- usage counters that need no reset job and fail openentitlements
- "Your plan" — a statement, not a shopentitlements
- a refusal that says which refusal it isentitlements
- count every refusal — denials are demand dataentitlements
- a kill switch that shows its cost and lifts itselfentitlements
- exceptions-first console and the provenance it already storedentitlements
- notice when chat dies, and say so when the kill switch is pulledops
- make the visitor widget disappear when chat is disabledchat
- elevation ceremony in the override console tooplatform
- elevation ceremony UI — refuse, verify, replayplatform
- gate destructive platform ops on a live elevationplatform
- just-in-time elevation model + serviceplatform
- bind the originating flow into the completion tokenauth
July 202687
- assign-role console — delegate a graduated role + Authentik syncplatform
- enforce graduated roles — 39 platform routes to withPlatformPermission + gated invariantplatform
- create the add-on annual Stripe Price + single source of truth for its amountbilling
- surface prorated add-on charge before purchase confirmbilling
- add-on purchase flow — webhook-confirmed grant, interval, cancel-at-period-end, plan-absorbbilling
- global capability kill-switchentitlements
- seat limit — the one numeric quota, atomic hard capentitlements
- self-serve enable/disable of entitled capabilitiesentitlements
- marketplace storefrontentitlements
- locked-state nav for capability-backed entriesentitlements
- kill-switch seam — every chat route resolves `chat` before doing workchat
- gate quotes dashboard pagesentitlements
- branding public-surface monogram fallbackentitlements
- gate quotes & proposals authoring routesentitlements
- gate client billing via Connectentitlements
- gate tenant branding / white-labelentitlements
- nightly reconciliation sweep + drift alertingentitlements
- super-admin override consoleentitlements
- migrate custom-domains gate onto the capability resolverentitlements
- capability manifests + shared 402 upgrade-prompt handlerentitlements
- gate client portal — neutral unavailable, never 402entitlements
- env-gated resolver source flip (adapter→table)entitlements
- enforce discord capability across routes + jobsentitlements
- migrate chat_ai enforcement onto withCapabilityentitlements
- gate Contacts routes with withCapabilityentitlements
- withCapability enforcement mechanism, 402 semantics & job guardentitlements
- append-only write paths, plan grants & backfillentitlements
- EntitlementService, TenantContext & cross-instance cache invalidationentitlements
- new tenants default to "always available"chat
- enable web push for new-message notificationschat
- persisted agent status (Available/Away/Invisible)chat
- closed-date exceptions for business hours (holidays)chat
- availability modes — always / business_hours / agent / hybridchat
- any-page in-app toast for new visitor messageschat
- dashboard-wide presence + activity-based auto-awaychat
- self-serve business-hours editor + platform chat settingschat
- business-hours availability + unattended-message emailchat
- verify platform-chat realtime keystone + full-flow E2Eplatform
- surface the chat inbox on /platformplatform
- thin /api/platform/chat/* inbox data layerplatform
- route apex marketing chat to the platform tenantplatform
- make Syrvos a real "tenant-zero"platform
- tenant logo upload on local disk behind a storage drivertenancy
- branded tenant front door + tenant-aware chrometenancy
- tenant branding settings — colour + taglinetenancy
- 19 outbound tenant links use the canonical custom domaindomains
- 18 canonical white-label redirect (subdomain → primary custom domain)domains
- 16 production rollout — §5b Traefik catch-all + mem-fixdomains
- 15 delegate custom-domain DCV to Cloudflare (cert issuance + renewal)domains
- route custom domains to their tenant (Node middleware) — CD-8 (audit A1)domains
- subdomain-as-default + locked upsell Domains UI (CD-6/CD-7)domains
- billing-driven custom-domain cutoff + auto-restore (CD-5)domains
- enforce the custom-domains paywall gate on add (CD-4)domains
- per-plan custom-domain quota (CD-3)domains
- custom-domain entitlement + activation gate + master switch (CD-2)domains
- custom_domains AGENCY-only feature flag (CD-1)domains
- SSL reconciliation + stale/orphan cleanup + per-tenant capdomains
- custom-domain SSL lifecycle via Cloudflare for SaaSdomains
- Cloudflare for SaaS custom-hostname service + client seamdomains
- background DNS-verification pollerdomains
- Domains dashboard UI + API routesdomains
- self-serve custom-domain service — add/verify/activatedomains
- white-label clean routes on tenant hostsrouting
- cleanDashboardPath helper + plumb host tenant slugrouting
- host-aware headless login flowauth
- centralize host-only session cookie for per-host isolationauth
- make verified tenant hosts app-capabledomains
- unified host->tenant resolverdomains
- TenantDomain model + repositorydomains
- executable-governance rule banning silent.catch swallowslint
- onboarding edge-case lane + declined-card fix + slug-race friendly 409 + email trimbilling
- funnel anti-inflation hardening + CI email receipt lanemeasurement,ci
- brand-new invitee registration UI + hard-delete rollbackmembers
- closing gate — cancel-scheduled email receipt leg + journey-proof runbooke2e
- funnel measurement MVP — checkout beacons, daily rollup, platform funnel panelanalytics
- widen the activation funnel — invite-team + first-client stepsonboarding
- cancel-flow dignity — confirmation modal, reason capture, save offers, pause resume-datebilling
- winback — un-cancel path, cancel-scheduled panel, 7/30-day dripsbilling
- trial nurture — day-3/day-7 emails on the scheduler + live countdown bannerbilling
- dev SMTP transport to Mailpit sink + receipt-assertion railemail
- wire verification email at signup + resend route + verify bannerauth
June 20263
May 20267
- 771 — cutover.sh + rollback.sh migration scriptssyrvos
- 769 Commit 6 — marketing + dashboard UI Syrvos brandingsyrvos
- 769 Commit 5 — tests + supporting source updatessyrvos
- 769 Commit 4 — compose + workflows + systemd + scriptssyrvos
- 769 Commit 3 — Authentik blueprints + email templatessyrvos
- 768 — rename.sh automation scriptsyrvos
April 2026133
- 753 wave-D — VirtualAuthenticator context-scoped + CSRF localhost carve-out + remaining blockers documentedmfa
- 753 wave-A — test-only helpers, live-run fixes, blueprint 07 appliedmfa
- 753 — e2e infrastructure + 13 Playwright specs + adversarial battery + stress harnessmfa
- 755 — step-up auth middleware + amr surfacing + 8 endpoint migrationsauth
- 752 — SUPER_ADMIN quorum + break-glass MFA disableauth
- 752 — lost-device MFA recovery (HMAC link + bcrypt verify)auth
- 752 foundation — schema + deleteAllMfaDevices + audit actionsauth
- 754 — WebAuthn / passkey enrolment via Authentik native stageauth
- 751 — hourly MFA enrolment nudge cron + email templateauth
- 751 — MFA grace banner + platform-admin compliance viewui
- 751 — MFA enforcement infra (flag + blueprint + disable guard)auth
- 751 — cached MFA-enrolment helpers + grace utilityauth
- 751 — sync tenantRoles + MFA grace to Authentik on mutationsauth
- 751 — add User.mfaGraceStartedAt + backfill scriptschema
- RECOVERY — clear local session on reset completionauth
- RECOVERY — make the generic flow proxy drive any Authentik flowauth
- RECOVERY — revoke prior sessions on reset, add tests + docsauth
- RECOVERY — wire UI to the recovery API + MFA inputauth
- RECOVERY — rewrite recovery service + routes as headless proxyauth
- RECOVERY — declare vividai-recovery-flow blueprintauth
- RECOVERY — wire Authentik SMTP + MailHog dev sidecarinfra
- MFA-NATIVE — make backup codes usable at loginauth
- MFA-NATIVE — re-enable MFA enforcement policy for SUPER_ADMINauth
- MFA-NATIVE — add static-tokens stage to enrol flowauth
- 749 followup — MfaEnrolFlow wizard + Security tab wire-upauth
- 749 followup — MFA enrolment flow proxy routesauth
- 749 followup — blueprint + flow-slug parameterisationauth
- 750 — MFA challenge UIauth
- 750 — MFA challenge endpoint + new-device emailauth
- 750 — partial-session JWT + withMfaSatisfied middlewareauth
- 750 — scaffolding for MFA challengeauth
- 749 — MFA enrolment (/enroll + /verify + backup codes)auth
- 719 — per-email login lockout + IP credential-stuffing detectionauth
- 711.1 — MFA foundation (services, schemas, migrations)auth
- per-user notification preferences + daily digestbilling
- 619 follow-up — deletion lifecycle emails + admin viewlegal
- tenant data export + GDPR right-to-erasurelegal
- 611 gap closure — pagination + tenant brandingbilling
- client self-service billing portalbilling
- invoice reminder automationbilling
- guided onboarding checklist — trial activation widgetbilling
- upgrade prompts + contextual feature wallsbilling
- platform maintenance mode — read-only kill switchbilling
- unambiguous currency labels + multi-currency seambilling
- webhook health monitoring + alertingbilling
- trial-expired middleware + unified billing bannerbilling
- E2E Playwright tests for billing flowsbilling
- referral system integration tests + atomic conversion fixbilling
- platform admin metrics dashboard with world-class SaaS analyticsbilling
- dispute & chargeback management UIbilling
- refund workflow UI for platform + tenant-to-clientbilling
- Stripe Tax configuration for both billing layersbilling
- security hardening + webhook event log & replaybilling
- Connect email templates + revenue dashboardbilling
- tenant revenue dashboard with daily snapshotsbilling
- webhook processing + invoice customisationbilling
- activate Connect webhook processing pipelinebilling
- add invoice creation and quote-to-payment conversionbilling
- implement Phase 2 Connect & tenant-to-client billingbilling
- custom enterprise theme system — replaces next-themestheme
- payment method + invoice history sectionsui
- billing dashboard — plan overview, bolt-ons, dunning bannerui
- checkout page — 3-step signup form + slug check APIui
- Payment Element integration — shared components + hooksbilling
- pricing page with plan cards, feature table, contact salesui
- extend Catalyst library — 16 components + 2 customui
- 15 branded email templates + dev preview routebilling
- signup API with orchestrated provisioning + rollbackbilling
- financial rate limits per operationbilling
- feature gate consistency — cache coordinator + legacy cleanupbilling
- dunning system — hard cutoff + graduated 4-stagebilling
- Stage 0.9 Phase 1 — Stripe billing foundation (8 issues)billing
- elegant staggered entrance animation for launcher + avatar peekchat
- world-class popout — personal greeting, stacked avatars, typing intro, brand accentchat
- enable speech bubble popout by defaultchat
- world-class launcher button — avatar peek, AI sparkle, offline state, popoutchat
- page-level visitor presence + engagement pulse + cleanupchat
- typing bubble enhancement + delivery lifecycle testschat
- add failed message retry button + offline queuechat
- add multi-agent read receipts with 'Seen by' UIchat
- persist delivery/seen status across page refreshchat
- add WhatsApp-style read receipts and delivery statuschat
- configurable pool, error handling cleanup, metric retention, apiList extra fieldsinfra
- merge inbox features + security hardening into productionchat
- SLA timers with per-plan targets, breach detection, visual countdownchat
- true unread tracking with per-agent read statechat
- assignment system + snooze with auto-resurfacechat
- green/red ring on visitor avatar for WebSocket connection statechat
- show online presence dot on visitor avatar in all sectionschat
- WebSocket-only presence, 6-section inbox, agent availability in widgetchat
- show active blue-green slot in footerui
- merge resource limits, pruning protection, and cleanup to productioninfra
- merge resource limits, image pruning protection, and cleanupinfra
- deploy zero-downtime blue-green infrastructure to productioninfra
- merge zero-downtime blue-green deployment infrastructureinfra
- add zero-downtime blue-green deployment infrastructureinfra
- show visitor location + browser instead of 'Anonymous Visitor'ui
- 510 — migrate Discord sync from fire-and-forget to QueueServicereliability
- real-time session list updates via WebSocketsessions
- replace all polling + heartbeat with WebSocketwidget
- 490/491 — sound notifications + emoji reactionswidget
- 489 — conversation persistence with session awarenesswidget
- 488 — offline/away state with contact form fallbackwidget
- 487 — smart contextual greeting with page awarenesswidget
- 485/486 — Intercom-style composer + quick reply chipswidget
- 482/483/484 — Intercom-style bubbles, delivery status, typing indicatorwidget
- 479/480/481 — compact header, slide animation, launcher redesignwidget
- 492 backend prep — visitor WebSocket auth + API gapschat
- real-time messaging, full-bleed layout, brand blue + UX fixeschat
- right-click context menu on conversation rowsui
- contextual visitor panel — Online Now, Active, Today sectionsui
- 3-panel chat layout — main sidebar nav + conversation list + contentui
- premium polish — dark sidebar, rich conversation cards, flush layoutui
- show online member avatars in channel headerui
- redesign chat navigation — vertical Catalyst Sidebar replacing horizontal tabsui
- merge development — Sprint 5-8 + Chatwoot integration (Phase 1-4)chat
- Phase 4 — chat search, file upload infrastructure, legacy deprecation planchat
- Phase 2F + Phase 3 — platform monitoring, AI agent bot, AI suggestionschat
- Phase 2 UI — labels, canned responses, automation, campaigns, reportschatwoot
- Phase 2 API routes — labels, canned responses, automation, campaigns, reports, monitoringchatwoot
- Phase 1 — expand client with labels, automation, campaigns, reports, bots, teams APIschatwoot
- wire Chatwoot sync — Option C (conversations + contacts, not messages)chat
- Sprint 6 complete — mobile widget, quote cards, CSAT surveyschat
- Sprint 5 + 6 — real-time chat engine, team channels, client portal, resolution flowchat
- deploy Sprints 0-5 — real-time chat platform to productionrealtime
- merge production infrastructure prepinfra
- production infrastructure — WebSocket server, Chatwoot wiring, tunnelinfra
- merge Sprints 0-5 — Chatwoot, Socket.IO, chat UI, channelsrealtime
March 202646
- Sprint 5 — internal channels, resolution flow, assignment, notificationschat
- dev-only test session endpoint for Playwright authe2e
- Sprint 4 Phase 3+4 — visitor widget, onboarding, settingschat-ui
- Sprint 4 Phase 1+2 — message design system + agent inboxchat-ui
- Sprint 3 — Chatwoot headless integration (9 of 10 issues)chatwoot
- Sprint 2 — presence tracking + instant notificationsrealtime
- Sprint 0+1 — Chatwoot infrastructure + Socket.IO foundationrealtime
- merge Phase 2 deployment overhaul to productioninfra
- Phase 2 — rollback, parity, no bare-metal deps, migration gateinfra
- merge CI/CD overhaul + auth fixes to productioninfra
- separate migrate image from app imageinfra
- switch to Next.js standalone outputinfra
- switch to Docker buildx with registry-backed cacheinfra
- single-page user settings with Catalyst UIsettings
- session audit trail, fix critical audit gaps, retention tiersaudit
- force password reset, tenant policies, trusted devicessessions
- login history, suspicious login alerts, inactivity timeoutsessions
- tenant admin kick with role hierarchy + sign out everywheresessions
- SUPER_ADMIN session management dashboardsessions
- merge Sprint 1 — session lifecycle, blacklist, max limits, logout reasonssessions
- forced logout reasons on login page + session monitorsessions
- enforce max concurrent sessions per tenant plansessions
- add session lifecycle tracking with soft-delete revocationsessions
- add graceful shutdown, circuit breaker, and OIDC discovery cacheinfra
- merge Prisma 7.5.0 upgrade to productionproduction
- upgrade to Prisma 7.5.0 with driver adapter and configinfra
- remove legacy NextAuth models, migrate email verification to signed tokensauth
- custom VividAI-branded password reset email templateauth
- MFA enrollment UI + fix password change endpointauth
- enterprise login UX + robust logout + forgot passwordauth
- enterprise login UX — session resume, forgot password, middlewareauth
- world-class login UX — email card, social buttons, password toggleauth
- enterprise-grade forgot/reset password pagesauth
- enterprise security hardening — blueprints, bootstrap, DB isolationauth
- add setup-authentik-admin target for user password setupops
- PgBouncer connection pooling (Sprint 3)stage0.7
- distributed rate limiting + per-tenant quotas (Sprint 2)stage0.7
- Redis + BullMQ job queue infrastructure (Sprint 1)stage0.7
- enterprise hardening — observability, audit trail, security, cachingstage0.6
- enable SUPER_ADMIN bypass on contacts, quotes, and members routesstage0.5