# CMS error center and dependency maintenance Open **HK > DevOps > CMS error center** (`/admin/devops/cms-errors`). The previous `/admin/devops/errors` page still displays emulator errors. ## Access and workflow - Read: existing `admin.devops.view` permission, checked on the server. - Mark resolved: `admin.devops.edit`, checked again in the server action; recorded in the staff audit log. - Search by event reference, Next.js digest, route, message or deployment version. - Expand a group for its latest stack, sanitized context and occurrence references. - Marking a group resolved does not delete evidence. A later occurrence reopens it. - Refresh is manual so inspecting an expanded error is not interrupted by polling. ## What is collected Pino `logger.error`, `logServerError`, unexpected action errors, Next.js request failures, React error boundaries, uncaught browser errors and unhandled browser promise rejections. API wrapper failures return an `errorId`; Next.js boundary errors can be correlated by their digest. Release identifiers come from the build. Browser reports retain their own client release separately from the receiving server. Reports are stored in `storage/cms-errors`, using the existing persistent storage mount. No third-party service, token or new database table is required. Storage does not depend on database availability; viewing the HK and its permission checks still require authentication/database availability. Server console logs remain the fallback when the CMS itself cannot serve requests. Retention: seven days; approximately 10 MB/day, 100 queued server writes, and the latest 1,000 events in the viewer. Limits are per CMS process/storage volume; this is intended for the existing single-instance deployment. Daily expiry runs on the next write. The browser endpoint is same-origin, body-size bounded and rate-limited. Browser reports are untrusted observations and cannot grant access. Known credential patterns and URL parameters are redacted; arbitrary object metadata and request bodies are excluded. Avoid putting personal data in error messages: this is pattern-based redaction, not a universal data-loss filter. This does not collect historical console logs, process crashes before framework startup, nginx failures, all `console.error` calls, or every handled business validation error. A browser stack may point at minified chunks; private source-map symbolication and distributed traces are not part of this local viewer. A recorded stack is diagnostic evidence, not an automatic root-cause determination. ## Dependencies `pnpm install --frozen-lockfile`, `pnpm deps:audit`, `pnpm typecheck`, `pnpm test`, `pnpm biome:lint`, and `pnpm build` are the verification sequence. `pnpm analyze --output` writes a Next.js bundle analysis (not an application build). TinyMCE 8.9 is pinned in pnpm. `pnpm assets:editor` copies its runtime files and license notices to ignored `public/vendor/tinymce`; dev/build run this first. The Docker build includes the generated assets. The editor retains its existing HTML fields and toolbar; validate saved content and preview when upgrading it. Lenis has been removed; the public site now uses native scrolling. Other used runtime libraries remain. Vitest and its coverage provider are upgraded together; `clearMocks: false` preserves initialization-time permission contract assertions. Dependency updates are manual: Renovate has been removed, so there is no bot opening upgrade pull requests. Node/pnpm upgrades remain coordinated with Docker and the runner toolchain. Obsolete global overrides were removed; a scoped esbuild override remains because Drizzle Kit's loader still resolves a vulnerable legacy development-server build. The two deprecated esbuild-kit packages remain upstream dependencies of Drizzle Kit; replacing the ORM is not warranted for this tooling issue. ## HK request performance Open **DevOps → Request performance** (`/admin/devops/performance`). Access requires `DEVOPS_VIEW`; collection only retains requests that passed authentication and permission checks through `withAdmin`. The view shows recent requests, their server duration, completed database query count/time/errors, monitored curl download count/time/errors, HTTP status and the existing operation ID. Search by route or operation ID, sort by duration or recency, and filter requests taking at least one second. ### Measurement boundaries - Duration ends when the handler returns its response. Streaming completion, background jobs, browser rendering and public pages are not measured. - Database spans wrap the shared mysql2 promise pool and transaction connection query/execute calls. Query parameters and SQL are never collected. Explicit transaction connection acquisition and transaction control methods are not separate spans. - External spans currently cover `curlFetchText` and `curlDownload`; ordinary fetch calls and other integrations are not covered. - Concurrent dependency durations can exceed wall-clock request duration. Do not subtract the sums to infer application CPU time. - Dynamic route values and query strings are removed. No request bodies, headers, usernames, download URLs or SQL text are stored. ### Storage and operation No new environment variables or packages are required. Redis stores at most 200 recent samples under `cms:performance:v1`, with one-hour retention. Writes are best effort, restricted to an already-ready connection and at most four pending batches; the request never waits for persistence. The view reads at most 200 entries and falls back after one second if shared storage is unavailable. An in-process buffer preserves up to 200 samples during outages. The page explicitly labels this local mode; it is per instance and disappears on restart. Filtering applies to the retained samples, not complete traffic history. Under load or during storage outages, some shared samples may be omitted.