Files
carmanagement/docs/ops/RentalDriveGo_Production_Readiness_Plan.md
T
root 8fc88ffc14
Build & Push / Pipeline Tests (push) Failing after 59s
Build & Push / Build & Push Docker Image (push) Has been skipped
Test / Type Check (all packages) (push) Failing after 51s
Test / API Unit Tests (push) Has been skipped
Test / Homepage Unit Tests (push) Has been skipped
Test / Carplace Unit Tests (push) Has been skipped
Test / Admin Unit Tests (push) Has been skipped
Test / Dashboard Unit Tests (push) Has been skipped
Test / API Integration Tests (push) Has been skipped
fix production issues
2026-08-12 16:48:41 -04:00

26 KiB
Raw Blame History

RentalDriveGo — Plan to Reach Production Ready

Purpose of this document: This is the execution plan to take the current codebase to production ready. It is not a feature roadmap and not an architecture rewrite. Success is measured only by the definition in §1 and the checkpoint in §13.

Status: Phases 04 application code / runbooks complete (12 Aug 2026). Remaining work is ops evidence (CI smoke, restore/alert drills, pen-test, soak) — not missing features. Phase 4 correctly does not extract microservices (ADR-003).
Code root: D:\1\management
Last re-verified against source: 12 Aug 2026 (Phases 04 code pass)
Inputs: Live repo state + Technical Architecture & Investor Due Diligence (v1.0, 9 Aug 2026) + prior hardening reports under docs/
Architecture decision: Keep the modular monolith. Do not split services until Phase 3 load/ownership evidence justifies it.

How this plan works

flowchart TD
  now[Current_beta_pre_scale] --> sec[Close_security_P0]
  sec --> p0[Finish_Phase0_evidence]
  p0 --> p1[Phase1_replica_safe]
  p1 --> p2[Phase2_operate_recover]
  p2 --> p3[Phase3_prove_under_load]
  p3 --> prod[Production_ready_gate]
Stage Outcome
Security P0 + Phase 0 close Safe enough to run a controlled environment; envelope proven
Phase 1 Safe to run two API replicas + worker
Phase 2 Operable: observe, backup/restore, commercial/privacy baselines
Phase 3 + §13 checklist Production ready — claims allowed only after exit evidence

1. Definition of “production ready” (the finish line)

The project is production ready only when all of the following are true and evidenced:

  1. Install, generate, lint, type-check, test, and build from a clean checkout
  2. Deploy a known topology (API, frontends, PostgreSQL, Redis, storage, worker)
  3. Run two API replicas without duplicate bookings, duplicate cron side effects, or missing files
  4. Deliver notifications that were written to the outbox (email + realtime) without multi-replica double-send
  5. Detect failure, restore data within agreed RPO/RTO, and show no unresolved critical/high dependency vulnerabilities outside a dated exception
  6. Ship with Critical/High application-security findings closed (§3)
  7. §13 diligence checkpoint items are checked off with retained evidence

Until then, the accurate label remains capable beta / pre-scale, not production SaaS.

Out of scope for this plan: new marketplace features, KYC/OCR, per-tenant Docker isolation, microservices rewrite, marketing-only work.

Why this path

The core product and data architecture exist; the next milestone is to convert that breadth into a reproducible, secure, observable, horizontally safe production system.


2. Current state (re-verified after project update)

2.1 Progress since the original diligence snapshot

Area Previous gap Status now Evidence
Turbo pipeline Missing Done turbo.json (build/dev/lint/type-check/db:*)
Shared TS base Missing Done tsconfig.base.json
Scripts / ops helpers Missing Done scripts/ (env, docker-prod-*, admin, security-static-check.mjs, backup guides)
Compose / Docker Missing Done docker-compose.dev.yml, docker-compose.production.yml, Dockerfile.dev / .production / .test, production/ mirror
.gitignore Missing Done Root .gitignore
Static security script Missing Done scripts/security-static-check.mjs + npm run security:static
CI No workflows Partial .gitea/workflows/ (test.yml, build-and-deploy.yml) + .gitlab-ci.yml; no .github/workflows
Notification outbox consumer Missing Partial processNotificationOutbox() in notificationService.ts; cron every minute in apps/api/src/index.ts — still inside the API process; email/IN_APP delivery + DLQ; no redis.publish for realtime
Prior hardening passes Documented SECURITY_HARDENING_APPLIED_REPORT.md, leftover report, docs/SECURITY_HARDENING_* (June 2026) — API-key hash-only, Socket.IO actor verify, cookie session work, etc.

2.2 What remains strong

Area Evidence
Product surfaces apps/homepage, dashboard, admin, carplace, api
Domain depth Prisma: fleet, reservations, billing, payments, notifications, collections, admin
API shape Express modular monolith
Auth / tenancy JWT actors, HttpOnly cookies, admin 2FA, company middleware, subscription gates
Payments foundation Stripe / PayPal / AmanPay; webhook signature verify; WebhookEvent
Upload validation Magic bytes + MIME + size limits
Deploy intent Compose + Traefik configs, backup/restore scripts present

2.3 What still blocks a production claim

Priority Gap Repo evidence (current)
P0 App security Critical/High still open Team API spreads passwordHash / reset tokens; plaintext invite tokens; open redirect; unrestricted payment return URLs on authenticated checkout; admin presenter scrub incomplete; container Docker-socket design still in tree — see §3
P0 Ghost / dangerous container feature containerService.ts + apps/admin/.../containers/page.tsx still present
P0 Dependency SCA not freshly proven Diligence (9 Aug): 21 prod vulns; must re-run on CI/clean runner
P0 Phase 0 exit not fully evidenced Envelope files exist, but clean-runner green CI + container removal + SCA gate not closed
P1 Outbox not multi-replica safe Processor runs via node-cron in every API process; no lease/lock; no Redis realtime publish
P1 Process-local coordination In-memory express-rate-limit; Carplace idempotencyCache Map; all crons in API index.ts
P1 Local file storage FILE_STORAGE_ROOT disk — no S3/MinIO adapter found
P1 Ops blind spots Only GET /health; no /ready; no SIGTERM graceful shutdown
P1 Catalog drift Homepage pricing vs shared plan/entitlement constants (unchanged risk)
flowchart LR
  subgraph done [Restored_envelope]
    Turbo[turbo_tsconfig]
    Compose[compose_Dockerfiles]
    Scripts[scripts_security_static]
    Gitignore[gitignore]
  end
  subgraph open [Still_open]
    Sec[App_security_Critical_High]
    Containers[containerService_UI]
    Shared[Redis_rate_limit_idempotency]
    Worker[Separate_worker_lease]
    Ready[ready_shutdown_object_store]
  end
  done --> open

3. Security assessment (re-checked 12 Aug 2026 after update)

Scope: Static re-verification of findings from the earlier application review against current source.
Not in this pass: Live pen-test, production secrets rotation, successful live npm audit (registry TLS may still block local runs).

3.1 Controls that remain solid

Control Evidence
Tenant scoping on core CRUD Company routes use session companyId
JWT from env + HS256 pinned security/tokens.ts
Session cookies HttpOnly / Secure / SameSite sessionCookies.ts
Hashed company API keys (legacy plaintext removed) Hardening reports + schema
Upload magic-byte validation http/upload
Payment webhook signatures Stripe / PayPal / AmanPay paths
Forwarded-header scrubbing by default sanitizeForwardedHeaders unless TRUSTED_FORWARD_HEADERS=true
Site payment redirect allowlist assertAllowedPaymentRedirect in site.service.ts

3.2 Findings status after project update

Sev ID Status Location Finding
Critical S1 FIXED (Phase 0) teamService.ts Team list/invite use safe presenter; no hash/token leakage
Critical S2 FIXED (Phase 0) containerService.ts + admin containers page + ADR-001 Feature disabled / fail-closed; UI shows out-of-scope
High S3 FIXED (Phase 0) teamService.ts invite Invite token stored as SHA-256 hash
High S4 FIXED (Phase 0) Prisma Employee + auth repo @@unique([companyId, email]); login fails closed on ambiguity
High S5 FIXED (Phase 0) SignInForm.tsx Safe relative-path redirect only
High S6 FIXED (Phase 0) paymentRedirects.ts + payment/subscription services Authenticated checkout allowlists return URLs
High S7 FIXED (Phase 0) admin.schemas / admin.repo Slug regex + slugify on update
High S8 FIXED (Phase 0) admin.presenter.ts Strips reset/verification secrets
High S9 FIXED (docs + default scrub) forwardedHeaders.ts + docs/ops/proxy-trust.md Default scrub; TRUSTED_FORWARD_HEADERS documented
Medium S10 FIXED (Phase 0) employee/admin reset repos Hash-only reset token lookup
Medium S11 FIXED (Phase 3) reservation/review presenters reviewToken omitted from API JSON
Medium S12 FIXED site.repo / site.service Public booking token unused-only on payment consume
Medium S13 FIXED requireFreshAdmin2FA Max-age TTL (ADMIN_FRESH_2FA_MAX_AGE_MS, default 30m)
Medium S14 FIXED .gitignore Present
Medium S15 FIXED scripts/security-static-check.mjs Present; CI wired; local pass after env scrub

3.3 Dependency / SCA status

Check Result
Diligence SCA (9 Aug 2026) 21 production findings: 1 critical, 14 high, 5 moderate, 1 low
Local re-audit May fail on TLS to registry — do not treat as clean
Required CI/clean-runner npm audit --omit=dev (or OSV) with fail-on critical/high

3.4 Security remediations (ordered)

P0 — before any production traffic

  1. S1: Explicit safe select/presenter for team APIs — never return hashes/tokens
  2. S3: Hash invite tokens at rest; remove raw dual-match after migration (S10)
  3. S8: Expand admin presenter denylist
  4. S2: Remove/disable containerService + admin containers UI from GA
  5. S14/S15: Already fixed — keep in CI
  6. Re-run and clear dependency critical/high

P1 — before multi-user GA

  1. S4: @@unique([companyId, email]) + fail-closed login
  2. S5: Allowlist relative same-origin post-login redirects
  3. S6: Apply payment redirect allowlist to authenticated checkout/subscription
  4. S7: Slugify/validate admin slug updates
  5. S9: Document proxy trust; never enable trusted forwards without edge scrubbing
  6. S11S13: Hide review tokens; single-use public access; align fresh 2FA on money mutations

3.5 Security exit criteria

  • No API response includes password hashes, TOTP secrets, or raw reset/invite tokens
  • Invite/reset tokens hashed at rest; legacy raw match removed
  • Container/Docker-socket feature absent from production scope
  • Login redirect and payment return URLs allowlisted on all checkout paths
  • Employee email uniqueness (or fail-closed login) enforced
  • CI runs security:static + SCA; no unresolved critical/high outside dated exception
  • Independent pen-test after deploy envelope proven (Phase 3)

4. Guiding principles

  1. Evidence over claims — Phase exit criteria must be demonstrable.
  2. Close Critical/High security before scale work — Envelope restore is largely done; app-sec P0 is now the front of the queue.
  3. Shared state before more replicas — Redis/DB/object storage before scaling API.
  4. One worker plane — Notifications and scheduled jobs must not run identically on every API replica.
  5. Disable incomplete privileged features — Especially Docker-socket container management.
  6. One commercial source of truth — Marketing, checkout, and enforcement share typed entitlements.
  7. Extract services last — Only after measurement.

5. Phase 0 — Evidence recovery

Goal: Reproducible build/deploy envelope + security hygiene baseline.
Update: Core files are restored. Remaining work is prove + close security/container/SCA.

5.1 Build orchestration — DONE

Deliverable Status
turbo.json Present
tsconfig.base.json Present
scripts/ (env, docker-prod, admin, security-static) Present

5.2 Runtime topology — DONE (files present)

Deliverable Status
docker-compose.dev.yml / .production.yml Present
Dockerfile.dev / .production / .test Present
Env examples Present (.env.example, docker env samples)
Backup/restore scripts Present under scripts/

Still required as evidence: recorded smoke that Compose boots API + Postgres + Redis and /health succeeds on a clean machine.

5.3 Continuous integration — PARTIAL

Deliverable Status
Gitea workflows Present (.gitea/workflows/test.yml, build-and-deploy.yml)
GitLab CI Present (.gitlab-ci.yml)
GitHub Actions Not present (optional if Gitea/GitLab is the system of record)
Proven green run + SCA fail gate Not evidenced in this review

5.4 Ghost container control plane — NOT DONE

Action Status
Remove admin containers UI Still present
Quarantine/delete containerService.ts Still present
ADR: out of GA Missing

5.5 Dependency hygiene — NOT EVIDENCED

Must re-run SCA on CI and clear critical/high.

Phase 0 exit criteria (updated checkboxes)

  • turbo.json + tsconfig.base.json + scripts/ present in tree
  • Compose + Dockerfiles present in tree
  • .gitignore + security:static present
  • Team API no longer returns password hashes / reset tokens; invite tokens hashed at rest (S1/S3)
  • Admin presenter scrubs reset/verification secrets (S8)
  • Container orchestration disabled + ADR (docs/ADR-001-disable-per-tenant-containers.md) (S2)
  • Login redirect allowlisted; authenticated payment return URLs allowlisted; admin slug validated; employee (companyId, email) unique (S4S7)
  • Env example/dev templates scrubbed of real-looking secrets; security:static passes locally
  • Gitea CI includes security:static + npm audit --omit=dev --audit-level=high
  • Fresh clone: npm ci → generate → lint → type-check → test → build with no local repair (needs recorded evidence on a clean runner)
  • CI green on default branch with SCA policy (push/run evidence)
  • Compose smoke: API + Postgres + Redis; /health ok (recorded)
  • No unresolved critical/high vulns outside dated exceptions (await CI audit result)

6. Phase 1 — Correctness and shared state

Goal: Two API replicas + one dedicated worker are correct under retries and failover.
Status (12 Aug 2026): Core code landed. Still needs npm ci, migrate, and a two-replica Compose smoke for exit evidence.

6.1 Notification outbox — DONE in code

Piece Status
Process isolation apps/api/src/workers/index.ts + Compose api-worker; API jobs only if ENABLE_EMBEDDED_JOBS=true
Leasing lockedAt / lockedBy / attempts / availableAt + claim via updateMany
Realtime redis.publish('notifications:' + userId, …) on IN_APP delivery
Metrics Phase 2 /metrics + outbox counters (scrape/alerts still open)

6.2 Shared rate limiting — DONE in code

Redis store in redisRateLimitStore.ts (memory when NODE_ENV=test or RATE_LIMIT_STORE=memory).

6.3 Durable booking idempotency — DONE in code

idempotencyStore.ts (Redis; memory in test) used by Carplace /reservations.

6.4 Externalize scheduled work — DONE in code

Cron moved to workers/jobs.ts with Redis leader lock. API no longer starts cron by default.

6.5 Object storage — DONE in code (optional)

FILE_STORAGE_DRIVER=local|s3 + @aws-sdk/client-s3 + MinIO Compose profile storage. Default remains local disk.

6.6 Readiness and graceful shutdown — DONE in code

Item Status
GET /health Liveness
GET /ready DB + Redis + storage probes
SIGTERM drain API + worker close HTTP/Socket/Redis/Prisma

Phase 1 exit criteria

  • Worker entrypoint + outbox lease + Redis publish implemented
  • Redis rate limits + durable Carplace idempotency implemented
  • Cron externalized with leader lock; /ready + graceful shutdown implemented
  • S3/MinIO adapter + Compose worker/MinIO services present
  • Two API replicas + one worker: recorded concurrency/retry smoke (needs runner)
  • Outbox no double-send under two API replicas (API without embedded jobs)
  • Files readable across replicas when FILE_STORAGE_DRIVER=s3 (optional smoke)

7. Phase 2 — Operational control

Code baselines landed (2026-08): metrics/logs endpoint, plan catalog + tests, OpenAPI coverage gate, backup smoke check, privacy + billing/notification source-of-truth docs. Remaining work is evidence (drills, owners, alerts).

Item Status Location
Structured JSON access logs + /metrics (latency, status, outbox) In code apps/api/src/lib/opsMetrics.ts, app.ts, worker
Readiness /ready In code (Phase 1) DB / Redis / storage
Plan/entitlement catalog + contract tests In code packages/types/src/planCatalog.ts, homepage pricing import
OpenAPI completeness gate In code npm run openapi:coveragescripts/check-openapi-coverage.mjs
Backup artifact smoke check + RPO/RTO checklist In code / docs scripts/backup-restore-smoke-check.sh, scripts/backup-restore-guide.md
Privacy data map Doc baseline docs/PRIVACY_DATA_MAP.md (owners / DSAR still open)
Billing & notification SoT Doc baseline docs/BILLING_NOTIFICATION_SOURCE_OF_TRUTH.md
Staging alert + restore drill evidence Open Ops exercise
Provider reconciliation sample Open Finance / eng

Phase 2 exit criteria

  • Staging incident detectable and recoverable via runbooks (metrics scraped + alert fired once)
  • Restore exercise meets RPO/RTO (dated drill using backup smoke check)
  • Provider reconciliation sample exists
  • Plan catalog single-sourced with tests
  • Privacy data map drafted (assign legal/ops owners before GA)
  • OpenAPI coverage script in CI
  • Billing/notification source-of-truth documented

8. Phase 3 — Scale and assurance

Goal: Prove the system holds under load/failure, tenant isolation, independent security testing, and operable release/secret drills. Keep the modular monolith until measurement says otherwise (ADR-002 defers RLS).

Code / runbook baselines landed (2026-08):

Item Status Location
Cross-tenant negative suite In code apps/api/src/tests/integration/cross-tenant-isolation.test.ts
S11 reviewToken scrubbed from API payloads FIXED reservation presenter + review service presenters
Soak / load probe (+ optional k6) In code scripts/load/soak-probe.mjs, booking-smoke.k6.jsnpm run test:soak
Failure injection helper In code scripts/chaos/failure-injection.sh
Canary / rollback runbook Doc docs/ops/canary-rollback.md
Key rotation drill Doc docs/ops/key-rotation-drill.md
Pen-test scope pack Doc docs/security/pen-test-scope.md
Postgres RLS Deferred docs/ADR-002-defer-postgres-rls.md

Phase 3 exit criteria

  • Soak + failure-injection drill completed on staging with dated metrics evidence
  • Cross-tenant suite green in CI (cross-tenant-isolation) — code landed; CI run is evidence
  • Independent pen-test report in security-reports/; Critical/High closed or accepted
  • Canary promote or rollback drill recorded once
  • Key rotation drill recorded on staging
  • App-level isolation suite started; RLS deferred per ADR-002
  • S11 review tokens not returned in reservation/review JSON
  • S12 single-use public payment token consume
  • S13 fresh admin 2FA TTL

Phase 4 (months 36) — extract only if measured

Default: stay on the modular monolith + api-worker (ADR-003). Phase 4 is not a microservices rewrite.

Item Status Location
Extraction policy ADR Accepted docs/ADR-003-defer-service-extraction.md
Gates G1G7 + candidates Doc docs/ops/phase4-extraction-gates.md
Measurement template Doc docs/ops/phase4-measurement-template.md
Module seam map Doc docs/ops/phase4-module-boundaries.md
Actual service split Blocked until a candidate passes G1G7

Phase 4 exit criteria (for deciding, not for “having microservices”)

  • Extraction gates and candidates documented
  • In-monolith-first alternatives listed (scale workers, async media, thin webhooks)
  • At least one soak/profile attribution packaged with the measurement template (needs Phase 3 evidence)
  • Explicit decision recorded per candidate: do not extract (default) or approved extract ADR

Candidates if gates ever pass: payments/webhooks, notification worker as independent deployable, media processing.


9. Risk register → plan mapping

ID Risk Status Addressed by
R1 Unreproducible package Mostly mitigated (files restored; prove CI smoke) Phase 0 evidence
R2 Dependency vulnerabilities Open (CI policy; clear on runner) Phase 0.5 / CI SCA
R3 Privileged container feature Mitigated in code (ADR-001) Phase 0.4 + S2
R4 Outbox not dispatched Mitigated in code (worker + leases) Phase 1.1
R5 Process-local rate limit / idempotency Mitigated in code (Redis stores) Phase 1.21.3
R6 Embedded schedules Mitigated in code (worker + lock) Phase 1.4
R7 Local file persistence Mitigated in code (S3 driver) Phase 1.5
R8 Ops blind spots Partial (metrics/logs in code; alerts/drills open) Phase 1.6 + Phase 2
R9R14 Privacy, catalog, OpenAPI, legacy overlap, FE drift, flaky tests Partial / later (catalog, OpenAPI gate, privacy map, SoT docs) Phase 23
S1S8 App security Critical/High Fixed in code (S9 documented) §3 P0/P1
S10S15 Medium app security Fixed in code (S11S13 included) Hardening + Phase 3
S16S21 Medium/Low (undefined IDs) Track in pen-test Phase 3 evidence

10. Suggested ownership and evidence log

Field Example
Phase 0 / Security P0
Date YYYY-MM-DD
Commit / tag prod-ready-phase0
Commands npm ci && npm run type-check && npm run security:static && npm audit --omit=dev
Artifacts CI URL, Compose smoke logs, audit report
Exceptions CVE-xxxx until DATE by OWNER
Sign-off Eng lead

11. Explicit non-goals (until gates pass)

  • Claiming production ready / HA / horizontally scaled before security P0 + Phase 1 exit evidence
  • Shipping per-tenant container orchestration
  • Guaranteeing SMS/push without provider paths + worker proof
  • Marketing KYC / license authenticity beyond date/expiry validation
  • Splitting the monolith for its own sake

12. Next backlog (when implementation resumes)

Immediate (security + Phase 0 close)

  1. Fix team API secret leakage (S1); hash invite tokens (S3); scrub admin presenter (S8)
  2. Remove/disable containerService + admin containers UI (S2)
  3. Allowlist login redirect (S5) and authenticated payment return URLs (S6); slugify admin slugs (S7); employee email uniqueness (S4)
  4. Record clean-runner CI green + production SCA clear/critical policy
  5. Record Compose smoke evidence

Then Phase 1

  1. Move outbox + cron to dedicated worker with leases; add Redis publish for realtime
  2. Redis rate-limit store; durable Carplace idempotency
  3. Object storage (MinIO/S3); /ready + graceful shutdown

Then Phase 23

  1. Observability, restore drill, catalog convergence, privacy map (Phase 2 evidence still open)
  2. Soak/failure drills, cross-tenant CI green, pen-test report, canary + key-rotation evidence (Phase 3)

Then Phase 4 (only if measured)

  1. Fill docs/ops/phase4-measurement-template.md from Phase 3 soak data; default decision remains do not extract (ADR-003)

13. Diligence checkpoint (definition of done)

  1. Clean checkout builds/tests on documented Node/npm with no local repair
  2. Production SCA has no unresolved critical/high outside formal exception
  3. Two API replicas pass booking/payment/job/notification/file correctness with shared coordination
  4. Authoritative API contract + auth test matrix
  5. Restore exercise meets RPO/RTO
  6. Logs/metrics/alerts/runbooks demonstrated in a failure exercise
  7. Privacy controls cover classification, encryption plan, retention, privileged reads/exports, incident response
  8. Plan catalog / checkout / entitlements single-sourced
  9. Independent security testing closes critical/high or records explicit acceptance
  10. §3 Critical/High application findings closed

14. Thesis and kickoff

This plan is how RentalDriveGo gets to production ready. The monorepo envelope is largely restored; the remaining path is: close Critical/High app security → finish Phase 0 evidence → replica-safe Phase 1 → operate/recover Phase 2 → prove Phase 3 → pass §13.

When implementation starts, execute §12 in order. Do not skip security P0 for feature work. Do not claim production ready until §1 and §13 are evidenced.


End of production-readiness plan. Code root: D:\1\management. Companion diligence: RentalDriveGo_Technical_Architecture_and_Investor_Due_Diligence.docx. Re-verified: 12 Aug 2026.