Files
moreminimore-chat/engineering-log.md
Kunthawat Greethong c21d579a6a [docs] correct full Vitest baseline to clean under TZ=UTC
The earlier '407 passed/15 failed' record was an artifact of running the
frontend suite without the TZ=UTC the package.json test script sets; the 15
timezone-sensitive failures (availabilityHelpers, timeHelper, snoozeHelpers,
ReportsDataHelper, ReportContainer) all pass under UTC and none are in changed
privacy/rebrand paths. Verified: TZ=UTC full run passes 414 files / 4176 tests,
0 failed (exit 0).
2026-08-16 07:45:22 +07:00

116 lines
15 KiB
Markdown

# Engineering Log
## 2026-08-15 — Chatwoot privacy and rebrand audit
### Objective
Clone/inspect the official Chatwoot source and produce an evidence-based plan for a private-label community fork with no Chatwoot telemetry or visible product branding while preserving upstream syncability.
### Baseline
- Repository: `https://github.com/chatwoot/chatwoot.git`
- Branch: `develop`
- Commit: `9a73c1473ffa0ae6a9c7725046b8ca17922dcc83`
- Root license: MIT
- `enterprise/` license: proprietary and excluded from the recommended baseline
### Actions performed
1. Confirmed clone origin, branch, commit and clean working tree.
2. Mapped Rails, Sidekiq, Vue/Vite, widget, runtime configuration, manifests and deployment entry points.
3. Traced scheduled installation sync to `ChatwootHub.sync_with_hub`.
4. Confirmed from implementation and specs that `DISABLE_TELEMETRY` removes metrics but still POSTs installation configuration to Chatwoot Hub.
5. Traced Chatwoot-hosted push relay, remote changelog and optional onboarding registration.
6. Audited Amplitude, frontend/backend Sentry, optional APM integrations, arbitrary dashboard script injection and marketing tracking configuration.
7. Classified feature-required outbound integrations separately from unsolicited telemetry.
8. Mapped branding configuration, views, frontend components, locale copy, assets, manifest, mail/widget surfaces and technical compatibility identifiers.
9. Wrote implementation plan at `.hermes/plans/2026-08-15_092534-chatwoot-private-rebrand.md`.
10. Incorporated independent outbound-audit findings: silent cwctl command reporting, prechecked onboarding subscription, manual super-admin Hub refresh, and unconditional signup-domain website/DNS enrichment.
11. Split the master plan into a small-model execution package with an execution contract, dependency index, 12 runbooks and 58 unique bounded tasks covering baseline, privacy removals, observability, branding, E2E egress, release gates and upstream maintenance.
### Key decisions
- Use Community Edition as the legal/technical baseline.
- Remove telemetry code paths rather than rely on environment opt-out.
- Preserve internal namespaces and widget API aliases initially to avoid upstream conflict and customer embed breakage.
- Use no-op adapters/central policy for high-churn analytics callsites.
- Remove hosted push relay; require own push credentials.
- Remove cwctl event reporting and automatic signup-domain enrichment from the strict privacy baseline.
- Validate zero unexpected egress dynamically, not by grep alone.
### Source changes
Application changes are tracked in the verified checkpoints and the uncheckpointed working tree described below.
### Implementation state (updated 2026-08-16)
- SM-01 audit harness checkpoint: `8ebb320`.
- SM-02 Hub sync/registration/telemetry removal checkpoint: `832a7fd`; compatibility correction checkpoint: `1a3697f`.
- SM-03 push-relay removal checkpoint: `8b1a033`; exact post-commit independent review passed with empty security and logic blocking arrays.
- SM-04 cwctl privacy checkpoint: `3458272`; exact post-commit independent review `1a3697f..3458272` passed with empty security and logic blocking arrays.
- SM-05 community enrichment removal checkpoint: `d9bf4c4`; split exact post-commit reviews `deleg_5e2578c5` (backend) and `deleg_ee3e217f` (frontend) both passed with empty security and logic blocking arrays. Enterprise compatibility fails because proprietary code still depends on the deleted community base class.
- SM-05 boundary decision: do not modify `enterprise/` or restore a compatibility stub in the community surface. The community-only checkpoint is independently verified; the enterprise `WebsiteBrandingService` load failure remains an explicit architecture/legal blocker.
- SM-06 Product Analytics removal is verified at `ced77af`: `a96b977` removed the Amplitude/token delivery surface, and `ced77af` added the missing `GLOBAL_CONFIG_KEYS` allowlist before `window.globalConfig` serialization. Fresh pre-commit review `deleg_f9644984` and exact post-commit review `deleg_9b0a2f63` both passed with empty security and logic blocking arrays. SM-07 local-only observability remains uncheckpointed. SM-08 community Help Center GTM removal is committed as `5619cc3` and exact post-commit independent review `deleg_dd3db5b7` passed; fixed, allow-listed provider snippets remain explicit admin-configured traffic, while the GTM container path is removed because its remote container can execute arbitrary tags/scripts.
- SM-08 enterprise marketing conversion tracking was classified as fixed-endpoint, cloud-only, limited attribution traffic inside proprietary enterprise code; it remains untouched pending a separate legal/compatibility decision.
- SM-09/10 remain blocked until approved product brand inputs are supplied.
- No push or deployment has occurred.
### Current verification notes
- SM-06 exact committed range `a96b977..ced77af` changed one controller line. The root cause was raw `GlobalConfig.get(...).merge(app_config)` allowing an unrequested `CLOUD_ANALYTICS_TOKEN` from a stubbed/configured hash to reach serialized `window.globalConfig`; the remediation applies `.slice(*GLOBAL_CONFIG_KEYS)` before the explicit `app_config` merge. Raw merge/sliced-allowlist reproductions were RED/GREEN, committed Ruby syntax and `git diff --check` passed, and the added-line security scan was clean. Rails RSpec/RuboCop remain environment-blocked because the active Ruby is 2.6.10 and Bundler 2.5.16 is unavailable.
- SM-03 post-commit exact-range review `1a3697f..8b1a033`: passed; exactly five requested paths changed; 7 targeted RSpec examples, Ruby syntax, RuboCop and git diff checks passed; no secrets or Hub relay fallback remain.
- SM-04 post-commit exact-range review `1a3697f..3458272`: passed; exactly two requested deployment paths changed; Bash 3.2 syntax, normal privacy test, fail-closed grep-status checks, secret scan and diff checks passed.
- SM-05 pre-commit staged review: passed for exactly 16 community-only paths; commit hook completed ESLint/RuboCop checks.
- SM-05 post-commit split review: backend slice `deleg_5e2578c5` and frontend slice `deleg_ee3e217f` both passed with explicit empty blocking arrays; combined committed range is exactly the intended 16 paths.
- SM-05 frontend targeted run: 9 suites and 8 tests passed. Backend RSpec rerun was blocked by the local toolchain (installed Ruby 3.4.10 versus Gemfile Ruby 3.4.4; bundle environment has no usable `rspec` executable). Historical controller baseline remains 28 examples/5 failures versus HEAD baseline 27/5; the new no-enrichment regression passed.
- SM-07 targeted Rails specs: 7 examples, 0 failures; Ruby/JS lint and syntax checks pass.
- SM-08 dashboard controller specs: 7 examples, 0 failures; runtime denylist confirms no `DASHBOARD_SCRIPTS` reader or sink in community files.
- SM-08 Help Center correction: 57 portal model/controller examples, targeted GTM regression 3/3, Ruby/ERB syntax, RuboCop, ESLint/Prettier and exact staged security/scope scans pass; Vite production build passes. Full Vitest run with `TZ=UTC` (the env the `package.json` `test` script sets) passes: 414 files / 4176 tests passed, 0 failed. The earlier recorded "407 passed/15 failed" baseline was an artifact of running without `TZ=UTC`; the 15 timezone-sensitive failures all pass under UTC and none are in changed paths.
- Frontend privacy batch: 14 tests, 14 passed.
### SM-05 gate closure
- Exact committed range `3458272..d9bf4c4` is verified by two fresh read-only reviews: backend `deleg_5e2578c5` and frontend `deleg_ee3e217f`; both returned `passed=true`, `security_concerns=[]`, and `logic_errors=[]`.
- Community signup/onboarding no longer performs automatic website/branding enrichment or its Redis marker; enterprise compatibility remains intentionally unresolved and must not be repaired by restoring remote enrichment.
### Remaining gates
- Resolve the SM-05 enterprise/legal boundary without restoring remote enrichment.
- Complete the SM-07 local-only observability checkpoint and the remaining SM-08 dashboard/config script-injection work; keep provider comments, marketing conversion tracking, and enterprise compatibility as separate scoped decisions.
- Run full Rails/frontend suites, production-like copied-database checks, HTTP/DNS egress capture, upstream history fetch and merge rehearsal.
## 2026-08-16 — SM-06 checkpoint closure
### Root cause and fix
- The first SM-06 commit (`a96b977`) removed `CLOUD_ANALYTICS_TOKEN` from the requested config keys but left `GlobalConfig.get(*GLOBAL_CONFIG_KEYS).merge(app_config)` vulnerable to unrelated keys returned by a stub/config source being serialized through `window.globalConfig`.
- The regression was reproduced with `analytics-test-token`; the one-line remediation in `ced77af` applies `.slice(*GLOBAL_CONFIG_KEYS)` before merging the explicit application config.
### Verification
- Pre-commit independent review `deleg_f9644984`: `passed=true`, `security_concerns=[]`, `logic_errors=[]`.
- Exact post-commit review `deleg_9b0a2f63` over `a96b977..ced77af`: `passed=true`, `security_concerns=[]`, `logic_errors=[]`.
- Committed scope: one path, one line; Ruby syntax, diff check, and added-line security scan passed.
- Rails RSpec/RuboCop: blocked by local runtime mismatch (`ruby 2.6.10`, missing Bundler 2.5.16); no runtime pass is claimed.
### Next action
- Start SM-07 as a separate isolated task/checkpoint. Do not mix its unstaged files with the verified SM-06 commits.
## 2026-08-16 — SM-07/SM-08 closure and residual Hub gate
### Verified implementation checkpoints
- SM-07 committed at `19dc449`; exact post-commit review of `372f316..19dc449` returned a complete pass with `security_concerns=[]` and `logic_errors=[]`. Focused frontend tests passed 6/6, ESLint passed, Ruby syntax passed, and the offline frozen-lockfile install passed. Rails RSpec/RuboCop remain blocked by Ruby 2.6.10 and missing Bundler 2.5.16.
- SM-08 dashboard/script removal committed at `364e72f`; exact post-commit review returned a complete pass with empty blocking arrays. `DASHBOARD_SCRIPTS` reader, catalog entry, and HTML sink are absent. Vite production build completed with exit code 0. Full Vitest with `TZ=UTC` passes 414 files / 4176 tests, 0 failed (the "407/15" earlier baseline was a non-UTC run artifact; the 15 timezone-sensitive tests all pass under UTC).
### Residual Hub worktree gate
- Remaining Community Hub URL/push/changelog code was removed in the worktree. The shared Enterprise billing callers were preserved: `ChatwootHub.billing_url` now accepts only explicit HTTPS `CHATWOOT_BILLING_URL`; both shared views suppress the link when unset or invalid.
- Deterministic evidence passed: changelog Vitest 1/1, ESLint, Ruby/Bash syntax, isolated billing validation harness (unset/HTTPS/http/malformed/userinfo), cwctl privacy test, privacy audit harness, default privacy audit `findings=0`, and added-line security scan. The residual Vite production build also completed successfully with `4,736 modules transformed`, `✓ built in 3m 49s`, exit code 0; only Browserslist/chunk-size warnings remain.
- The residual diff was intentionally **not committed** until a fresh independent review returned a valid approval. Fail-closed policy treats interrupted/timeout/incomplete/no-payload reviews as no verdict.
## 2026-08-16 — SM-07 remediation and residual Hub gate closure
### SM-07 remediation (committed `2ef6fa5`)
- A fresh backend review (`deleg_96a5daef`, `372f316..19dc449`) returned `passed=false` with one logic blocker: `FilteredCountInstrumentation` discarded its documented local observability signals (computed `duration_ms` and discarded it, mutated a transient `Thread.current` summary then discarded it, and `increment` silently no-oped for non-aggregated signals).
- Remediation (user chose in-process local sink): added a thread-safe (`Mutex`) per-account in-process store with a public `last_request_summary(account_id:)` reader; `record_request_summary` now stores the completed summary (status + `duration_ms` + counts) instead of discarding it. Regression specs assert the completed summary is readable and an unknown account returns `nil`.
- Verification: Ruby syntax passed, isolated sink probe `SINK_PROBE=pass` (dedup/status/counts/duration/unknown-account all asserted), and a grep for NewRelic/`new_relic`/`record_metric`/`record_custom_event`/`record_event`/`Net::HTTP`/`RestClient`/`HTTPClient` returned zero matches (no egress). Rails RSpec/RuboCop remain blocked by Ruby 2.6.10 / missing Bundler 2.5.16; no runtime pass is claimed.
- Independent exact committed-diff review `deleg_087e4a3f` over `19dc449..2ef6fa5` is **pending**; the SM-07 gate is not fully closed until that returns `passed=true` with empty blocking arrays.
### Residual Hub removal (committed `8101395`)
- A fresh independent review `deleg_49d6ee2e` of `/tmp/chatwoot-residual-index-FINAL.txt` (regenerated from the exact current index; verified byte-identical to the staged blobs with zero mismatches) returned `passed=true`, `security_concerns=[]`, `logic_errors=[]`. It verified: Community Hub URL/push/sync/registration/event/changelog egress removed; `changelog.js` is a local empty-feed adapter exporting the default `ChangelogApi` with no axios/fetch/network and its spec traps both `global.fetch` and `axios.get`; `billing_url` is HTTPS-only with host and no userinfo and never Hub-falls-back; both ERB templates guard with `.present?` and escaped `<%= %>`; `chatwoot_hub_spec` uses `singleton_class.instance_methods(false)` for edition-safe `base_url` assertion without modifying the proprietary Enterprise file; `privacy_audit` uses a narrow per-file/per-rule (`hub-url`) Enterprise exception while all other rules still scan it; deployment privacy guard unchanged; no secrets/injection/dummy service/unrelated scope.
- Committed as `8101395` (10 paths) with the approval review ID referenced in the message. The older review `deleg_589b22d1` (also `passed=true`) covered a prior snapshot and is superseded by the fresh final-index gate. Commit is clean: only the 10 residual paths, with SM-07 already committed separately at `2ef6fa5`.
- A post-commit exact committed-diff gate over `2ef6fa5..8101395` remains to be run to fully close the committed-bytes gate (staged review saw the index; a range review sees the final committed bytes).
### Release and compatibility blockers
- SM-09/10 cannot change branding defaults/assets/metadata until approved product name, domains, logos/icons, sender, legal URLs and primary color are supplied. Current visible-branding report remains 2,878 findings; it is report-only, not a privacy-rule pass.
- Enterprise `WebsiteBrandingService` remains proprietary and currently fails with `NameError: uninitialized constant WebsiteBrandingService`; no dummy service or remote enrichment was added.
- SM-11/12 static inventory: repository is shallow (`git rev-parse --is-shallow-repository=true`); Docker, Syft, Trivy, Cosign and Gitleaks are unavailable; Dockerfile base images are mutable tags. Full-history fetch, isolated HTTP/DNS capture, production-like DB rehearsal, image digest and SBOM gates remain blocked. No network action was performed.