Files
resolutionflow/legal/implementation-verification.md
Michael Chihlas 41f5519916
All checks were successful
Mirror to GitHub / mirror (push) Successful in 6s
docs(legal): add baseline legal documents (privacy, ToS, DPA, subprocessors, cookies)
Generated by the resolutionflow-legal skill from a code scan of the FastAPI
backend + React frontend on commit 0564646. Each document is a starting
point for attorney review, not legal advice.

Includes:
- privacy-policy.md, terms-of-service.md, cookie-policy.md (public-facing)
- dpa.md (contractual; signed with MSP customers)
- subprocessor-list.md (Railway, Anthropic, Voyage, Stripe, Resend, Sentry,
  PostHog, Google Fonts — confirmed live as of scan)
- data-inventory.md + classification.md (Phase 1/2 working files)
- attorney-review-checklist.md (consolidated [LEGAL REVIEW] punch list)
- implementation-verification.md (claim-by-claim audit vs. actual code)

Three blocking issues filed before public publication:
- #175 deletion-on-offboarding (or rewrite retention claims)
- #176 narrow Sentry send_default_pii + Session Replay config
- #177 EU/UK consent for PostHog + Google Fonts

Public-facing documents intentionally route physical-mail requests through
support@ rather than publishing the LLC's registered address.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 12:51:19 -04:00

11 KiB

Implementation Verification

Generated: 2026-05-14 Scanned commit: 0564646 on feat/public-landing-routing-refactor

This document checks every concrete claim in the generated legal documents against what the code actually does. Each row is marked:

  • Confirmed — code clearly supports the claim
  • ⚠️ Partial — the code supports a narrower or related claim; the language is acceptable but tighten if possible
  • Not implemented — the claim is aspirational; either build it or rewrite the claim
  • Cannot verify in scan — depends on a runtime config, deployment posture, or external attestation the scan can't reach

A claim that overpromises is worse than one that underpromises. Anything must be resolved (built or rewritten) before publication.


Privacy Policy

Claim Source in docs Reality Verdict
Passwords are bcrypt-hashed with 12 rounds; plaintext never stored §3.1, §9 BCRYPT_ROUNDS=12 (config.py:86); User.password_hash (user.py:36)
PSA integration credentials encrypted at the application layer using Fernet (AES-128-CBC + HMAC), key derived via HKDF from SECRET_KEY §3.1, §9; DPA Annex B.1 encryption.py
TLS for production traffic §9; DPA Annex B.1 Hosted at api.resolutionflow.com / resolutionflow.com via Railway with HTTPS (depends on Railway domain config; verify)
Tenant isolation enforced by PostgreSQL row-level security §9; DPA Annex B.2 / B.5 RLS referenced in PROJECT_CONTEXT.md:206 as "Phase 4 RLS"; account_id scoping pervasive
Access tokens stored in localStorage rather than HTTP-only cookies §9 Confirmed in authStore.ts:47-48, OAuthCallbackPage.tsx:100-101
5-minute access tokens, idle 3d / absolute 14d refresh defaults §6 retention table; Cookie Policy §2.1 config.py:69-79
Account deletion soft-deletes the user and revokes refresh tokens; account-scoped content not automatically purged §6 (drafted as a [LEGAL REVIEW] flag) accounts.py:524-567 — confirms the soft-delete + token revoke; no purge of audit_logs, ai_sessions, etc. ⚠️ disclosed accurately as a flagged gap; if you intend to claim "we delete your data"
AI flow-builder wizard conversations purged 24h after creation §6 retention scheduler.py:118-136, hourly job
Assistant chat threads retained 90 days OR 100-chat cap (account-configurable), pinned exempt §6 retention retention_cleanup.py; defaults in account.py:40-45
AI chat sessions auto-archived after 30 days idle §6 retention main.py:45-63 (note: archived, not deleted — disclosed accurately)
Audit logs retention §6 (flagged) No purge job — indefinite — fix or rewrite
Refresh-token row cleanup §6 retention Rows persist after expiry/revoke — fix or rewrite (data-inventory open item)
Email-verification / password-reset token cleanup §6 retention Rows persist after expiry/use — fix or rewrite
File-upload deletion on account deletion §6 retention file_uploads rows + Railway Object Storage objects retained — fix or rewrite
Stripe never sees full card data; we hold only Stripe customer/subscription IDs §3.4; Subprocessor List Stripe row @stripe/stripe-js on frontend (Elements pattern); backend stores stripe_customer_id, stripe_subscription_id only (account.py:28, subscription.py)
PostHog initialized with persistence: 'localStorage+cookie'; identified by user.id, grouped by account_id; US instance §3.2; Cookie Policy §2.3 main.tsx:17-23; analytics.ts:34-40
Sentry: backend send_default_pii=True; replay 1%/100% with text + media unmasked §3.2 (disclosed); Subprocessor List main.py:14-26; instrument.ts:9-12 (disclosed accurately; ⚠️ recommend narrowing — see Attorney Checklist A2)
Anthropic is the sole live LLM provider §5.1; Subprocessor List AI_PROVIDER='anthropic' (config.py:159); user-confirmed Gemini not provisioned
Voyage AI is the live embedding provider Subprocessor List VOYAGE_API_KEY, EMBEDDING_MODEL='voyage-3.5' (config.py:219-221); user-confirmed key set
No model training on Customer Data (Anthropic, Voyage) ToS §3.4; Subprocessor List Public terms commitment of each subprocessor; not enforceable from our side — re-verify subprocessor terms before each publish
Resend is the transactional email provider; address invites@resolutionflow.com Subprocessor List config.py:97-99
Google Fonts loaded over CDN → IP exposed to Google §5.1; Subprocessor List; Cookie Policy §2.5 index.html:11-13
Microsoft Learn MCP retrieves public docs only; no Customer Data egress Subprocessor List "What is NOT" ENABLE_MCP_MICROSOFT_LEARN=True (config.py:216); the MCP search query string is the only outbound payload ⚠️ partial — the query string itself can include AI-session context. Disclosed at a high level; if Customer Data text could be substantively included in a query, consider listing MS Learn as a subprocessor.
Backup retention 90 days §9 backup language; DPA §6.3 User-stated target; depends on Railway PITR window configuration — verify Railway PITR configuration matches

Terms of Service

Claim Source Reality Verdict
Owner, admin, engineer, viewer role hierarchy; team-admin gate separately §2.3 permissions.py, User.account_role (user.py:25-52)
Only owner can delete the account; deletion blocked if other members remain §9.2 accounts.py:524-548
Removed members are moved to a personal account on the free tier §2.3 accounts.py:231-254
ConnectWise PSA integration available §1, §3.1, §8 services/psa/connectwise/; only live PSA provider per user
AI features integrate Anthropic; outputs may include errors §4.2 Code confirms Anthropic integration; honest disclosure
30-day export window post-termination §9.4 No automated export-window enforcement in code — needs implementation or rewrite
Stripe handles payment processing §5.3 @stripe/stripe-js + STRIPE_* env vars
Auto-renewal of subscriptions §5.2 Stripe Subscriptions semantics
30-day notice for price changes §5.5 Operational commitment; not code-enforced — operational
MFA disclosure (not required) (Privacy Policy §9 — accurate omission) No MFA code path detected

DPA

Claim Source Reality Verdict
Application-layer encryption for PSA credentials Annex B.1 Confirmed (above)
RLS for tenant isolation Annex B.2/B.5 Confirmed (above)
Authorized sub-processors list matches reality Annex C Matches Subprocessor List (Anthropic, Voyage, Stripe, Resend, Sentry, PostHog, Railway, Google Fonts)
72-hour breach notification SLA §3.7 Operational commitment — define an internal detection-to-notify procedure to make this credible
Audit reports (SOC 2) available §3.8.1 No SOC 2 today ⚠️ language says "when available," which is honest
Customer Data deleted after 30-day export window §6.2 Not implemented — see Privacy Policy table above — flagged in Attorney Checklist A1
90-day backup retention §6.3 User-stated; depends on Railway PITR config
SCC Module 2 / Module 3 incorporation §5.1 + Annex D Drafting only — no Customer signed instance yet — operational

Subprocessor List

Subprocessor Listed correctly? Notes
Railway Hosting + DB + Object Storage all in one entry
Anthropic LLM API for FlowPilot and AI features
Voyage AI Embedding provider; confirm DPA URL with attorney
Stripe Payment processor
Resend Transactional email
Sentry Error + Session Replay; see A2 about config
PostHog Product analytics; US instance
Google Fonts Disclosed; consider self-hosting (A3)
Gemini / Google AI Omitted (correct) Not provisioned in prod
OpenAI Omitted (correct) Not detected
Autotask, HaloPSA Omitted (correct) Not live
ConnectWise Disclosed as non-subprocessor (correct) Customer-controlled data source
Microsoft Learn MCP Disclosed as non-subprocessor Verified: doc-retrieval only

Item Reality Verdict
access_token and refresh_token in localStorage authStore.ts:47-48, 86-87 and others
theme-storage, rf-editor-fullscreen, rf-intended-plan, recentFlows, step-feedback flag, rated-sessions, escalation-queue seen All confirmed by grep
ph_* cookie set by PostHog due to persistence: 'localStorage+cookie' main.tsx:17-23
Sentry described as telemetry-only, not cookie-setting Default Sentry browser SDK behavior matches description
Google Fonts disclosed index.html:11-13
Consent mechanism for EU/UK Not implemented — see Attorney Checklist A3

Net verdict

Safe to share with an attorney as a starting draft. Do not publish to the public website until the items marked are resolved by either:

  1. Building the missing behavior (recommended path for A1 deletion-on-offboarding, A3 consent banner, A2 Sentry config tightening), OR
  2. Rewriting the relevant paragraph to describe the actual behavior with no overclaim.

The factual scaffolding (subprocessors, encryption posture, retention reality, cookie inventory) is accurate. The remaining work is commercial-risk calibration and a small number of high-priority implementation gaps.