Rollout, Feature Flag & Observability
Business view
This feature does not turn on for every clinic the moment it merges. It's gated behind a beta feature flag, so it can be enabled for a small set of pilot clinics first and expanded deliberately — and it comes with a monitoring dashboard purpose-built for this rollout, so problems in the pilot (failed uploads, scanners that can't authenticate, requests that stall) surface as alerts rather than as support tickets.
Technical view
The feature flag
FEATURE_XRAY_ACQUISITION (packages/server/src/utils/xrayFeatureFlag.js:3) uses Dentolize's existing rule-based feature-flag engine (the same one gating ZATCA integration, e-invoicing, and other beta features). Its seed definition (packages/server/src/generateServerData/seedFeatureFlags.js:8-17):
key: 'FEATURE_XRAY_ACQUISITION'
positiveRules: "Rule('isBeta', 'EQUALS', true)"
active: true
In plain terms: the flag record is switched on (active: true), but its targeting rule restricts it to companies with isBeta === true — i.e. opted-in pilot tenants, not a general release. The active switch doubles as an emergency kill switch independent of that targeting rule: flipping it off disables the feature for every company immediately, beta or not, without touching application code.
isXrayAcquisitionEnabled({ prisma, companyId }) is checked in three places server-side: the requestXrayAcquisition mutation, the desktop connect endpoint, and every subsequent lease validation (desktopPrincipal.js) — so a company that loses beta access mid-session has its desktop app's lease validation start failing on the very next call, not just on new connection attempts. Client-side, XrayUploadBox.js separately checks the same flag via useFeatureFlag('FEATURE_XRAY_ACQUISITION') before showing the Acquire button at all, so a non-beta company's UI doesn't even offer the entry point.
Feature-flag cache propagation was also tightened in this same change: the Redis cache key for feature flags was bumped to a new version (rules:featureFlags:v2) and its TTL shortened from two months to five minutes specifically for this rollout (packages/server/src/utils/redisCacheForPrisma.js) — meaning a flag change (e.g. adding a company to the beta) now takes effect within minutes rather than being effectively frozen until the old cache entry aged out.
Monitoring
A dedicated Prometheus alert group, xray-acquisition (monitoring/prometheus/xray-alerts.yml, wired into the existing monitoring stack via monitoring/docker-compose.monitoring.yml and monitoring/prometheus/prometheus.yml.example), watches eight conditions, all fed by counters/histograms/gauges added in packages/server/src/utils/metrics/businessMetrics.js and metricsSDK.js:
| Alert | Condition | Severity |
|---|---|---|
XrayAuthorizationFailuresHigh | >20 auth failures / 5 min | warning |
XrayInvalidTransitionsHigh | >5 rejected state transitions / 10 min | warning |
XrayLeaseCollisionsHigh | >5 room-lease collisions / 10 min | warning |
XrayUploadFailuresHigh | >5 upload-intent/completion failures / 10 min | critical |
XrayTimeoutsHigh | >5 acquisitions force-timed-out / 15 min | warning |
XrayWaitingUploadsStale | any request stuck in WAITING_UPLOAD for 15+ min | warning |
XrayOrphanCleanupFailing | any S3 orphan-cleanup failure / 30 min | critical |
XrayCompletionLatencyHigh | p95 completion latency > 5 min / 15 min | warning |
These map directly to the lifecycle events documented on the other pages of this walkthrough: authorization failures are recorded by desktopPrincipal.js and helpers.js on every rejected lease/key check, lease collisions by the connect endpoint's 409 ROOM_OCCUPIED path, invalid transitions by xrayAcquisitionTransitions.js, timeouts and stale-upload counts by the cron job, and orphan-cleanup failures by the S3 object-deletion step in that same cron job — see Real-Time Status, Presence & Timeouts for the cron itself.
Go-live checklist
The existing operations runbook (scripts/GO_LIVE_RUNBOOK.md) gained a section specific to this feature. Before enabling it for any tenant:
ACCESS_KEY_HMAC_SECRETmust be set to at least 32 random bytes (the server refuses to boot otherwise — see Access Keys & Security Model).TRUST_PROXY_HOPSmust be set to the exact number of trusted reverse-proxy hops in front of the API (left unset only for direct, no-proxy access) — the server now deliberately ignores forwarded client IPs otherwise, which would make the desktop-endpoint IP rate limiters trivial to bypass if misconfigured the other way.MINIMUM_XRAY_DESKTOP_VERSIONshould be set before enabling any tenant, so an outdated desktop app is rejected atconnect(426 CLIENT_UPDATE_REQUIRED) rather than allowed to talk to a server it doesn't fully match.- Desktop-acquired X-rays reuse the same S3 bucket and presigned-POST flow as ordinary manual uploads — the runbook explicitly calls out verifying that bucket configuration before flipping the flag for a tenant, since a misconfigured bucket would fail silently at the upload-verification step described in X-Ray Acquisition Workflow.
A separate, manual workflow_dispatch GitHub Action (.github/workflows/sandbox-ops.yml) can reseed, reset, redeploy, or destroy per-branch PR sandbox environments — this is general sandbox tooling that happens to ship in this PR, not X-ray-specific, and isn't something Support or Sales need to know about.