fix production issues
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
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
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
# Commercial and data design updates (Phase 2)
|
||||
|
||||
## Plan / entitlement catalog
|
||||
|
||||
**Single source of truth:** `packages/types/src/planCatalog.ts`
|
||||
|
||||
Exports (via `@rentaldrivego/types` / `api.ts` re-exports):
|
||||
|
||||
- `PLAN_PRICES`, `PLAN_ENTITLEMENTS`, capabilities, public marketing map (`launch` → `STARTER`, etc.)
|
||||
- Helpers: `getVehicleLimit`, `getPublicMonthlyMajorUnits`, `planHasCapability`
|
||||
|
||||
**Consumers:**
|
||||
|
||||
- Homepage marketing prices → `apps/homepage/src/content/pricing-config.ts`
|
||||
- API site/subscription fallbacks → `@rentaldrivego/types`
|
||||
- Vehicle fleet limits → catalog first (no duplicate hardcoded fallback table)
|
||||
|
||||
Contract tests: `packages/types/src/planCatalog.test.ts` (`npm run test:types`).
|
||||
|
||||
## Billing & notifications source of truth
|
||||
|
||||
See `docs/BILLING_NOTIFICATION_SOURCE_OF_TRUTH.md`:
|
||||
|
||||
| Domain | Canonical | Legacy |
|
||||
|--------|-----------|--------|
|
||||
| Money | `BillingAccount` / `BillingInvoice` / intents | `SubscriptionInvoice` (compat reads only) |
|
||||
| Notifications | `NotificationEvent` → recipients → deliveries + **outbox** | Flat `Notification` |
|
||||
|
||||
Dispatch design: outbox worker is the delivery executor; direct transactional email remains allowed for auth/security messages.
|
||||
|
||||
## Privacy baseline
|
||||
|
||||
`docs/PRIVACY_DATA_MAP.md` inventories data classes, storage, access, and open gaps (field encryption, DSAR automation, privileged-read audit). Ops metrics intentionally exclude PII payloads.
|
||||
|
||||
## OpenAPI completeness
|
||||
|
||||
Design rule: ops endpoints `/health`, `/ready`, `/metrics` must appear in OpenAPI. CI runs `npm run openapi:coverage` (`scripts/check-openapi-coverage.mjs`).
|
||||
@@ -0,0 +1,106 @@
|
||||
# Production readiness — what was implemented
|
||||
|
||||
**Date:** 2026-08-12
|
||||
**Code root:** monorepo `apps/*`, `packages/*`
|
||||
**Plan:** `docs/ops/RentalDriveGo_Production_Readiness_Plan.md`
|
||||
|
||||
This document is the design-facing summary of work already landed. It is not a backlog.
|
||||
|
||||
---
|
||||
|
||||
## Architecture decision (unchanged)
|
||||
|
||||
Keep a **modular Express monolith** plus a dedicated **job worker process**. Do not split payments/webhooks/media into microservices until measurement gates pass (ADR-003).
|
||||
|
||||
```
|
||||
[Traefik] → [API × N] → PostgreSQL
|
||||
↓ ↑
|
||||
[Redis] [api-worker]
|
||||
↓
|
||||
[local disk | S3/MinIO]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 0 — Security & envelope
|
||||
|
||||
| Area | Design outcome | Primary paths |
|
||||
|------|----------------|---------------|
|
||||
| Team API secrets | Safe presenters; no `passwordHash` / raw reset tokens in responses | `apps/api/src/services/teamService.ts` |
|
||||
| Invite tokens | Stored as SHA-256 hashes | `teamService.ts` |
|
||||
| Admin presenters | Scrub reset/verification secrets | `admin.presenter.ts` |
|
||||
| Per-tenant Docker | Feature **fail-closed**; UI out-of-scope | `containerService.ts`, ADR-001 |
|
||||
| Login redirects | Relative same-origin allowlist only | dashboard `SignInForm` |
|
||||
| Payment return URLs | Authenticated checkout allowlists | `paymentRedirects.ts` |
|
||||
| Admin slugs | Validated / slugified | admin schemas/repo |
|
||||
| Employee email | `@@unique([companyId, email])`; ambiguous login fails closed | Prisma + auth repo |
|
||||
| Reset / email verify tokens | Hash-only lookup (no raw dual-match) | employee/admin repos |
|
||||
| Env templates | Scrubbed of real-looking secrets | `.env.example`, docker env samples |
|
||||
| CI hygiene | `security:static` + production `npm audit` gate | `.gitea/workflows/test.yml` |
|
||||
|
||||
---
|
||||
|
||||
## Phase 1 — Replica-safe shared state
|
||||
|
||||
| Area | Design outcome | Primary paths |
|
||||
|------|----------------|---------------|
|
||||
| Job plane | Dedicated `api-worker`; API embeds jobs only if `ENABLE_EMBEDDED_JOBS=true` | `apps/api/src/workers/` |
|
||||
| Notification outbox | DB leases (`lockedAt` / `lockedBy` / `attempts` / `availableAt`) | `notificationService` + migration |
|
||||
| Realtime | Redis publish on IN_APP delivery | worker / notification service |
|
||||
| Rate limits | Shared Redis store | `redisRateLimitStore.ts` |
|
||||
| Carplace idempotency | Redis-backed store | `idempotencyStore.ts` |
|
||||
| Files | `FILE_STORAGE_DRIVER=local\|s3` (+ MinIO compose profile) | `lib/storage`, `objectStorage` |
|
||||
| Readiness | `GET /ready` checks DB, Redis, storage | `app.ts` |
|
||||
| Shutdown | SIGTERM drains API and worker | `index.ts`, `workers/index.ts` |
|
||||
|
||||
---
|
||||
|
||||
## Phase 2 — Operational control (code baselines)
|
||||
|
||||
| Area | Design outcome | Primary paths |
|
||||
|------|----------------|---------------|
|
||||
| Metrics / logs | Structured JSON access logs; Prometheus text at `GET /metrics` | `opsMetrics.ts`, `app.ts` |
|
||||
| Worker metrics | Optional `WORKER_METRICS_PORT` exposes `/metrics` + `/health` | `workers/index.ts` |
|
||||
| Plan catalog | Single source in `@rentaldrivego/types` | `packages/types/src/planCatalog.ts` |
|
||||
| Homepage pricing | Imports catalog (no hard-coded MAD amounts) | `apps/homepage/.../pricing-config.ts` |
|
||||
| OpenAPI gate | Coverage script + CI step | `scripts/check-openapi-coverage.mjs` |
|
||||
| Backup smoke | Artifact checker + RPO/RTO checklist | `scripts/backup-restore-*` |
|
||||
| Privacy / SoT docs | Baseline maps | `docs/PRIVACY_DATA_MAP.md`, `docs/BILLING_NOTIFICATION_SOURCE_OF_TRUTH.md` |
|
||||
|
||||
**Still evidence-only:** staging alert fire, dated restore drill, provider reconciliation sample, privacy owners.
|
||||
|
||||
---
|
||||
|
||||
## Phase 3 — Scale & assurance (code / runbooks)
|
||||
|
||||
| Area | Design outcome | Primary paths |
|
||||
|------|----------------|---------------|
|
||||
| Cross-tenant suite | Vehicles, customers, reservations, offers, team, payments negatives | `cross-tenant-isolation.test.ts` |
|
||||
| S11 | `reviewToken` omitted from API JSON | reservation presenter, review service |
|
||||
| S12 | Public booking token: GET does not burn; payment **consumes** once | `site.service.ts` / `site.repo.ts` |
|
||||
| S13 | Fresh admin 2FA max-age (default 30 minutes) | `requireFreshAdmin2FA` |
|
||||
| Soak / chaos | Node soak probe + optional k6; failure-injection helper | `scripts/load/`, `scripts/chaos/` |
|
||||
| Canary / keys / pen-test | Runbooks and scope pack | `docs/ops/`, `docs/security/pen-test-scope.md` |
|
||||
| Postgres RLS | Explicitly deferred | ADR-002 |
|
||||
|
||||
**Still evidence-only:** soak/chaos drill records, pen-test report, canary + key-rotation drills.
|
||||
|
||||
---
|
||||
|
||||
## Phase 4 — Extract only if measured
|
||||
|
||||
| Area | Design outcome |
|
||||
|------|----------------|
|
||||
| Default | Stay on monolith + `api-worker` |
|
||||
| Gates | G1–G7 in `docs/ops/phase4-extraction-gates.md` |
|
||||
| Candidates | Notifications worker deployable, payments/webhooks, media — **blocked** until measurement |
|
||||
| Code change | None required for “phase complete”; ADR-003 + templates only |
|
||||
|
||||
---
|
||||
|
||||
## Explicit non-goals (still)
|
||||
|
||||
- Claiming production ready without §13 evidence
|
||||
- Shipping per-tenant container orchestration
|
||||
- Microservices rewrite without gate evidence
|
||||
- Marketing KYC / OCR authenticity beyond license date checks
|
||||
@@ -0,0 +1,32 @@
|
||||
# Design docs — production-readiness updates
|
||||
|
||||
This folder records **architecture and design changes** landed while taking RentalDriveGo to production readiness (Phases 0–4). It complements the longer plans under `docs/ops/` and `docs/project-design/`.
|
||||
|
||||
## Investor diligence (Word)
|
||||
|
||||
| Document | Notes |
|
||||
|----------|--------|
|
||||
| [RentalDriveGo_Technical_Architecture_and_Investor_Due_Diligence.docx](./RentalDriveGo_Technical_Architecture_and_Investor_Due_Diligence.docx) | **v1.1 (12 Aug 2026)** — updated executive status, risk/roadmap annotations, and **Addendum A** for Phases 0–4 implementation. Original v1.0 backup: `RentalDriveGo_Technical_Architecture_and_Investor_Due_Diligence.v1.0.backup.docx` |
|
||||
|
||||
## Markdown design notes
|
||||
|
||||
| Doc | Purpose |
|
||||
|-----|---------|
|
||||
| [PRODUCTION_READINESS_IMPLEMENTED.md](./PRODUCTION_READINESS_IMPLEMENTED.md) | What was implemented per phase (code + runbooks) |
|
||||
| [RUNTIME_AND_OPS_SURFACE.md](./RUNTIME_AND_OPS_SURFACE.md) | Worker, Redis, storage, `/ready`, `/metrics`, env knobs |
|
||||
| [SECURITY_DESIGN_UPDATES.md](./SECURITY_DESIGN_UPDATES.md) | Security findings S1–S15 design outcomes |
|
||||
| [COMMERCIAL_AND_DATA_DESIGN.md](./COMMERCIAL_AND_DATA_DESIGN.md) | Plan catalog, billing/notification SoT, privacy map |
|
||||
|
||||
## Related ADRs
|
||||
|
||||
- `docs/ADR-001-disable-per-tenant-containers.md`
|
||||
- `docs/ADR-002-defer-postgres-rls.md`
|
||||
- `docs/ADR-003-defer-service-extraction.md`
|
||||
|
||||
## Canonical execution plan
|
||||
|
||||
- `docs/ops/RentalDriveGo_Production_Readiness_Plan.md`
|
||||
|
||||
## Status (2026-08-12)
|
||||
|
||||
**Application code and in-repo runbooks for Phases 0–4 are complete.** Remaining production-ready gates are **ops evidence** (CI smoke, restore/alert drills, soak, pen-test), not missing product features. Phase 4 does **not** extract microservices by default (ADR-003).
|
||||
@@ -0,0 +1,56 @@
|
||||
# Runtime and ops surface (post Phases 1–2)
|
||||
|
||||
Design note for operators and engineers. Reflects the **current** intended production topology.
|
||||
|
||||
## Processes
|
||||
|
||||
| Process | Role | Notes |
|
||||
|---------|------|--------|
|
||||
| `api` | HTTP API | Do **not** set `ENABLE_EMBEDDED_JOBS=true` when running multiple API replicas |
|
||||
| `api-worker` | Outbox + scheduled jobs | Uses DB leases + Redis leader lock for cron |
|
||||
| Frontends | homepage, carplace, dashboard, admin | Unchanged modular apps |
|
||||
| postgres | System of record | |
|
||||
| redis | Rate limit, idempotency, pub/sub, locks | Required for replica-safe behavior |
|
||||
| object storage | Optional S3/MinIO | Prefer over local disk for multi-replica files |
|
||||
|
||||
## HTTP ops endpoints
|
||||
|
||||
| Method | Path | Purpose |
|
||||
|--------|------|---------|
|
||||
| GET | `/health` | Liveness |
|
||||
| GET | `/ready` | Readiness: database, redis, storage |
|
||||
| GET | `/metrics` | Prometheus-style counters/gauges (latency, status, outbox pending/published) |
|
||||
| GET | `/api/v1/openapi.json` | OpenAPI document |
|
||||
| GET | `/docs` | Swagger UI (when enabled) |
|
||||
|
||||
Worker (optional):
|
||||
|
||||
| Env | Behavior |
|
||||
|-----|----------|
|
||||
| `WORKER_METRICS_PORT=<port>` | Worker listens for `GET /metrics` and `GET /health` |
|
||||
| `0` / unset | No worker HTTP listener |
|
||||
|
||||
## Important environment knobs
|
||||
|
||||
Documented in `.env.example`:
|
||||
|
||||
- `ENABLE_EMBEDDED_JOBS` — default false in production examples
|
||||
- `RATE_LIMIT_STORE` / `IDEMPOTENCY_STORE` — prefer `redis`
|
||||
- `FILE_STORAGE_DRIVER` — `local` or `s3` (+ `S3_*`)
|
||||
- `TRUSTED_FORWARD_HEADERS` — default false; see `docs/ops/proxy-trust.md`
|
||||
- `ADMIN_FRESH_2FA_MAX_AGE_MS` — default `1800000` (30 minutes)
|
||||
- `WORKER_METRICS_PORT` — optional
|
||||
|
||||
## Observability design
|
||||
|
||||
- Access logs: structured JSON via morgan in `app.ts`
|
||||
- In-process series: `apps/api/src/lib/opsMetrics.ts`
|
||||
- Durable outbox gauges: API `/metrics` also counts PENDING / PUBLISHED rows from PostgreSQL so scrapes remain useful when jobs run only on the worker
|
||||
|
||||
## Failure / release runbooks
|
||||
|
||||
- Chaos helper: `scripts/chaos/failure-injection.sh`
|
||||
- Soak: `npm run test:soak` → `scripts/load/soak-probe.mjs`
|
||||
- Canary / rollback: `docs/ops/canary-rollback.md`
|
||||
- Key rotation: `docs/ops/key-rotation-drill.md`
|
||||
- Backup smoke: `scripts/backup-restore-smoke-check.sh`
|
||||
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,54 @@
|
||||
# Security design updates (Phases 0 & 3)
|
||||
|
||||
Summary of application-security design changes from the production-readiness program. Full finding table lives in `docs/ops/RentalDriveGo_Production_Readiness_Plan.md` §3.
|
||||
|
||||
## Closed in code (S1–S15)
|
||||
|
||||
| ID | Design rule now enforced |
|
||||
|----|---------------------------|
|
||||
| S1 | Team list/invite responses use safe presenters — never return password hashes or raw reset tokens |
|
||||
| S2 | Per-tenant Docker/Compose orchestration is **out of production scope** (fail-closed + ADR-001) |
|
||||
| S3 | Invite tokens hashed at rest (SHA-256) |
|
||||
| S4 | Employee email unique per company; login fails closed on ambiguity |
|
||||
| S5 | Post-login redirects limited to safe relative paths |
|
||||
| S6 | Authenticated payment/subscription return URLs allowlisted |
|
||||
| S7 | Admin company slugs validated / slugified |
|
||||
| S8 | Admin presenters strip reset/verification secrets |
|
||||
| S9 | Forwarded headers scrubbed by default; `TRUSTED_FORWARD_HEADERS=true` only behind a scrubbing edge (`docs/ops/proxy-trust.md`) |
|
||||
| S10 | Password reset token lookup is hash-only |
|
||||
| S11 | `reviewToken` is never returned in reservation/review API JSON (still used server-side for email links) |
|
||||
| S12 | Public booking access: **read** does not burn the token; **payment init** atomically consumes an unused token |
|
||||
| S13 | Admin money / privileged mutations require 2FA proof newer than `ADMIN_FRESH_2FA_MAX_AGE_MS` |
|
||||
| S14 | `.gitignore` present for secrets/build artifacts |
|
||||
| S15 | `npm run security:static` in CI |
|
||||
|
||||
## Public booking token (S12) flow
|
||||
|
||||
```
|
||||
createBooking → mint publicAccessToken (hash stored)
|
||||
│
|
||||
├─ GET booking?token=… → validate (used or unused OK until expiry) — do not consume
|
||||
│
|
||||
└─ initPayment(token) → require usedAt IS NULL → updateMany set usedAt → proceed
|
||||
(second payment attempt with same token → 404)
|
||||
```
|
||||
|
||||
## Fresh admin 2FA (S13)
|
||||
|
||||
`requireFreshAdmin2FA` rejects when:
|
||||
|
||||
- TOTP not enrolled, or
|
||||
- JWT lacks `last2faAt`, or
|
||||
- `now - last2faAt > ADMIN_FRESH_2FA_MAX_AGE_MS` (default 30 minutes)
|
||||
|
||||
Wired on finance/support money and high-privilege admin mutations.
|
||||
|
||||
## Tenant isolation design
|
||||
|
||||
- App-level: every company resource query includes `companyId` from the authenticated session
|
||||
- Regression suite: `apps/api/src/tests/integration/cross-tenant-isolation.test.ts`
|
||||
- Postgres RLS: **deferred** (ADR-002) until the app-level suite and worker/migration roles are ready
|
||||
|
||||
## Pen-test
|
||||
|
||||
Scope pack: `docs/security/pen-test-scope.md`. Reports belong under `security-reports/` (evidence, not design).
|
||||
@@ -0,0 +1,357 @@
|
||||
from docx import Document
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
|
||||
src = Path(r"D:\1\management\docs\design\RentalDriveGo_Technical_Architecture_and_Investor_Due_Diligence.docx")
|
||||
backup = src.with_suffix(".v1.0.backup.docx")
|
||||
if not backup.exists():
|
||||
shutil.copy2(src, backup)
|
||||
print("backup:", backup)
|
||||
|
||||
doc = Document(str(src))
|
||||
|
||||
|
||||
def set_runs_text(paragraph, text: str) -> None:
|
||||
if paragraph.runs:
|
||||
paragraph.runs[0].text = text
|
||||
for r in paragraph.runs[1:]:
|
||||
r.text = ""
|
||||
else:
|
||||
paragraph.add_run(text)
|
||||
|
||||
|
||||
# --- Title / version updates ---
|
||||
for i, p in enumerate(doc.paragraphs[:15]):
|
||||
t = p.text.strip()
|
||||
if t.startswith("Version 1.0"):
|
||||
set_runs_text(
|
||||
p,
|
||||
"Version 1.1 • 12 August 2026 (addendum update of v1.0 dated 9 August 2026)",
|
||||
)
|
||||
print(f"updated version para {i}")
|
||||
if "Prepared from the supplied" in t:
|
||||
set_runs_text(
|
||||
p,
|
||||
"Prepared from the car_management / management monorepo; v1.1 reflects Phases 0–4 "
|
||||
"production-readiness implementation status as of 12 August 2026",
|
||||
)
|
||||
print(f"updated prepared para {i}")
|
||||
|
||||
# --- Soft-update executive bottom line ---
|
||||
for i, p in enumerate(doc.paragraphs):
|
||||
if p.text.startswith("Bottom line"):
|
||||
set_runs_text(
|
||||
p,
|
||||
"Bottom line (updated 12 Aug 2026) RentalDriveGo remains a serious multi-application "
|
||||
"rental SaaS with strong domain depth. Since the 9 August 2026 archive review, the monorepo "
|
||||
"has been restored and Phases 0–4 of the production-readiness program have landed in "
|
||||
"application code and in-repo runbooks: security P0/P1 fixes, dedicated api-worker with "
|
||||
"leased outbox, Redis rate-limit/idempotency, object-storage driver, /ready and /metrics, "
|
||||
"single plan catalog, OpenAPI coverage gate, cross-tenant tests, and extraction deferred "
|
||||
"pending measurement (ADR-003). The platform is still not “production ready” as an investor "
|
||||
"claim until clean-runner CI/SCA evidence, Compose multi-replica smoke, restore/alert drills, "
|
||||
"soak/pen-test, and §13 checkpoint items are recorded. The accurate frame is now: product + "
|
||||
"operational foundations exist in code; remaining capital/ops work is evidence and assurance, "
|
||||
"not a greenfield rebuild.",
|
||||
)
|
||||
print(f"updated bottom line para {i}")
|
||||
break
|
||||
|
||||
# Update investment interpretation cells
|
||||
for table in doc.tables:
|
||||
for row in table.rows:
|
||||
cells = [c.text.strip() for c in row.cells]
|
||||
if not cells:
|
||||
continue
|
||||
dim = cells[0]
|
||||
if dim == "Delivery reproducibility" and len(cells) >= 3:
|
||||
row.cells[1].text = "Mostly restored in tree"
|
||||
row.cells[2].text = (
|
||||
"turbo.json, tsconfig.base.json, scripts, Compose, backup/restore present; "
|
||||
"clean-runner CI green still needs recorded evidence."
|
||||
)
|
||||
elif dim == "Scale readiness" and len(cells) >= 3:
|
||||
row.cells[1].text = "Improved (code)"
|
||||
row.cells[2].text = (
|
||||
"api-worker, Redis stores, S3/MinIO driver, readiness/shutdown landed; "
|
||||
"two-replica soak evidence still open."
|
||||
)
|
||||
elif dim == "Operational maturity" and len(cells) >= 3:
|
||||
row.cells[1].text = "Baseline in code"
|
||||
row.cells[2].text = (
|
||||
"/metrics, structured logs, backup smoke, runbooks present; "
|
||||
"alert/restore/pen-test drills still open."
|
||||
)
|
||||
elif dim == "Security design" and len(cells) >= 3:
|
||||
row.cells[1].text = "Strengthened"
|
||||
row.cells[2].text = (
|
||||
"S1–S15 application findings closed or documented; containers fail-closed; "
|
||||
"fresh 2FA TTL; review tokens scrubbed; pen-test still required."
|
||||
)
|
||||
|
||||
# Update material findings glance
|
||||
for table in doc.tables:
|
||||
for row in table.rows:
|
||||
texts = [c.text for c in row.cells]
|
||||
joined = " | ".join(texts)
|
||||
if "turbo.json and tsconfig.base.json are absent" in joined and len(row.cells) >= 3:
|
||||
row.cells[0].text = "P0→Mitigated"
|
||||
row.cells[1].text = (
|
||||
"Monorepo envelope (turbo.json, tsconfig.base.json, scripts, Compose) "
|
||||
"restored in the management tree."
|
||||
)
|
||||
row.cells[2].text = "Remaining: recorded clean-runner CI/smoke evidence."
|
||||
elif "21 vulnerabilities" in joined and len(row.cells) >= 3:
|
||||
row.cells[0].text = "P0→Open evidence"
|
||||
row.cells[1].text = (
|
||||
"CI now runs production npm audit (fail on high); "
|
||||
"local/registry results still time-sensitive."
|
||||
)
|
||||
row.cells[2].text = "Clear critical/high on a clean runner or file dated exceptions."
|
||||
elif ("Per-tenant container" in joined or "container orchestration" in joined.lower()) and len(
|
||||
row.cells
|
||||
) >= 3:
|
||||
row.cells[0].text = "P0→Mitigated"
|
||||
row.cells[1].text = "Container feature fail-closed; admin UI out-of-scope; ADR-001 accepted."
|
||||
row.cells[2].text = "Do not re-enable Docker-socket tenant isolation."
|
||||
elif "no dispatcher/worker was found" in joined and len(row.cells) >= 3:
|
||||
row.cells[0].text = "P1→Mitigated in code"
|
||||
row.cells[1].text = "Dedicated api-worker with leased outbox dispatch and Redis realtime publish."
|
||||
row.cells[2].text = "Prove under two API replicas + worker in staging."
|
||||
elif "Rate limits" in joined and "process-local" in joined.lower() and len(row.cells) >= 3:
|
||||
row.cells[0].text = "P1→Mitigated in code"
|
||||
row.cells[1].text = (
|
||||
"Redis rate-limit store, Redis Carplace idempotency, cron leader lock, S3/local storage driver."
|
||||
)
|
||||
row.cells[2].text = "Multi-replica correctness evidence still required."
|
||||
elif (
|
||||
"No complete CI/CD, observability" in joined
|
||||
or "graceful shutdown, backup/restore" in joined
|
||||
) and len(row.cells) >= 3:
|
||||
row.cells[0].text = "P1→Partial"
|
||||
row.cells[1].text = (
|
||||
"CI security/OpenAPI gates, /ready, /metrics, backup smoke scripts and ops runbooks landed."
|
||||
)
|
||||
row.cells[2].text = "Alert/restore/pen-test/soak evidence still open."
|
||||
|
||||
# Update recommendation bullets
|
||||
for p in doc.paragraphs:
|
||||
if p.text.startswith("Treat the current asset as a capable beta"):
|
||||
set_runs_text(
|
||||
p,
|
||||
"Treat the current asset as a late beta / hardening-stage platform: application foundations "
|
||||
"for production readiness are largely implemented; do not claim production-hardened SaaS until "
|
||||
"exit evidence (§13) is complete.",
|
||||
)
|
||||
if p.text.startswith("Condition any production or scale claim"):
|
||||
set_runs_text(
|
||||
p,
|
||||
"Condition any production or scale claim on remaining evidence gates: clean CI/SCA, "
|
||||
"two-replica Compose smoke, restore meeting RPO/RTO, alert/failure drill, soak, "
|
||||
"independent pen-test, and provider reconciliation sample.",
|
||||
)
|
||||
if p.text.startswith("Fund a 90-day production-readiness program"):
|
||||
set_runs_text(
|
||||
p,
|
||||
"Continue the funded production-readiness program through evidence close-out; Phase 4 "
|
||||
"service extraction remains deferred until measurement gates pass (ADR-003). Reevaluate "
|
||||
"extraction only from measured load and ownership boundaries.",
|
||||
)
|
||||
|
||||
# Risk register updates
|
||||
risk_updates = {
|
||||
"R1": ("Mostly mitigated", "Envelope restored in tree; prove clean-runner CI/smoke."),
|
||||
"R2": ("Open (CI policy)", "SCA gate in CI; clear critical/high on clean runner."),
|
||||
"R3": ("Mitigated in code", "ADR-001; containerService fail-closed; UI out-of-scope."),
|
||||
"R4": ("Mitigated in code", "api-worker + DB leases + Redis publish; prove multi-replica."),
|
||||
"R5": ("Mitigated in code", "Redis rate-limit and idempotency stores."),
|
||||
"R6": ("Mitigated in code", "Schedules on worker with Redis leader lock."),
|
||||
"R7": ("Mitigated in code", "FILE_STORAGE_DRIVER local|s3; prefer object storage in prod."),
|
||||
"R8": ("Partial", "Metrics/logs/runbooks in repo; drills still open."),
|
||||
"R9": ("Partial", "Privacy data map drafted; owners/DSAR/encryption plan open."),
|
||||
"R10": ("Mitigated in code", "packages/types planCatalog + homepage/API consumers + tests."),
|
||||
"R11": ("Mitigated in code", "OpenAPI coverage CI gate; /ready and /metrics documented."),
|
||||
"R12": ("Partial", "Billing/notification SoT documented; migration convergence ongoing."),
|
||||
"R13": ("Open / later", "Shared clients still a Phase 3+ concern."),
|
||||
"R14": ("Open / later", "Stabilize CI runners; bounded completion still evidence."),
|
||||
}
|
||||
|
||||
for table in doc.tables:
|
||||
for row in table.rows:
|
||||
first = row.cells[0].text.strip()
|
||||
for rid, (status, action) in risk_updates.items():
|
||||
if first.startswith(rid + " ") or first.startswith(rid + "•") or first.startswith(rid + " ·") or first.startswith(rid + " •"):
|
||||
if len(row.cells) >= 4:
|
||||
ev = row.cells[2].text.strip()
|
||||
if "STATUS (12 Aug 2026)" not in ev:
|
||||
row.cells[2].text = f"{ev}\nSTATUS (12 Aug 2026): {status}"
|
||||
row.cells[3].text = action
|
||||
elif len(row.cells) >= 3:
|
||||
row.cells[2].text = f"STATUS (12 Aug 2026): {status}. {action}"
|
||||
|
||||
# Phase status annotations
|
||||
phase_status = {
|
||||
"Phase 0 — Evidence recovery": (
|
||||
"STATUS (12 Aug 2026): Application/envelope code largely complete; "
|
||||
"remaining is recorded CI green + Compose smoke evidence."
|
||||
),
|
||||
"Phase 1 — Correctness and shared state": (
|
||||
"STATUS (12 Aug 2026): Core code landed (worker, Redis stores, S3 driver, /ready, shutdown). "
|
||||
"Two-replica correctness evidence still open."
|
||||
),
|
||||
"Phase 2 — Operational control": (
|
||||
"STATUS (12 Aug 2026): Metrics, catalog, OpenAPI gate, backup smoke, privacy/SoT docs landed. "
|
||||
"Alert/restore/reconciliation drills still open."
|
||||
),
|
||||
"Phase 3 — Scale and assurance": (
|
||||
"STATUS (12 Aug 2026): Cross-tenant suite, soak/chaos scripts, canary/key/pen-test runbooks, "
|
||||
"S11–S13 fixes landed. Staging drills and pen-test report still open. RLS deferred (ADR-002)."
|
||||
),
|
||||
"Phase 4 — Selective evolution": (
|
||||
"STATUS (12 Aug 2026): Extraction gates documented (ADR-003). Default decision remains "
|
||||
"do not extract microservices until G1–G7 measurement passes."
|
||||
),
|
||||
}
|
||||
|
||||
paras = list(doc.paragraphs)
|
||||
for idx, p in enumerate(paras):
|
||||
for title, status in phase_status.items():
|
||||
if p.text.startswith(title):
|
||||
insert_after = idx
|
||||
for j in range(idx, min(idx + 12, len(paras))):
|
||||
if paras[j].text.startswith("Exit evidence"):
|
||||
insert_after = j
|
||||
break
|
||||
if insert_after + 1 < len(paras) and not paras[insert_after + 1].text.strip():
|
||||
set_runs_text(paras[insert_after + 1], status)
|
||||
elif paras[insert_after].text.startswith("Exit evidence"):
|
||||
set_runs_text(paras[insert_after], paras[insert_after].text + " " + status)
|
||||
else:
|
||||
set_runs_text(p, p.text + " — " + status)
|
||||
print("phase status:", title[:48])
|
||||
|
||||
# Claims not supportable — soft updates
|
||||
for p in doc.paragraphs:
|
||||
if p.text.startswith("CI-green or reproducibly deployable from the supplied package"):
|
||||
set_runs_text(
|
||||
p,
|
||||
"CI-green or reproducibly deployable without recorded clean-runner evidence "
|
||||
"(envelope restored in tree, but attestation still required).",
|
||||
)
|
||||
if p.text.startswith("Complete OpenAPI coverage or a stable external API contract"):
|
||||
set_runs_text(
|
||||
p,
|
||||
"Perfect OpenAPI coverage (coverage gate exists; completeness still approximate "
|
||||
"and must stay green in CI).",
|
||||
)
|
||||
if p.text.startswith("Guaranteed email, push, SMS, or realtime notification delivery through the modeled outbox"):
|
||||
set_runs_text(
|
||||
p,
|
||||
"Guaranteed multi-channel notification delivery in production without worker soak "
|
||||
"evidence (dispatcher exists in code).",
|
||||
)
|
||||
if p.text.startswith("Per-tenant container isolation or functioning tenant container orchestration"):
|
||||
set_runs_text(
|
||||
p,
|
||||
"Per-tenant container isolation (explicitly disabled and out of production scope — ADR-001).",
|
||||
)
|
||||
|
||||
# --- Append full addendum ---
|
||||
doc.add_page_break()
|
||||
doc.add_heading("Addendum A — Implementation status (12 August 2026)", level=1)
|
||||
intro = doc.add_paragraph()
|
||||
intro.add_run(
|
||||
"This addendum updates Version 1.0 (9 August 2026) after execution of the production-readiness "
|
||||
"program against the management monorepo. It does not replace the original evidence-based findings; "
|
||||
"it records what has since been implemented in application code and in-repo runbooks, and what remains "
|
||||
"evidence-only before a production-ready claim."
|
||||
)
|
||||
|
||||
doc.add_heading("A.1 Verdict shift", level=2)
|
||||
doc.add_paragraph(
|
||||
"9 Aug 2026: capable beta / pre-scale; package incomplete; scale-critical paths process-local or missing."
|
||||
)
|
||||
doc.add_paragraph(
|
||||
"12 Aug 2026: late beta / hardening stage. Phases 0–4 application code and runbooks are complete. "
|
||||
"Investor “production ready” still requires recorded ops evidence (CI smoke, multi-replica proof, "
|
||||
"DR/alert drills, soak, pen-test)."
|
||||
)
|
||||
|
||||
doc.add_heading("A.2 Phase implementation summary", level=2)
|
||||
phases = [
|
||||
(
|
||||
"Phase 0 — Security & envelope",
|
||||
"Team/admin secret scrubbing; hashed invites; containers fail-closed (ADR-001); "
|
||||
"redirect/payment allowlists; employee email uniqueness; hash-only reset/verify tokens; "
|
||||
"env scrub; CI security:static + npm audit gate.",
|
||||
),
|
||||
(
|
||||
"Phase 1 — Replica-safe shared state",
|
||||
"Dedicated api-worker; outbox DB leases; Redis publish; Redis rate-limit & Carplace idempotency; "
|
||||
"FILE_STORAGE_DRIVER local|s3; GET /ready; graceful shutdown; ENABLE_EMBEDDED_JOBS gated.",
|
||||
),
|
||||
(
|
||||
"Phase 2 — Operational control",
|
||||
"Structured logs + GET /metrics; planCatalog single source; OpenAPI coverage CI; "
|
||||
"backup smoke + RPO/RTO checklist; privacy map; billing/notification source-of-truth docs.",
|
||||
),
|
||||
(
|
||||
"Phase 3 — Scale & assurance",
|
||||
"Cross-tenant isolation suite; S11 reviewToken scrub; S12 single-use payment consume; "
|
||||
"S13 fresh 2FA TTL; soak/chaos scripts; canary/key-rotation/pen-test docs; RLS deferred (ADR-002).",
|
||||
),
|
||||
(
|
||||
"Phase 4 — Selective evolution",
|
||||
"No microservice extract. ADR-003 + extraction gates G1–G7 + measurement templates. "
|
||||
"Default: stay modular monolith + api-worker.",
|
||||
),
|
||||
]
|
||||
for title, body in phases:
|
||||
p = doc.add_paragraph()
|
||||
run = p.add_run(title + ". ")
|
||||
run.bold = True
|
||||
p.add_run(body)
|
||||
|
||||
doc.add_heading("A.3 Architecture topology (current intent)", level=2)
|
||||
doc.add_paragraph(
|
||||
"Traefik → API × N → PostgreSQL; API and worker share Redis (rate limit, idempotency, pub/sub, locks); "
|
||||
"api-worker owns outbox/cron; files via local disk or S3/MinIO. Ops endpoints: /health, /ready, /metrics."
|
||||
)
|
||||
|
||||
doc.add_heading("A.4 Security findings S1–S15", level=2)
|
||||
doc.add_paragraph(
|
||||
"Application findings S1–S15 from the follow-on security review are fixed or documented in code "
|
||||
"(including S9 proxy-trust documentation, S11–S13). Independent penetration testing remains an open "
|
||||
"Phase 3 evidence item. See docs/design/SECURITY_DESIGN_UPDATES.md and "
|
||||
"docs/ops/RentalDriveGo_Production_Readiness_Plan.md §3."
|
||||
)
|
||||
|
||||
doc.add_heading("A.5 Remaining evidence before production-ready claim", level=2)
|
||||
for item in [
|
||||
"Clean-runner: npm ci → generate → lint → type-check → test → build with CI green and SCA clear/exceptioned.",
|
||||
"Compose smoke: two API replicas + worker + Postgres + Redis; booking/payment/notification/file correctness.",
|
||||
"Phase 2 drills: staging alert fired once; dated restore meeting RPO/RTO; provider reconciliation sample; privacy owners.",
|
||||
"Phase 3 drills: soak + failure injection; pen-test report under security-reports/; canary or rollback record; key-rotation drill.",
|
||||
"Phase 4: fill measurement template; default decision remains do not extract.",
|
||||
]:
|
||||
doc.add_paragraph(item, style="List Bullet")
|
||||
|
||||
doc.add_heading("A.6 Design documentation pointers", level=2)
|
||||
for item in [
|
||||
"docs/design/README.md — index of production-readiness design updates",
|
||||
"docs/design/PRODUCTION_READINESS_IMPLEMENTED.md — phase-by-phase landed work",
|
||||
"docs/design/RUNTIME_AND_OPS_SURFACE.md — worker, metrics, env knobs",
|
||||
"docs/ops/RentalDriveGo_Production_Readiness_Plan.md — living execution plan and exit checkboxes",
|
||||
"docs/ADR-001-disable-per-tenant-containers.md",
|
||||
"docs/ADR-002-defer-postgres-rls.md",
|
||||
"docs/ADR-003-defer-service-extraction.md",
|
||||
]:
|
||||
doc.add_paragraph(item, style="List Bullet")
|
||||
|
||||
doc.add_paragraph(
|
||||
"End of Addendum A. Original sections 1–17 remain the baseline diligence narrative from 9 August 2026, "
|
||||
"as amended inline above where statuses were refreshed."
|
||||
)
|
||||
|
||||
doc.save(str(src))
|
||||
print("saved", src, "size", src.stat().st_size)
|
||||
Reference in New Issue
Block a user