Files
resolutionflow/CLAUDE.md
2026-03-18 03:34:23 +00:00

26 KiB
Raw Blame History

CLAUDE.md - Patherly / ResolutionFlow Project Context

Last Updated: March 16, 2026


Project Overview

Patherly (user-facing brand: ResolutionFlow) is a SaaS product for MSP professionals. It provides troubleshooting decision trees that guide engineers through proven troubleshooting paths, capture decisions and notes, and generate professional ticket documentation.

Target Market: MSP companies — IT service providers managing infrastructure and support for multiple clients.

SaaS Context: Multi-tenant design — teams represent MSP companies, trees shared within teams, tiered access (super_admin, team_admin, engineer, viewer).

Branding

Context Name Used
Repository / directory / database / Docker patherly / patherly_postgres
Backend, frontend UI, production URLs ResolutionFlow
  • Design: Dark glassmorphism with ice-cyan accent gradient (#06b6d4#22d3ee). Charcoal backgrounds, frosted-glass cards with backdrop-filter: blur(), orchestrated page-load animations, bold display typography. Design doc: docs/plans/2026-03-03-aesthetic-redesign-design.md
  • Fonts: Bricolage Grotesque (font-heading, headings/titles), IBM Plex Sans (font-sans, body text), JetBrains Mono (font-label, labels/badges/timestamps) — loaded via Google Fonts
  • Logo: Inline SVG in BrandLogo.tsx (decision-tree icon with cyan gradient). Wordmark: "Resolution" in text-foreground + "Flow" in text-gradient-brand
  • Brand assets: brand-assets/ (source SVGs + brand-guide.html), frontend/src/assets/brand/ (app assets), frontend/public/icons/ (favicon)
  • CSS utilities: text-gradient-brand, bg-gradient-brand, bg-gradient-brand-hover (defined in tailwind.config.js and index.css). Glass utilities: .glass-card (interactive, scale(1.02) hover), .glass-card-static (no hover transform), .active-glow (breathing cyan shadow)
  • Layout: App shell with persistent sidebar + top bar + main content (CSS Grid). Two fixed atmosphere orbs (cyan top-right, purple bottom-left) behind the shell for ambient glow. See UI-DESIGN-SYSTEM.md
  • Navigation: Sidebar nav with type sub-items (All Flows → Troubleshooting / Projects / Maintenance). Pinned flows section for quick access. NO workspace switcher. See UI-DESIGN-SYSTEM.md
  • Terminology: User-facing label is "Flows" (not "Trees"). Procedural flows are called "Projects" in the UI. Maintenance flows are called "Maintenance" in the UI. tree_type column values unchanged in DB.
  • Rebrand guide: REBRAND-IMPLEMENTATION-GUIDE.md

Component styling rules:

  • Primary buttons: bg-gradient-brand (cyan 135deg) with shadow-lg shadow-primary/20, hover opacity-0.9, active scale(0.97)
  • Secondary buttons: bg-[rgba(255,255,255,0.04)] with border-[rgba(255,255,255,0.06)], hover brightens border
  • Active nav items: bg-primary/10 background + 3px left cyan gradient accent bar
  • Stat values: use text-gradient-brand for highlighted metrics
  • Status colors: emerald-400 (success), amber-400 (in-progress), rose-500 (error/critical)
  • Category dots: 8px colored circles using the category color palette
  • Tags/badges: font-label (JetBrains Mono), small rounded chips with bg-card border-border
  • Cards: .glass-card (interactive) or .glass-card-static (non-interactive) — semi-transparent bg with backdrop-filter: blur(16px), border-radius: 16px
  • Section labels: font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground

When adding new pages/components: use "ResolutionFlow" branding, ice-cyan gradient accent theme, .glass-card / .glass-card-static containers, text-foreground/text-muted-foreground hierarchy. Primary actions use bg-gradient-brand. Pages render inside the app shell (CSS Grid: topbar + sidebar + main). Use "Flows" not "Trees" in all user-facing text; use "Projects" not "Procedures" for procedural flows. Reference UI-DESIGN-SYSTEM.md for layout patterns, navigation, and component specs.

Implementation Principles

  • Prefer correct architecture over minimal diff
  • If two approaches exist, implement the one that scales, not the one that's faster to write
  • Flag any "simpler approach" tradeoffs for product owner review before proceeding

Current State

  • Phase: Phase 3 - PSA Integration (In Progress)
  • Backend: Complete (35+ API endpoints, 100+ integration tests)
  • Frontend: Core features complete, Tree Editor functional
  • Database: PostgreSQL with Docker, 75 migrations
  • Detailed status: CURRENT-STATE.md

What's In Progress

  • ConnectWise PSA Integration (ticket linking, note posting, member mapping, status updates)

Recently Completed

  • Step Library Foundation
  • AI chat session conclusion: outcome tracking, AI-generated ticket summaries, resume flow
  • Survey completion: email-to-self, thank-you page, admin read/unread/archive/delete management
  • Survey system: public survey page, admin invite tracking, response viewer with CSV export
  • Email verification: tokens, banner, admin toggle (platform setting)
  • AI assistant: in-session copilot panel, standalone chat with RAG
  • Slate & Ice aesthetic redesign: glassmorphism, brand fonts, orchestrated animations
  • Account management: profile settings, delete/leave/transfer, chat retention
  • Maintenance flows: batch session launch, saved target lists, APScheduler scheduling

Tech Stack

Backend

  • Framework: Python FastAPI
  • Database: PostgreSQL 16 (async via SQLAlchemy 2.0 + asyncpg)
  • Migrations: Alembic
  • Auth: JWT (python-jose) + bcrypt, refresh token rotation (JTI-based)
  • Validation: Pydantic v2
  • Scheduling: APScheduler 3.x (async, in-process with FastAPI lifespan) + croniter + pytz

Frontend

  • Framework: React 19 + Vite + TypeScript
  • Styling: Tailwind CSS v3 — dark-first with purple gradient accents (see Branding section)
  • State: Zustand (with immer + zundo for undo/redo)
  • Routing: React Router v7
  • API Client: Axios with token refresh interceptor
  • Icons: Lucide React

Key Project Structure

patherly/
├── backend/
│   ├── app/
│   │   ├── main.py                 # FastAPI entry point
│   │   ├── api/endpoints/          # Route handlers (auth, trees, sessions, admin, steps, survey, copilot, assistant_chat, psa_connections)
│   │   ├── api/deps.py             # Auth dependencies
│   │   ├── api/router.py           # Route registration
│   │   ├── core/                   # config, database, permissions, security, audit, rate_limit
│   │   ├── models/                 # SQLAlchemy models
│   │   ├── schemas/                # Pydantic schemas
│   │   └── services/psa/           # PSA provider abstraction (base, connectwise/, cache, encryption, registry, types)
│   ├── alembic/                    # Database migrations (001-029+)
│   ├── scripts/                    # seed_data.py, seed_trees.py
│   └── tests/                      # pytest integration tests
├── frontend/
│   ├── src/
│   │   ├── api/                    # Axios client + endpoint modules
│   │   ├── components/             # common, layout, tree-editor, session, procedural, procedural-editor, library, step-library, ui
│   │   ├── hooks/                  # usePermissions, useSessionTimer, useKeyboardShortcuts
│   │   ├── pages/                  # All page components
│   │   ├── store/                  # Zustand stores (auth, treeEditor, proceduralEditor, userPreferences)
│   │   └── types/                  # TypeScript interfaces
│   └── tailwind.config.js
├── docs/plans/archive/             # Archived design/impl docs (pre-March 2026)
├── CLAUDE.md                       # This file
├── CURRENT-STATE.md                # Detailed feature status
├── LESSONS-LEARNED.md              # (Deprecated — consolidated into CLAUDE.md)
└── docs/plans/                     # Design docs & implementation plans

Environment Variables

Backend (backend/.env)

APP_NAME=ResolutionFlow
DEBUG=true
DATABASE_URL=postgresql+asyncpg://postgres:postgres@localhost:5432/patherly
DATABASE_URL_SYNC=postgresql://postgres:postgres@localhost:5432/patherly
SECRET_KEY=<openssl rand -hex 32>
ACCESS_TOKEN_EXPIRE_MINUTES=5
REFRESH_TOKEN_EXPIRE_DAYS=7
REQUIRE_INVITE_CODE=true

Frontend (frontend/.env.local - optional)

VITE_API_URL=http://localhost:8000

ConnectWise PSA Integration

ResolutionFlow integrates with ConnectWise PSA (formerly Manage) as the primary PSA integration. All ConnectWise API reference materials live in docs/connectwise/.

Best Practices Documentation

Official ConnectWise developer guides live in docs/connectwise/best-practices/. Read these BEFORE implementing any CW API integration code:

  • PSA-API-Requests.md — HTTP methods, response codes, condition query syntax, PATCH format, URL encoding, partial responses, custom fields. READ FIRST.
  • PSA-Callbacks.md — Callback type/level matrix, retry behavior, URL parameter gotcha, HMAC signature verification.
  • PSA-Pagination.md — Navigable vs Forward-Only pagination, Link headers, while-loop pattern.
  • PSA-Service-Tickets.md — Ticket field philosophy, recommended field mappings.
  • PSA-Versioning.md — Pin API version via Accept header. Use application/vnd.connectwise.com+json; version=2025.16.
  • PSA-Cloud-URL-Formatting.md — Dynamic base URL construction via /login/companyinfo/{companyId}.
  • Bundled-Requests.md — Batch multiple API calls into one request via /system/bundles.
  • PSA-Markdown.md — Ticket notes support markdown. Format session documentation output accordingly.
  • PSA-Company-Synchronization.md — Filter companies by Status/Type for mapping UI.
  • PSA-Data-Protection.md — Security role model, request minimal permissions (MY not ALL).

Reference Files (read in this order)

  1. docs/connectwise/CONNECTWISE-API-REFERENCE.md — Read FIRST. Quick reference covering auth patterns, tiered endpoint map, key field mappings, and integration architecture flows.
  2. docs/connectwise/connectwise-psa-resolutionflow-reference.json — Extracted OpenAPI 3.0.1 spec (v2025.16) with only the 670 endpoints and 342 schemas relevant to ResolutionFlow. Use for exact field types, request/response shapes, and parameter details.
  3. docs/connectwise/connectwise-psa-openapi-full.json — Complete ConnectWise PSA OpenAPI spec (1838 endpoints, 842 schemas). Only consult if you need an endpoint outside the extracted subset.

Integration Architecture

  • Session → Ticket Notes: Post auto-generated session documentation to ConnectWise tickets as internal analysis notes via POST /service/tickets/{id}/notes
  • Ticket Context → Session Runner: Pull ticket details, company info, and attached configurations to give FlowPilot AI real-world context
  • Callbacks: Register webhooks via /system/callbacks for real-time ticket event notifications to suggest relevant Flows

Key Implementation Rules

  • Auth: API Key auth (Base64 of companyId+publicKey:privateKey) + clientId header on every request
  • clientId is server-side config (CW_CLIENT_ID in config.py) — identifies the ResolutionFlow app, NOT per-tenant. Per-connection credentials: company_id, public_key, private_key, server_url
  • All PSA integration code in services/psa/ — provider pattern with BasePsaProvider abstract class, ConnectWiseProvider implementation, PsaProviderRegistry for multi-PSA dispatch
  • PSA endpoints in api/endpoints/psa_connections.py — connection CRUD, ticket ops, member mapping
  • Credentials encrypted at rest via services/psa/encryption.py (Fernet)
  • Each MSP tenant provides their own CW credentials — ResolutionFlow stores these per-team, never per-user
  • Design for the Autotask integration following the same service layer pattern (future PSA)
  • In-memory TTL cache in services/psa/cache.py for board/status/priority lookups
  • Respect CW API: paginate with max 1000 per page, handle retries gracefully

Development Commands

# Start PostgreSQL
docker start patherly_postgres

# Backend (from backend/)
source venv/bin/activate        # Linux/Mac
# .\venv\Scripts\Activate       # Windows
uvicorn app.main:app --reload

# Frontend (from frontend/)
npm run dev

# Run tests (from backend/)
pytest --override-ini="addopts="

# First time only: create test database
docker exec -it patherly_postgres psql -U postgres -c "CREATE DATABASE patherly_test;"

# Frontend build (IMPORTANT: stricter than tsc --noEmit — always use as final check)
cd frontend && npm run build

# Database migrations
cd backend && alembic upgrade head
alembic revision --autogenerate -m "Description" --rev-id=NNN  # NNN = next sequential number

# Access PostgreSQL
docker exec -it patherly_postgres psql -U postgres -d patherly

# Seed data
cd backend && pip install httpx && python -m scripts.seed_trees

# CI/CD debugging
gh run list --limit 5                          # Recent CI runs
gh run view <id> --log-failed                  # Failed job logs
gh run watch <id> --exit-status                # Watch run until complete
gh run view <id> --json jobs --jq '.jobs[] | {name: .name, conclusion: .conclusion}'

URLs

Test Users (seeded via scripts/seed_test_users.py)

  • All share password: TestPass123!
  • admin@resolutionflow.example.com (super_admin), teamadmin@resolutionflow.example.com (team_admin), engineer@resolutionflow.example.com (engineer), pro@resolutionflow.example.com (solo pro)

Critical Lessons Learned

Lessons 1-40 archived to docs/LESSONS-ARCHIVE.md — fixes are baked into the codebase. Consult if you hit a regression.

Active Lessons (41+)

41. Assistant chat uses local React state, not Zustand: AssistantChatPage.tsx uses useState for chats, messages, input, loading. No store.

42. Public pages use raw fetch(), not apiClient: Survey, shared sessions, and no-auth pages use fetch() with full URL. apiClient requires auth tokens.

43. Adding new email types: Add static async method to EmailService in core/email.py. Fire-and-forget from endpoints (log errors, don't fail).

44. AI Chat Builder is flow-type-aware: ai_chat_service.py dispatches by flow_type. Troubleshooting: [TREE_UPDATE] markers. Procedural: [STEPS_UPDATE] markers. Both support [METADATA].

45. Intake form field schema: Uses variable_name and field_type (NOT name and type).

46. CreateFlowDropdown uses AIPromptDialog: Opens prompt modal, starts AI session, generates flow, navigates to editor with { state: { aiPanelOpen: true, sessionId } }.

47. Editor-Embedded Flow Assist: EditorAIPanel (320px side panel) + useEditorAI hook. Ghost nodes use _suggestion: true flag. Actions route to model tiers via settings.get_model_for_action(). Delta responses use [DELTA]...[/DELTA] markers.

48. Tree orphan validation uses dynamic root ID: Orphan check compares against state.treeStructure?.id (NOT hardcoded 'root').

49. Full-stack features — verify both ends: Check the full data flow: schema → endpoint → API client → hook → store → UI.

50. Anthropic SDK retry: Set max_retries=1 to fail fast. Default max_retries=2 can take 3× timeout.

51. AI model tier routing: Use settings.get_model_for_action(action_type). Model IDs: use alias form (claude-sonnet-4-6).

52. Mobile scroll-to-top: Use ref.current.scrollIntoView(), not window.scrollTo(). Trigger via useEffect.

53. Flex height chain: Every ancestor must be a flex container for flex-1 to work. Missing flex class collapses React Flow to 0 height.

54. React Flow CSS in Tailwind v4: Import in index.css, not component JS. Override dark theme using --xy-* CSS custom properties.

55. App shell height chain: Every wrapper between .main-content and canvas needs flex + flex-1 + min-h-0 or h-full.

56. Railway backend service name is patherly: Production DB name is railway. Public Postgres proxy: interchange.proxy.rlwy.net:45797.

57. Node field priority: titlequestiondescriptioncontentlabel. See copilot_service.py.

58. scriptGeneratorStore.generate() optional param: Always wrap: onClick={() => generate()}, never onClick={generate}.

59. ConnectWise clientId is server-side config: Set in config.py as CW_CLIENT_ID. Per-connection: company_id, public_key, private_key, server_url.

60. Dockerfile build args for Vite env vars: Any new VITE_* or VITE_PUBLIC_* env var must be added as ARG + ENV in frontend/Dockerfile for Railway deploys. Railway env vars are runtime-only unless explicitly passed through as Docker build args. Without this, import.meta.env.VITE_* resolves to undefined in production builds.

61. Procedural sessions auto-start on page load: ProceduralNavigationPage calls startSession() immediately in loadTree() — there is no intake form screen or "Start" button. Variables are filled inline during execution. Troubleshooting flows DO have a start screen with ticket/client fields. Don't write tests or UI that assume a Start button on procedural flows.

62. Playwright strict mode — scope selectors to avoid ambiguity: Step titles appear in both the sidebar checklist and main content heading. Use getByRole('heading', { name }) for the main content, or scope with page.locator('.animate-scale-in') for command palette items. getByText() frequently matches multiple elements due to the sidebar + main content layout.

63. Node 20 required for frontend builds: Vite 7+ requires Node 20.19+. The system Node may be v18; use nvm: export NVM_DIR="$HOME/.nvm" && source "$NVM_DIR/nvm.sh" && nvm use 20. For direct binary access without nvm sourcing: PATH="/home/michaelchihlas/.nvm/versions/node/v20.19.0/bin:$PATH".

64. PostHog product analytics: Initialized via PostHogProvider in main.tsx with explicit posthog.init() + client prop pattern. Event helpers in lib/analytics.ts — use analytics.eventName(props) to track. identifyUser() called in authStore.fetchUser(), resetAnalytics() on logout. Env vars: VITE_PUBLIC_POSTHOG_KEY, VITE_PUBLIC_POSTHOG_HOST. Autocapture enabled.

65. Local Docker Compose uses resolutionflow database on port 5433: Container name is resolutionflow_postgres, database is resolutionflow (not patherly), port mapped to 5433 (not 5432). The POSTGRES_PORT env var controls this. Playwright config defaults must match: postgresql+asyncpg://postgres:postgres@127.0.0.1:5433/resolutionflow.

66. Dev environment runs on devserver01 (192.168.0.9), not localhost: Code-server runs in Docker on a LAN server. Frontend/backend are accessed via 192.168.0.9, not localhost. CORS must include http://192.168.0.9:5173 in CORS_ORIGINS and FRONTEND_URL. Frontend .env must set VITE_API_URL=http://192.168.0.9:8000. See DEV-ENV.md for full setup, Docker config, networking, and known issues.


RBAC & Permissions

  • Role hierarchy: super_admin > team_admin > engineer > viewer
  • Team Admin: role='engineer' + is_team_admin=True + valid team_id
  • Backend deps: get_current_active_user(user, db) (any active + auto-downgrades expired trials), require_engineer_or_admin (blocks viewers), require_admin (super admin only)
  • Never use role == "admin" — use is_super_admin instead
  • Frontend: usePermissions() hook for all permission checks
  • Centralized: backend/app/core/permissions.py, frontend/src/hooks/usePermissions.ts

Design System (Slate & Ice Modern)

  • Theme: Dark glassmorphism with ice-cyan accent (#06b6d4#22d3ee). Uses .glass-card / .glass-card-static for card surfaces
  • Backgrounds: bg-background (#101114 page), glass surfaces use rgba(24, 26, 31, 0.55) with backdrop-filter: blur()
  • Cards: .glass-card (interactive, hover scale(1.02) + border/shadow upgrade) or .glass-card-static (no hover). Both have border-radius: 16px, semi-transparent bg, backdrop blur
  • Buttons: Primary: bg-gradient-brand text-[#101114] font-semibold rounded-[10px] hover:opacity-90 active:scale-[0.97]. Secondary: bg-[rgba(255,255,255,0.04)] border-[rgba(255,255,255,0.06)] text-foreground rounded-[10px]
  • Inputs: border-border bg-card text-foreground placeholder:text-muted-foreground + focus: focus:border-[rgba(6,182,212,0.3)]
  • Text: text-foreground (#f8fafc) → text-muted-foreground (#8891a0) → text-[#5a6170] (dim, for section labels/timestamps)
  • Borders: var(--glass-border) (rgba(255,255,255,0.06)) default, rgba(255,255,255,0.12) on hover
  • Hover states: Border brightens to rgba(255,255,255,0.12), shadow upgrades to --shadow-float-hover
  • Active/selected: bg-primary/10 text-foreground or cyan gradient accent bar
  • Functional colors: emerald-400 (success), rose-500 (error), amber-400 (warning), blue-400 (info). Always pair with icons, not color alone.
  • CSS variables: Glass system vars (--glass-bg, --glass-border, --glass-blur), shadow system (--shadow-float, --shadow-float-hover, --shadow-cyan-glow), easing (--ease-out-smooth) — all in index.css :root
  • Animations: Orchestrated page-load sequence (slideDown, slideInLeft, fadeInUp cascade, fadeInRight). breatheGlow on first stat card. bellWobble on notification hover. See design doc for full spec.

Frontend Patterns

  • Component guidelines: Use cn() from @/lib/utils, Lucide icons (wrap in <span> for title), modals with fixed header/footer
  • Type organization: Create in types/, export from types/index.ts, import with import type { T } from '@/types'
  • Scratchpad overlay: position: fixed, onOpenChange callback for parent padding adjustment, right-2 positioning
  • Custom step flow: CustomStepModalPostStepActionModalContinuationModal → custom step view. Key state: pendingStep, pendingContinuationNodeId, customBranchMode, branchOriginNodeId. Use findCustomStep() not findNode() for custom step UUIDs.
  • Session sharing: ShareSessionModal manages share links, SharedSessionPage renders public/account views. Helper utils in lib/sessionShare.ts. Share URLs use /shared/sessions/:token.
  • Procedural navigation: ProceduralNavigationPage handles intake forms, step-by-step execution, and resume via location.state.sessionId. Uses StepChecklist, StepDetail, ProgressBar, CompletionSummary components.
  • Routing helper: Use getTreeNavigatePath() and getTreeEditorPath() from @/lib/routing for all tree/session navigation.
  • Account section layout: AccountLayout has NO sidebar nav. Account sub-pages (categories, target-lists) are reached via link cards on AccountSettingsPage.tsx. New account pages: add route in router.tsx under account children + add a link card in AccountSettingsPage.

Common Tasks

  • New endpoint: Create in endpoints/ → add to router.py → schema in schemas/ → tests → frontend API client
  • New page: Create in pages/ → add route in router.tsx → nav link in AppLayout.tsx
  • New public route (no auth): Add at top level in router.tsx alongside /login, /register — NOT inside the ProtectedRoute/AppLayout children.
  • Schema change: Update model → alembic revision --autogenerate -m "desc" → review → alembic upgrade head
  • New frontend API module: Types in types/ → export from types/index.ts → client in api/ → export from api/index.ts

Coding Standards

Python

  • Type hints everywhere, async/await for DB, Pydantic for validation, DateTime(timezone=True) always

TypeScript

  • Interfaces for all data, const over let, functional components + hooks, reusable logic in custom hooks

Git

  • Format: type: description (feat, fix, refactor, docs, test, chore)
  • Always include Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
  • Always create feature branch BEFORE committing: git checkout -b feat/feature-name
  • Large features: commit per phase with npm run build validation

After Completing Work

When a feature, fix, or significant piece of work is finished and merged/committed:

  1. Update CURRENT-STATE.md — move completed items, update "In Progress" and "What's Next" sections
  2. Update 03-DEVELOPMENT-ROADMAP.md — check off completed work, update phase status
  3. Close related GitHub Issues — use gh issue close #N for any issues resolved by the work
  4. Update CLAUDE.md if the work introduced new patterns, lessons learned, or changed project structure

Deployment (Railway)

  • Production: resolutionflow.com (frontend), api.resolutionflow.com (backend)
  • Auto-deploys on push to main
  • PR environments auto-created (need manual domain generation in Railway dashboard)
  • PR envs need VITE_API_URL set with https:// prefix on frontend service
  • ALLOW_RAILWAY_ORIGINS=true enables CORS for *.up.railway.app
  • Shared Variables (project-level in Railway dashboard) auto-propagate to all environments including PR envs — use for secrets like ANTHROPIC_API_KEY
  • Super admin utility: backend/make_superadmin_simple.py list|<email>

Future Roadmap

  • Phase 3: PSA integrations (ConnectWise in progress), file attachments, client context, analytics
  • Phase 4: Additional PSA integrations (Autotask/Kaseya), PowerShell automation, enterprise SSO

Quick Reference

What Where
API Docs http://localhost:8000/api/docs
Detailed Status CURRENT-STATE.md
Development Roadmap 03-DEVELOPMENT-ROADMAP.md
GitHub Issues gh issue list --state open
Bugs & Fixes CLAUDE.md → Critical Lessons Learned section
Feature Specs 04-FEATURE-SPECIFICATIONS.md
Design System docs/plans/Frontend/DESIGN_SYSTEM_GUIDE.md
Dev Environment DEV-ENV.md — devserver01 setup, Docker, CORS, networking