Files
moreminimore-chat/engineering-log.md
2026-08-16 00:12:29 +07:00

9.2 KiB

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 has 407 passed/15 failed across six unrelated date/time and report-snapshot tests; those failures are not in the changed Help Center path.
  • 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.