Files
jobtrackingapp/docs/audits/2026-09-07/product-operations.md
T
cesnimda 0954876295 fix(security): enforce audited account, network and resource boundaries
Implement B01-B06 with regression coverage and retained validation evidence. Preserve the production ingress preflight; F04 runtime verification remains open.
2026-09-09 18:21:47 +02:00

28 KiB
Raw Blame History

Product coherence, operational readiness and reusable quality gates

This is a source/observational assessment plus a proposed acceptance programme. Verification states what actually ran. No live uptime, provider price, current vulnerability catalogue or legal requirement was verified externally.

Critical paths and time to value

Priority order: sign in/recover account → view/find tracked jobs → save/update application → edit Career Profile/CV → download/share an approved document → communication/follow-up → optional AI/discovery/analytics. Optional AI/email outages should not block the first four paths.

The existing manual add-job browser test clicks Add Job, Enter details manually, fills company/title, Continue, skips three optional steps, then Create job. That is a measurable interaction sequence, not a timed usability study. Offer a short “Save job” path after required details and let the user enrich it later; keep the reviewed import path. Never require AI or a complete CV before tracking a first job.

Registration depends on configuration, verification and Turnstile. Default-disabled registration is an operational rollout choice, not inherently a bug. The landing CTA must explain when signup is unavailable. First-run empty states should offer three small actions: save a job, add career details, create a CV. Explain that Career Profile supplies facts and a CV is a chosen presentation for an application. Dismissible hints beat a mandatory tutorial. Verify registration → email verification → first saved job against a local mailbox sink; this whole journey was not executed in the audit.

Navigation and domain language

Current routes are visible in src/App.tsx: dashboard, jobs/detail, discovery, reminders, operations, Kanban, companies, offers, correspondence, profile, career, CV builder, trash, settings and admin. Deep job routes and career subroutes are valuable; legacy application/correspondence routes redirect. The existing browser workspace deep-link/back/dirty-navigation test passes. Not every tab/filter/modal was exercised.

Proposed information architecture, retaining working routes:

Dashboard: next useful actions
Jobs: list / board / job workspace / reminders
Career: facts / CVs / import review / evidence
Communication: correspondence and send/review status
Discovery: optional acquisition workflow
Settings: identity, connected providers, privacy, notifications, plan, data
Admin: account/security/health diagnostics, hidden from ordinary users

Offer comparison, companies and operation status need not compete equally with daily job actions in primary navigation. Keep direct links for frequent users; make secondary entries contextual. Do not remove features solely because usage analytics are absent. Before deleting an apparent duplicate, inspect dynamic imports, stored template IDs, compatibility routes, migrations and deployment scripts.

Concept English / recommended Bokmål Meaning / consistency rule
Job / opportunity Job / Stilling Employer opportunity; not proof of an application
Tracked application Job application / Jobbsøknad User's pursuit of a job, with status/history
Application document Cover letter / Søknadsbrev Written letter, not the tracked workflow itself
Career Profile Career Profile / Karriereprofil Reusable factual source; account Profile is identity/settings
CV CV / CV Saved presentation/settings; define draft versus submitted snapshot
Interview Interview / Intervju Scheduled or logged interview; not interview-preparation notes
Reminder Reminder / Påminnelse A due action; distinguish notification channel from task
Correspondence Correspondence or Job email / Korrespondanse or Jobb-e-post Choose one label per navigation context, explain manual versus connected mail
Offer Offer / Tilbud Employer outcome; no implicit monetary currency conversion
Rejected Rejected / Avslått Application outcome; avoid confusing with archive
Withdrawn Withdrawn / Trukket User ended pursuit
Archive / trash / delete Archive / Arkiv; Trash / Papirkurv; Delete / Slett Distinguish inactive, recoverable deletion and permanent erasure
Save / share / download Save / Lagre; Share / Del; Download / Last ned Explicit object on destructive/ambiguous actions; sentence case

Current terminology is partly established in docs/01-glossary.md, translations.ts and JobPipeline. Reconcile with that vocabulary rather than creating a competing glossary. The observed Bokmål builder still shows English template descriptions and “Dashboard”; button capitalization includes “Last Ned PDF” and “AI-Verktøy”. Treat these as editorial inconsistencies to review with a native Bokmål speaker. Keep document language distinct from interface language; the English synthetic CV inside a Norwegian UI is not itself a translation failure.

Visual, accessibility and professional polish review

Observed: the current static builder was inspected at 375 and 1440 pixels in both themes and both actual language states (en / nb-NO). The surrounding page had zero measured horizontal overflow. Desktop has a useful editor/preview split; mobile provides an edit/preview alternative. Shared navigation, toolbar and template thumbnails have consistent placement. These are strengths worth preserving.

Confirmed problems: F14's success/ATS chips use green text with measured 3.29:1 contrast on white in one light-theme sample; desktop scrolling regions were not keyboard focusable. All eight inspected states produced at least one serious contrast finding. F15's combined iframe sandbox flags produce repeated browser warnings. A short synthetic CV also produced a preview overflow warning; investigate the measurement/rendered document width rather than hiding the warning. External Google Fonts were blocked by the harness and fallback rendering was inspected; compare with approved self-hosted-font rendering before attributing every document-layout discrepancy to a product defect.

The template gallery uses small badges and substantial descriptive copy. Keep selection predictable, use stable action positions, and ensure Pro-disabled cards have an explanatory accessible name rather than opacity alone. Do not conflate “ATS-friendly” with a guarantee for every recruiter system. The saved status, language and theme should not move focus unexpectedly. No native screen-reader or actual browser-zoom certification was performed.

Canonical component choices should build on MUI and existing shared theme.ts, layout/AppShell.tsx, layout surfaces, feedback context/dialogs and spacing tokens. GradientButton.tsx is a special treatment to inventory, not justification for another general button package. Keep normal controls on MUI, standardize PageHeader/empty/error/loading patterns at their actual common callers, and migrate one page before broad rollout. Add a lightweight component state showcase only if repeated visual review warrants it; Storybook is not a launch dependency.

Component / state Review and target
Button/input/select Clear accessible names; focus visible; Enter/paste/autofill work; busy does not imply permission; use native/MUI semantics
Dialog/menu/portal One focus trap; Escape semantics; focus return; no nested modal stack for routine edits; theme inherited
Cards/badges/status Text conveys status independent of color; keyboard equivalents for clickable cards; translated long names wrap
Toast/error/empty/loading Persistent actionable error for failed saves; important failures not transient toast only; live region announces state changes, not every token
Tables/Kanban/charts Paged data; allow keyboard change-status/reorder; meaningful zero-data explanation; charts have textual values/summary
CV editor/preview Dirty/saving/saved/failed states; recoverable conflict; accessible scrolling; output language metadata and reading order tested separately
Responsive layout 320 effective width, 375/768/1440, 200%/400% actual zoom, landscape, on-screen keyboard, safe area, text spacing; no hidden-only-on-hover action
Interaction details Real links support middle/right click; copy feedback only after clipboard success; focus/title after SPA navigation; Back restores useful list/filter/scroll state

Copy action in CvBuilderEditor.tsx shows a success toast without awaiting clipboard completion; make success contingent on the browser result. History/preview/unsaved dialogs deserve keyboard and screen-reader runs, not only snapshots. Shared page title/focus/scroll behavior was not proven by source search; add explicit route-level checks before claiming polish.

Professional polish checklist (open until visually verified): consistent loading indicators and skeleton ARIA; favicon/page titles and missing assets; margins/spacing tokens; both themes; translated empty states and errors; tooltips for icon-only controls; stable actions; no layout shift; no unexplained console/network/backend warnings; no broken route/404 fallback hiding missing assets; sensible scrollbars and z-index layers; no accidental nested modals; copyable text; no forced orientation; reduced motion/forced colors; clear failed/disabled/long-content states. A screenshot is not proof of every item.

Generated documents require a separate gate: selectable/searchable text, intended page size/margins, logical reading order, headings/lists, document language, meaningful links, metadata, contrast and absence of hidden server paths. Existing PDF browser tests prove portions of rendering/searchability, not tagged PDF/PDF-UA or screen-reader reading order. DOCX extraction support does not imply a complete accessible DOCX generation/export feature. Verify current offered export formats before promising them. Browser Print must hide application navigation and preserve page boundaries independently of PDF export.

Feature maintenance and simplification

Scores are judgments (high/medium/low), not usage telemetry.

Subsystem User value / maintenance / risk Decision
Job tracking and routed workspace High / medium / high correctness Keep and prioritize reliable save/search/history
Career Profile and CV builder High / high / high privacy/data loss Keep; strengthen revision/time/preview contracts before more customization
Mail connectors/provider abstraction Potentially high / high / high privacy/external effects Retain working seams; measure adoption before adding providers or full mailbox features
AI assistant/work queues Useful / high / high privacy/cost Keep human review, gates and current queue; narrow workloads/evaluate models
Discovery/import/translation Medium / high / high SSRF/source churn Manual fallback, limits and per-source tests; no bespoke scraping platform
Company CRM/offer comparison/analytics Medium / medium / medium Contextual access; measure adoption before more domain entities
Public CV Medium / medium / high publication/render abuse Explicit review/revocation and bounded rendering; optional expiry when needed
Custom auth/billing/backup orchestration Essential / high / high Keep Identity and Stripe primitives; repair small missing protocols; compare managed options only with requirements and costs

Remain a modular monolith. A parser service boundary is justified by unsafe documents and different runtime/resource needs. A separate renderer may be justified by measurable resource/privilege isolation; explain its queue, artifact-storage, failure and deployment costs first. No reason yet for Redis, Elasticsearch, Kubernetes, a PWA/service worker or independently deployed domain microservices. An offline-capable PWA would add sensitive cache/upgrade complexity before F01/F09 are solved.

Existing configuration switches cover workers, registration, deletion, provider routing and paid authorization. Maintain a flag register with owner, purpose, default, rollback behavior, test of both states and review/removal date. Server-side entitlement policy remains authoritative; a feature flag does not grant access. Percentage rollout is unnecessary for a small closed cohort; explicit internal/beta allowlisting and kill switches suffice.

Performance, capacity and cost model

Use measured traffic/service time before setting SLO commitments. The audit's localhost navigation-to-main timings exclude real WAN latency, complete data/render readiness and production contention. They are not LCP or production p95 evidence. Existing owner/page limits help, but list-of-all board/analytics/history/exports and per-request Chromium are the likely first bottlenecks.

Proposed initial internal targets for a declared synthetic dataset and reference device/network: core read/save API p95 <500ms and p99 <2s; authentication p95 <2s excluding provider challenge; dashboard ready <3s; LCP <2.5s, INP <200ms, CLS <0.1; search p95 <500ms; bounded PDF p95 <10s for an agreed 2-page CV; queue acceptance <1s; an immediate truthful queued/progress state for slow AI. These are acceptance hypotheses, not measured guarantees. Benchmark on the actual host before promising them. Record p50/p95/p99 and error rate, and budget the initially loaded JS based on measured gzip/Brotli bytes; fail unexpected >10% route-bundle growth unless justified.

Scale What must be measured first Likely trigger / next step
10 users Real daily workflow failures, restore success, single PDF RSS Fix correctness/UX before scaling
100 Concurrent PDF/AI requests, monthly storage/mail growth, worst-owner queries Bounded admission and alerting; single host may suffice but capacity unproven
1,000 DB p95/query plans, pool use, queue age, file backup duration Tune projections/indexes, batch/stream exports, independent worker capacity if measured
10,000 Real active concurrency, arrival/service rates, DB recovery/availability DB-backed quota reservation before replicas; separate durable storage/compute as needed
100,000 Workload partitioning, retained history, operational staffing/cost Reassess architecture with production telemetry; no exact capacity claim here

Suggested scaling triggers: sustained DB CPU >70% plus missed latency targets; pool utilization >80% with connection waits; queue wait exceeds user-facing SLO for 15 minutes; disk forecast under 30 days; backup/restore exceeds target; repeated PDF admission saturation. Tune alert thresholds against baseline and avoid paging for every transient failure. First inspect query plans on the actual SQLite/MariaDB provider; SQL text or InMemory tests do not establish production query cost.

Cost model variables (record actual invoices/provider metering):

monthly cost = fixed host + database + monitoring + stored_GB × storage_rate + backup_GB × backup_rate + egress_GB × egress_rate + email_count × email_rate + external_AI_input_tokens × input_rate + external_AI_output_tokens × output_rate + operator_time × internal_rate.

Local AI substitutes memory/compute/energy and operator cost for a provider token bill; it is not free. PDF consumes CPU/process time and retains output bytes. For workload w, estimate cost_per_success = (successful + retried + failed_attempt_cost) / successful_results. Separate reservations, actual estimates, failures and external fallback. Character/4 estimates are not provider billing truth, especially for Bokmål and unusual text.

Storage planning example, assumption not telemetry: 1,000 users × 20 documents × 1MB = 20GB original bytes before generated files, versions, package copies and backups. Thirty full daily copies alone could multiply that substantially. Measure dedup/compression/retention rather than promising a flat quota covers all copies. Database growth includes JobEvents, CV/Profile/letter versions, mail, AI operations/usage and deletion/security records. Set retention before partition/archive infrastructure.

Useful privacy-preserving telemetry: workflow starts/success/failure, safe task/version/category, elapsed time, approximate size bucket, queue wait, retry count, upload bytes, quota rejection, empty-state completion. Exclude CV/letter/mail/note text, full URLs/query strings, tokens and unredacted screenshots. Keep support diagnostics usable without viewing career content. Monitor externally, but never make application health depend on the telemetry collector being online.

Operational ownership and alerts

For a solo project, the repository owner can initially own all roles; explicitly name the backup contact before paid launch. Record actual people/contact routes outside public source. Proposed availability goal is 99.5% monthly for core reads/saves after baseline measurement; adopt a stricter promise only with operational evidence. Track separate generation/delivery success SLOs so optional AI errors do not disguise or inflate core reliability.

Signal Severity / initial rule Owner / action
External HTTPS/readiness P1 after 3 failed probes over ~3min; immediate if broad confirmed outage Operator: proxy/DB runbook
Core save errors P1 sustained >1% or any confirmed data loss App owner: freeze risky rollout, inspect safe trace IDs
Worker stalled P1 if accepted jobs miss deadline; otherwise P2 sustained lag App operator: F20 lease/backoff runbook
Backup age/failure/off-site failure P1 beyond RPO; warning at 26h for daily schedule Recovery owner: preserve prior set, rerun safely, verify restore
Disk/inode forecast Warn 80%, urgent 90% or <7 days forecast; adjust for volumes Operator: stop expensive admission, inspect approved cleanup
PDF/AI cost or saturation P2 sustained queue/cost anomaly; P1 if affecting core API App owner: kill switch, admission/abuse investigation
Billing reconciliation/deletion P1 unresolved paid-state/cancellation divergence Billing owner: sandbox-validated reconciliation, user follow-up
Privilege changes / unusual export Security review; urgent on unauthorized activity Security owner: account/session containment and evidence
TLS/DNS Warn certificate <21 days, urgent <7; sustained DNS mismatch Domain/proxy owner: renewal/DNS runbook

Deduplicate alerts by incident, include last good release, safe reference/metric, impact, runbook and recovery notification. Ensure a missing scheduler/monitor itself produces a dead-man alert outside the app host. Check backups becoming suspiciously small/large and off-site encryption failure, not only exit codes.

Incident response and technical runbooks

Common sequence: identify impact and start an incident record; preserve UTC timeline, release IDs and sanitized logs; contain further writes/exposure when justified; keep a known recovery point; repair/rotate; verify user journeys and integrity; communicate factually through the approved channel; document cause and regression prevention. Do not email users automatically from this audit. Determine legal notification obligations with the responsible qualified advisor.

Incident Checks and diagnosis Safe recovery / escalation Verification
502/site unreachable External DNS/TLS/HTTP, frontend route, backend /health and /ready, recent deploy Correct proxy/service/network or roll back only schema-compatible image; do not restart DB blindly External auth/config + synthetic core journey and readiness
DB unavailable Connection reachability/pool/server health, provider identity, disk, credential changes; no credential dumps Restore connectivity; reduce admission; recover worker leases; escalate to DB owner if integrity uncertain Read/save synthetic record, FK/integrity checks, queued work progresses
Bad migration / partial deploy Migration history and exact failing statement/release; schema compatibility Stop further deploys; preserve checkpoint; forward fix or restore isolated verified set; explicit approval before production restore Old/new client contracts, data counts/hashes, app-ready with intended version
Disk full Per-volume/inode usage, DB/WAL, logs, exports, backups Disable PDF/AI/upload admission; rotate/prune only documented disposable outputs; add capacity; never delete active DB/WAL or sole backup Free headroom, DB integrity, safe new upload/export and backup
High CPU/RSS/OOM Active Chromium/model workers, queue size, request classes, container limits Apply admission/kill switch, terminate only identified stuck disposable worker, restore service; inspect missing results Resource baseline, queue recovery, no partial artifacts reported ready
AI/model outage Sidecar health vs model availability, task/model route, consent and timeout category Keep manual product usable; disable failing route; switch only to evaluated permitted model; queue/retry bounded Synthetic factuality/format/locale checks, no unauthorized external fallback
Email unavailable SMTP/provider response category, queue/attempt state, token scope, sender config Reconnect/rotate as needed; retry known unsent only, reconcile uncertain sends; use local sink during diagnosis Delivery accepted/received in safe mailbox; pending verification/reminders resolved
Backup failed/corrupt Last independently verified set, manifest/hash, backup/copy logs, disk Preserve prior verified set; recreate consistent snapshot; restore isolated copy first DB+files+keys+tombstones usable within RPO/RTO; record actual minutes/bytes
TLS expired / DNS wrong Cert chain/SAN/expiry, renewal job, authoritative vs recursive DNS Renew correct certificate/fix intended record; escalate registrar/proxy access to owner External HTTPS from independent network and renewal alert
Leaked JWT signing secret Scope/token issuance window, affected sessions, safe auth logs Rotate signer, revoke sessions/trusted devices as required, redeploy configured key; plan forced sign-in Old tokens rejected, new sign-in works, no secret in logs
DB/SMTP/AI/OAuth/Stripe key leak Identify specific credential, usage anomalies and accessible data Revoke/replace at issuer and app, test dependent workflows, reconcile mail/billing side effects; rotate webhook secret with provider overlap policy Old credential rejected, correct service restored, anomaly monitoring quiet
Data Protection key exposure/loss Which encrypted tokens/TOTP depend on ring; backup custody Treat exposed reversible values as compromised; revoke/relink providers or re-encrypt with controlled key transition; never casually delete ring Token decryptability/re-enrollment and restored backups tested
Admin compromise / cross-user leak Actor/session/role timeline, affected owners/objects, access logs Revoke compromised sessions, disable account/route as needed, preserve evidence, patch authorization with failing test Complete cross-owner suite; scoped security review; appropriate user notice process
Malicious dependency / host compromise Release provenance/SBOM, runner/host boundaries, secret exposure Isolate host/runner; rebuild from clean trusted artifacts; rotate reachable credentials; do not trust in-place cleanup alone Clean deployment, restored validated data, external/user smoke, evidence retained

Operator inspection can use read-only service listings and /health//ready, sanitized logs and backup manifests. Do not paste docker compose config, full environment, connection strings or tokens into an incident ticket. A production restart, restore, destructive cleanup or credential change is a separate authorized action. The audit did not execute these runbooks.

Release gates and production-readiness checklist

Choose measurable targets with the owner. Proposed initial recovery target: RPO <=24h and RTO <=4h for the current daily-backup service; paying-user expectations may require a shorter RPO. Record restored snapshot timestamp, last committed synthetic marker, restoration duration and file/key verification. Never claim this target is achieved until a drill passes.

  • No known P0; all applicable P1 findings closed with reproduction and regression evidence.
  • Backend build/tests, frontend tests, standalone strict types, lint and static build pass using declared locked toolchains.
  • Real relational tests cover owner predicates, revision conflicts, byte/usage reservation, idempotency and deletion; MariaDB behavior is tested when deploying MariaDB.
  • Browser critical paths pass on the production export; both languages/themes and editor failure states included; no unexplained console/network errors.
  • Zero serious/critical axe findings on core routes; manual keyboard/screen-reader/zoom/text-spacing checks recorded, with documented exceptions if unavoidable.
  • Bounded PDF/parser/AI/import/export load proves process/memory/request ceilings and recoverable errors.
  • Current dependency/advisory/container/secret scans and SBOM retained; each exception has owner, mitigation and expiry; supported toolchain verified.
  • Migration rehearsed on realistic synthetic shape; old/new compatibility window and rollback boundary stated; verified pre-deploy recovery point retained.
  • Actual DB+files+keys+tombstones restore meets chosen RPO/RTO; off-site copy and alert routing tested.
  • Host ingress/firewall, TLS renewal, trusted proxies, least privilege and production secrets validated; development fallbacks cannot activate silently.
  • Stripe sandbox proves duplicate/order/concurrency/deletion; mail sandbox proves known-unsent vs uncertain delivery; OAuth uses intended tenant/scopes.
  • Privacy/retention/export/deletion/support wording matches implemented behavior; external/legal decisions resolved by responsible owner.
  • Release version observable, post-deploy external smoke succeeds and a responsible operator can respond to the alerts.

Definition of Done and practical PR checklists

Every feature: explicit acceptance behavior; correct owner/permission/validation; appropriate regression test; recoverable loading/error/empty states; no private log content; useful documentation only where behavior changes. A cosmetic edit does not need the same gates as billing or a migration.

PR checklist

  • Problem, resulting behavior and smallest affected boundary explained.
  • Relevant tests/checks pass; omitted checks and actual risks stated.
  • Owner/security/duplicate/failure behavior considered for changed paths.
  • UI/locales/themes/accessibility checked when changed.
  • Data/migration/rollout/rollback and behavior docs updated when relevant.

Endpoint/security checklist

  • Identity and owner come from trusted server context; test another owner's identifiers on each verb.
  • DTOs reject unauthorized fields/oversized inputs; enum/null/numeric boundaries and query allowlists tested.
  • Cookie mutations have CSRF protection; auth/admin/export changes require appropriate recent auth and safe audit.
  • Uploads have byte/type/expansion/storage limits; URLs validate connected destinations; downloads have safe headers/names.
  • AI uses bounded consent-approved data, validated output and human review; actions cannot come from untrusted prompt instructions.
  • Side effects have operation identity, correct quota admission and safe failure/retry semantics.

UI checklist

  • Shared spacing/type/color/control conventions; every action has a meaningful name.
  • Keyboard, focus return, contrast, touch and no hover-only requirements.
  • 375/768/1440 and applicable reflow/zoom; both EN/NB and light/dark.
  • Loading/empty/error/disabled/success/long-text states; save failures retain edits.
  • Back/deep links/clipboard/scroll/title behavior and console/network cleanliness.

Database-change checklist

  • One schema owner; migration and model snapshot agree; both supported providers handled.
  • Correct FK/unique/check/index constraints; owner scope and query plan tested.
  • Existing rows preserved; deterministic backfill; locking/disk/time measured on realistic shape.
  • Expand/contract compatibility and irreversibility documented; no blind time-zone conversion.
  • Verified backup, rehearsal, rollback/forward-repair plan and integrity check.

Deployment checklist

  • Intended commit/artifact/config and environment verified; no secrets printed.
  • Required quality gates, backup/recovery checkpoint and migration review complete.
  • Previous compatible image/recovery path available; feature kill switches understood.
  • Deploy, then public TLS/health/readiness/auth/config + synthetic core smoke; correlate version.
  • Check safe logs/metrics and queue recovery; stop/repair if release gate fails.

These are repository recommendations, not evidence that branch protection, deployment permissions, legal policies or production operations currently meet them.