9.2 KiB
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
- Confirmed clone origin, branch, commit and clean working tree.
- Mapped Rails, Sidekiq, Vue/Vite, widget, runtime configuration, manifests and deployment entry points.
- Traced scheduled installation sync to
ChatwootHub.sync_with_hub. - Confirmed from implementation and specs that
DISABLE_TELEMETRYremoves metrics but still POSTs installation configuration to Chatwoot Hub. - Traced Chatwoot-hosted push relay, remote changelog and optional onboarding registration.
- Audited Amplitude, frontend/backend Sentry, optional APM integrations, arbitrary dashboard script injection and marketing tracking configuration.
- Classified feature-required outbound integrations separately from unsolicited telemetry.
- Mapped branding configuration, views, frontend components, locale copy, assets, manifest, mail/widget surfaces and technical compatibility identifiers.
- Wrote implementation plan at
.hermes/plans/2026-08-15_092534-chatwoot-private-rebrand.md. - Incorporated independent outbound-audit findings: silent cwctl command reporting, prechecked onboarding subscription, manual super-admin Hub refresh, and unconditional signup-domain website/DNS enrichment.
- 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 review1a3697f..3458272passed with empty security and logic blocking arrays. - SM-05 community enrichment removal checkpoint:
d9bf4c4; split exact post-commit reviewsdeleg_5e2578c5(backend) anddeleg_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 enterpriseWebsiteBrandingServiceload failure remains an explicit architecture/legal blocker. - SM-06 Product Analytics removal is verified at
ced77af:a96b977removed the Amplitude/token delivery surface, andced77afadded the missingGLOBAL_CONFIG_KEYSallowlist beforewindow.globalConfigserialization. Fresh pre-commit reviewdeleg_f9644984and exact post-commit reviewdeleg_9b0a2f63both passed with empty security and logic blocking arrays. SM-07 local-only observability remains uncheckpointed. SM-08 community Help Center GTM removal is committed as5619cc3and exact post-commit independent reviewdeleg_dd3db5b7passed; 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..ced77afchanged one controller line. The root cause was rawGlobalConfig.get(...).merge(app_config)allowing an unrequestedCLOUD_ANALYTICS_TOKENfrom a stubbed/configured hash to reach serializedwindow.globalConfig; the remediation applies.slice(*GLOBAL_CONFIG_KEYS)before the explicitapp_configmerge. Raw merge/sliced-allowlist reproductions were RED/GREEN, committed Ruby syntax andgit diff --checkpassed, 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_5e2578c5and frontend slicedeleg_ee3e217fboth 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
rspecexecutable). 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_SCRIPTSreader 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..d9bf4c4is verified by two fresh read-only reviews: backenddeleg_5e2578c5and frontenddeleg_ee3e217f; both returnedpassed=true,security_concerns=[], andlogic_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) removedCLOUD_ANALYTICS_TOKENfrom the requested config keys but leftGlobalConfig.get(*GLOBAL_CONFIG_KEYS).merge(app_config)vulnerable to unrelated keys returned by a stub/config source being serialized throughwindow.globalConfig. - The regression was reproduced with
analytics-test-token; the one-line remediation inced77afapplies.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_9b0a2f63overa96b977..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.