Files
zarinpal-analytics-deployment/tasks/todo.md

349 lines
16 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Platform Operations implementation tasks
- Status: Approved — Phase 4 implementation authorized 2026-08-20
- Canonical plan: `tasks/plan.md`
- Adaptation baseline: `docs/QLOCKIFY-ADAPTATION-CONTRACT.md`
## Task 0: Approve the implementation plan
**Description:** Confirm that the Qlockify adaptation contract, dependency order, task boundaries, and verification gates are the correct basis for later implementation.
**Acceptance criteria:**
- [x] Platform Operations specification and ADRs 0001–0003 are approved.
- [x] Qlockify flow/environment mapping is documented without real runtime values.
- [x] Stakeholder approved this plan/task breakdown and authorized implementation on 2026-08-20.
**Verification:** Documentation diff review and confirmation that no implementation artifact was created.
**Dependencies:** None
**Files likely touched:** documentation only
**Estimated scope:** Small
## Task 1: Add the deployment environment contract
**Description:** Add the root deployment example and redaction-safe validation for the Qlockify-shaped root/backend/frontend environment layout. Backend and frontend examples remain owned by their repositories and are consumed without duplication.
**Acceptance criteria:**
- [ ] The root example contains only deployment-owned safe defaults/blank placeholders; every service-owned variable has one documented owner.
- [ ] Real `.env` files stay ignored, mode `0600`, excluded from backup, and absent from rendered/logged output.
- [ ] Validation rejects missing, unknown, malformed, or client-exposed secret variables for the selected operation.
**Verification:** Placeholder render, negative validation matrix, Git-ignore/permission checks, and staged secret scan.
**Dependencies:** Task 0 plus explicit implementation authorization
**Files likely touched:** `.env.example`, validation helper/test, documentation; maximum 5 files
**Estimated scope:** Medium
## Task 2: Add PostgreSQL and Redis Compose foundation
**Description:** Define pinned private data services, health checks, named volumes, authentication, resource limits, bounded logs, and Redis AOF behavior.
**Acceptance criteria:**
- [ ] PostgreSQL and Redis become healthy using the approved pins and memory limits.
- [ ] Neither publishes a host port; only approved services can join the private data network.
- [ ] Redis uses AOF `everysec`, `noeviction`, 192-MB maxmemory, and remains excluded from backup.
**Verification:** Compose validation, health/port/network/volume/resource inspection, restart and Redis-loss tests.
**Dependencies:** Task 1
**Files likely touched:** `compose.yaml`, data config, focused validation/test files; maximum 5 files
**Estimated scope:** Medium
## Task 3: Add backend runtime boundaries
**Description:** Wire API and worker services plus one-off migrate/import/admin profiles from the same recorded backend checkout/image lineage.
**Acceptance criteria:**
- [ ] API and worker use private networks, health/readiness, graceful shutdown, and approved resource limits.
- [ ] Migrate/import/admin are explicit one-off profiles and never run during ordinary startup.
- [ ] Worker uses pre-1.0 Asynq `v0.26.0` with concurrency 16 globally and 4 per merchant.
**Verification:** Compose profile/startup/failure/shutdown tests and service command-contract review.
**Dependencies:** Task 2 and the backend-owned command contract
**Files likely touched:** `compose.yaml`, backend image definition, lifecycle tests/docs; maximum 5 files
**Estimated scope:** Medium
## Task 4: Add frontend production build
**Description:** Build and serve the direct frontend checkout using its ordinary static context and same-origin API configuration.
**Acceptance criteria:**
- [ ] Build consumes frontend commit `64f5ac2` or later and its tracked font/license normally.
- [ ] Static sentinel, SPA fallback, and emitted font resource/content type pass.
- [ ] Deployment Git contains no font/license copy, fetch, credentials, or alternate asset path.
**Verification:** Production build/static-server checks plus repository-policy scan.
**Dependencies:** Task 1 and the frontend-owned build contract
**Files likely touched:** frontend image definition, `compose.yaml`, focused health/tests; maximum 5 files
**Estimated scope:** Medium
## Checkpoint A: Runtime foundation
- [ ] Tasks 1–4 pass from clean nested checkouts with placeholder test values.
- [ ] Normal startup cannot invoke migration/import/admin.
- [ ] No secrets, runtime state, raw data, or duplicated font assets are tracked.
## Task 5: Adapt Qlockify Nginx routing
**Description:** Adapt the Qlockify template/entrypoint for the single canonical domain, default-host rejection, frontend/API routing, security headers, health, caching, and SSE.
**Acceptance criteria:**
- [ ] Only ports 80/443 publish; HTTP redirects exactly to `zarinpal.amiirkhl.ir`; unknown hosts reject.
- [ ] API and frontend routing/fallback do not overlap and public readiness remains generic.
- [ ] Exact SSE route preserves auth/event ids and streams heartbeats without buffering, cache, compression, or premature timeout.
**Verification:** `nginx -t`, route/header/default-host tests, static/font test, and streamed SSE fixture.
**Dependencies:** Tasks 3–4
**Files likely touched:** Nginx template, image/entrypoint, focused tests; maximum 5 files
**Estimated scope:** Medium
## Task 6: Adapt Qlockify Certbot lifecycle
**Description:** Adapt Qlockify's HTTP-01 bootstrap, pinned Certbot service, certificate watcher, renewal, and Nginx reload for the single domain.
**Acceptance criteria:**
- [ ] Missing production certificate uses only the bounded bootstrap certificate path until issuance.
- [ ] Staging issuance, production issuance, renewal, and validated Nginx reload pass.
- [ ] No alternate domain, local production certificate mode, private key, or ACME state enters Git.
**Verification:** Compose/entrypoint tests, staging ACME drill, certificate name/expiry/protocol inspection.
**Dependencies:** Task 5
**Files likely touched:** Certbot entrypoint/config, `compose.yaml`, focused tests/docs; maximum 5 files
**Estimated scope:** Medium
## Task 7: Add guarded first-start orchestration
**Description:** Add the explicit first-server path that starts data services, migrates, imports/reconciles the pinned CSV, and activates public services only after success.
**Acceptance criteria:**
- [ ] Input is exact/read-only and migration/import/reconciliation failures prevent public activation.
- [ ] A completed identical import is a safe no-op; recovery never guesses a partial CSV position.
- [ ] Ordinary deploy/start/reboot paths never invoke first-start, importer, or admin jobs.
**Verification:** Success, checksum/schema/count, interruption/recovery, idempotency, and ordinary-start negative tests.
**Dependencies:** Tasks 2–6 and approved backend importer commands
**Files likely touched:** `scripts/first-start.sh`, Compose profile, focused tests/docs; maximum 5 files
**Estimated scope:** Medium
## Checkpoint B: Edge and first-start
- [ ] Nginx/TLS/static/API/SSE checks pass.
- [ ] First-start is fail-closed and ordinary startup never imports.
- [ ] Only Nginx is externally reachable.
## Task 8: Add release records and operation lock
**Description:** Define ignored current/previous/timestamped release records and one exclusive lock shared by deploy, rollback, restore, migration, and import as required.
**Acceptance criteria:**
- [ ] Record captures three SHAs, local image ids, migration, backup, operator, health, outcome, and previous release without secrets.
- [ ] Invalid/incomplete records cannot become active and current/previous updates are atomic.
- [ ] Conflicting operations fail safely without stale-lock ambiguity.
**Verification:** Schema/parser tests, invalid-record matrix, lock contention/stale-owner tests, and redaction scan.
**Dependencies:** Task 1
**Files likely touched:** release schema/helper/tests/docs; maximum 5 files
**Estimated scope:** Medium
## Task 9: Adapt Qlockify manual deployment
**Description:** Adapt Qlockify's deploy structure to verify three manually pulled repositories, build/migrate/replace services, observe health, and activate one coherent release.
**Acceptance criteria:**
- [ ] Script performs no Git fetch/pull/reset and rejects wrong path/remote/branch/SHA/dirty state.
- [ ] Build, migration, readiness, font, worker, or SSE failure leaves the active release unchanged and records evidence.
- [ ] Successful deployment records all three SHAs and passes a 15-minute postflight observation before activation.
**Verification:** ShellCheck, disposable Git fixtures, Compose mocks/integration, and induced failure matrix.
**Dependencies:** Tasks 5–8
**Files likely touched:** `scripts/deploy.sh`, release helper, focused tests/runbook; maximum 5 files
**Estimated scope:** Medium
## Task 10: Add three-hash rollback
**Description:** Select the previous release record, verify its three revisions, rebuild, and run the normal postflight without automatic database down-migration.
**Acceptance criteria:**
- [ ] Exact previous hashes and clean repository guards precede checkout/build.
- [ ] Migration incompatibility stops rollback with the current stack preserved.
- [ ] Normal application rollback completes within 15 minutes and is recorded as a new event.
**Verification:** Git fixture tests, incompatible-migration negative test, and timed rollback drill.
**Dependencies:** Task 9
**Files likely touched:** rollback script/helper/tests/runbook; maximum 5 files
**Estimated scope:** Medium
## Checkpoint C: Release safety
- [ ] Dirty/diverged/manual-pull/build/migration/readiness failures preserve the active stack.
- [ ] Active and previous releases map to exact three-repository revisions.
- [ ] Timed rollback meets its target without a down migration.
## Task 11: Adapt Qlockify PostgreSQL backup
**Description:** Retain Qlockify's matching-client plain-SQL dump flow while packaging only PostgreSQL plus a non-secret release/migration/checksum manifest.
**Acceptance criteria:**
- [ ] Dump nonzero exit, empty output, invalid header, or checksum failure prevents package success.
- [ ] Package contains only plain SQL and the approved non-secret manifest/checksums.
- [ ] `.env`, secrets, Redis, TLS, CSV, credentials, logs, monitoring, font/license, and media are absent.
**Verification:** Fixture database dump, package listing/header/checksum checks, and forbidden-content scan.
**Dependencies:** Tasks 2, 8
**Files likely touched:** `scripts/backup.sh`, manifest helper/tests/runbook; maximum 5 files
**Estimated scope:** Medium
## Task 12: Adapt encrypted ParsPack upload and retention
**Description:** Retain Qlockify's OpenSSL AES-256-CBC PBKDF2-200k and rclone structure while keeping three encrypted local and seven encrypted remote verified backups.
**Acceptance criteria:**
- [ ] Upload uses a private temporary key and is finalized only after remote verification.
- [ ] Failed encryption/upload/verification preserves all previous copies and no plaintext backup remains durably.
- [ ] Retention removes only exact stale targets after new-copy verification and never deletes the last good copy.
**Verification:** Fake S3/ParsPack-compatible fixture, failure/retention matrix, checksum and plaintext-residue checks.
**Dependencies:** Task 11
**Files likely touched:** upload script, retention helper/tests/runbook; maximum 5 files
**Estimated scope:** Medium
## Task 13: Add guarded local restore
**Description:** Restore an explicitly selected verified backup into a new empty target using `psql -X --set ON_ERROR_STOP=on` and independent production guards.
**Acceptance criteria:**
- [ ] Provenance/checksum/header/version/free-space/exact-empty-target checks precede mutation.
- [ ] Every missing production factor aborts before writers stop or a database is changed.
- [ ] Restore never changes `.env`, Redis, TLS, CSV, credentials, logs, or frontend assets.
**Verification:** Disposable database restore, negative guard matrix, partial-SQL failure, and exclusion checks.
**Dependencies:** Task 11
**Files likely touched:** `scripts/restore.sh`, guard/helper/tests/runbook; maximum 5 files
**Estimated scope:** Medium
## Task 14: Adapt encrypted S3 restore handoff
**Description:** Download an explicit encrypted object, verify/decrypt it in a restrictive temporary directory, and hand it to the guarded local restore.
**Acceptance criteria:**
- [ ] Production requires an explicit object id; an implicit `latest` cannot select a production backup.
- [ ] Remote identity/size/checksum and decrypted package checks pass before restore invocation.
- [ ] Temporary encrypted/plaintext material is removed only from the exact validated temporary directory.
**Verification:** Fake S3 object-selection/tamper tests, decryption failure tests, and guarded handoff rehearsal.
**Dependencies:** Tasks 12–13
**Files likely touched:** `scripts/restore-from-s3.sh`, focused tests/runbook; maximum 4 files
**Estimated scope:** Medium
## Checkpoint D: Recovery
- [ ] Daily schedule/3-local/7-remote retention behaves safely.
- [ ] Forbidden content is absent from every backup package.
- [ ] Clean restore passes schema/count/tenant/login/cold-cache/worker/SSE validation within two hours.
## Task 15: Bootstrap and accept the MVP server
**Description:** Execute the reviewed runbooks on the prepared Ubuntu server, fill runtime values, perform first-start, activate the release, and rehearse rollback/restore.
**Acceptance criteria:**
- [ ] Host access/firewall/swap/Docker/DNS/TLS and only-80/443 exposure checks pass.
- [ ] Migration/import/accounts/tenant isolation/worker/SSE/frontend font and manual health/log checks pass.
- [ ] Three-hash release, rollback, encrypted remote backup, and clean restore meet their time/recovery targets.
**Verification:** Signed non-secret bootstrap/MVP acceptance record.
**Dependencies:** Checkpoints A–D and a prepared server/runtime values
**Files likely touched:** ignored server evidence/release records only
**Estimated scope:** Operational session
## Checkpoint E: MVP ready
- [ ] Every MVP acceptance criterion passes.
- [ ] Active/previous releases and backup/restore are proven.
- [ ] Stakeholder explicitly opens the observability phase.
## Task 16: Add private Prometheus collection
**Description:** Add post-MVP application, worker/SSE, PostgreSQL/Redis, host/container, backup/restore, and TLS collection with bounded labels and retention.
**Acceptance criteria:**
- [ ] Required signals and target health pass within approved resource/retention limits.
- [ ] Prometheus/exporters are private and not routed by Nginx.
- [ ] Merchant/user/request/card identifiers are absent from metric labels.
**Verification:** Target/signal/cardinality/resource tests and sensitive-label scan.
**Dependencies:** Checkpoint E
**Files likely touched:** split into focused Prometheus/exporter slices of no more than 5 files
**Estimated scope:** Multiple Medium slices
## Task 17: Add private Grafana dashboards and alerts
**Description:** Add post-MVP dashboards, release correlation, actionable symptom alerts, runbook links, and tested operator delivery.
**Acceptance criteria:**
- [ ] Service, worker/SSE, data, host, backup, and TLS dashboards answer the approved operator questions.
- [ ] Every page/ticket alert is actionable, bounded, linked to a runbook, and tested through an induced failure.
- [ ] Grafana remains private and contains no sensitive labels, logs, or embedded credentials.
**Verification:** Dashboard review, induced failure, alert test-fire, access and redaction checks.
**Dependencies:** Task 16
**Files likely touched:** split into focused dashboard/alert slices of no more than 5 files
**Estimated scope:** Multiple Medium slices
## Final observability checkpoint before AI
- [ ] Prometheus/Grafana acceptance criteria pass after MVP.
- [ ] Alert delivery and runbook ownership are proven.
- [ ] Stakeholder explicitly approves beginning AI implementation.
- [ ] No CI/CD pipeline or automated deployment system was introduced.