All checks were successful
Mirror to GitHub / mirror (push) Successful in 6s
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>
120 lines
11 KiB
Markdown
120 lines
11 KiB
Markdown
# 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](../backend/app/core/config.py#L86)); `User.password_hash` ([user.py:36](../backend/app/models/user.py#L36)) | ✅ |
|
|
| 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](../backend/app/services/psa/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](../.ai/PROJECT_CONTEXT.md#L206) 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](../frontend/src/store/authStore.ts#L47-L48), [OAuthCallbackPage.tsx:100-101](../frontend/src/pages/OAuthCallbackPage.tsx#L100-L101) | ✅ |
|
|
| 5-minute access tokens, idle 3d / absolute 14d refresh defaults | §6 retention table; Cookie Policy §2.1 | [config.py:69-79](../backend/app/core/config.py#L69-L79) | ✅ |
|
|
| 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](../backend/app/api/endpoints/accounts.py#L524-L567) — 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](../backend/app/core/scheduler.py#L118-L136), hourly job | ✅ |
|
|
| Assistant chat threads retained 90 days OR 100-chat cap (account-configurable), pinned exempt | §6 retention | [retention_cleanup.py](../backend/app/services/retention_cleanup.py); defaults in [account.py:40-45](../backend/app/models/account.py#L40-L45) | ✅ |
|
|
| AI chat sessions auto-archived after 30 days idle | §6 retention | [main.py:45-63](../backend/app/main.py#L45-L63) | ✅ (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](../backend/app/models/account.py#L28), [subscription.py](../backend/app/models/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](../frontend/src/main.tsx#L17-L23); [analytics.ts:34-40](../frontend/src/lib/analytics.ts#L34-L40) | ✅ |
|
|
| Sentry: backend `send_default_pii=True`; replay 1%/100% with text + media unmasked | §3.2 (disclosed); Subprocessor List | [main.py:14-26](../backend/app/main.py#L14-L26); [instrument.ts:9-12](../frontend/src/instrument.ts#L9-L12) | ✅ (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](../backend/app/core/config.py#L159)); 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](../backend/app/core/config.py#L219-L221)); 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](../backend/app/core/config.py#L97-L99) | ✅ |
|
|
| Google Fonts loaded over CDN → IP exposed to Google | §5.1; Subprocessor List; Cookie Policy §2.5 | [index.html:11-13](../frontend/index.html#L11-L13) | ✅ |
|
|
| Microsoft Learn MCP retrieves public docs only; no Customer Data egress | Subprocessor List "What is NOT" | `ENABLE_MCP_MICROSOFT_LEARN=True` ([config.py:216](../backend/app/core/config.py#L216)); 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](../backend/app/models/user.py#L25-L52)) | ✅ |
|
|
| Only owner can delete the account; deletion blocked if other members remain | §9.2 | [accounts.py:524-548](../backend/app/api/endpoints/accounts.py#L524-L548) | ✅ |
|
|
| Removed members are moved to a personal account on the free tier | §2.3 | [accounts.py:231-254](../backend/app/api/endpoints/accounts.py#L231-L254) | ✅ |
|
|
| 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 |
|
|
|
|
---
|
|
|
|
## Cookie Policy
|
|
|
|
| Item | Reality | Verdict |
|
|
|---|---|---|
|
|
| `access_token` and `refresh_token` in localStorage | [authStore.ts:47-48, 86-87](../frontend/src/store/authStore.ts) 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](../frontend/src/main.tsx#L17-L23) | ✅ |
|
|
| Sentry described as telemetry-only, not cookie-setting | Default Sentry browser SDK behavior matches description | ✅ |
|
|
| Google Fonts disclosed | [index.html:11-13](../frontend/index.html#L11-L13) | ✅ |
|
|
| 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.
|