This report documents a deep analysis of /Users/luolei/DEV/clash-master, including architecture, runtime behavior, code-level design patterns, edge cases, and operational specifics.
Neko Master is a pnpm monorepo for network traffic analytics and visualization.
apps/collector: backend data collector + API + WebSocket push server (Fastify + ws + SQLite)apps/web: Next.js dashboard frontend (App Router, next-intl, React Query)packages/shared: shared contracts/utilities used by both apps
Core capability: ingest traffic from Clash/Mihomo and Surge, aggregate into multi-dimensional stats, and expose near-real-time dashboards.
- Workspace config:
pnpm-workspace.yaml, rootpackage.json - Task orchestration:
turbo.json - Root scripts:
pnpm dev->turbo devpnpm build->turbo buildpnpm lint->turbo lint
builddepends on dependency graph (^build), cachesdist/**and.next/**(excluding.next/cache/**)devis non-cached and persistentlintalso respects dependency graph (^lint)- Global cache invalidation includes
**/.env.*local
Main orchestrator: apps/collector/src/index.ts
Single process hosts all of the following:
- Fastify HTTP API server (
APIServer) - Dedicated WebSocket server (
StatsWebSocketServer) - One collector instance per backend (Clash WS collector or Surge polling collector)
- GeoIP service (
GeoIPService) - Surge policy sync service (
SurgePolicySyncService)
Startup sequence in main():
- Load
.env.local(if present), then.env - Initialize SQLite facade (
StatsDatabase) - Initialize GeoIP service
- Start WS server (default
3002) - Start policy sync service
- Start Fastify API server (default
3001) - Start backend management loop immediately + every 5s
- Schedule retention cleanup (+30s once, then every 6h)
- Register graceful shutdown (
SIGINT/SIGTERM)
manageBackends() continuously reconciles DB backend configs with in-memory collectors:
- starts collectors for enabled + listening backends
- restarts collector when config changes (
url,token,type,listening,enabled) - stops collectors for deleted/disabled/non-listening backends
State maps are keyed by backendId, preserving backend isolation.
Implementation: apps/collector/src/collector.ts
- Connects to upstream
/connectionsWS endpoint - Tracks connection snapshots in
Map<id, TrackedConnection> - Per update, computes positive deltas (
max(0, current - previous)) - Ignores invalid/empty frames safely
- Removes stale missing IDs and periodically purges stale tracked connections
- Reconnect uses fixed interval behavior (no exponential retry)
Implementation: apps/collector/src/surge-collector.ts
- Polls
/v1/requests/recent(default interval ~2s) - Uses exponential retry backoff for request failures
- 4xx is treated as non-retryable class in retry path
- Tracks requests by ID and computes incremental byte deltas
- Handles counter reset edge case (
current < previous) - Uses
recentlyCompletedmap (TTL ~5 min) to avoid duplicate counting of completed requests - Parses policy/rule-chain metadata from Surge notes
Batch layer: apps/collector/src/batch-buffer.ts
- Buffers updates and flushes on:
- timer (
FLUSH_INTERVAL_MS, default 30000) - buffer size threshold (
FLUSH_MAX_BUFFER_SIZE, default 5000)
- timer (
- Flush writes both traffic and country data in batch form
Writer repository: apps/collector/src/database/repositories/traffic-writer.repository.ts
- Pre-aggregates in memory before SQL writes
- Writes to multiple aggregate/fact tables in transactional groups
Realtime interaction:
- Deltas are also pushed into in-memory
realtimeStore - After successful DB flush, relevant realtime deltas are cleared to avoid DB+memory double counting
- Collectors trigger WS broadcast callback with internal throttling
Core server assembly: apps/collector/src/app.ts
- Registers CORS, cookie plugin, service injections, and module controllers
- Controller prefixes:
/api/backends/api/stats/api/auth/api/db
- Adds compatibility routes directly in
app.ts:/api/gateway/proxies/api/gateway/providers/proxies/api/gateway/providers/proxies/refresh/api/gateway/rules/health
Pattern:
- Controllers are thin orchestration layers (parse + validate + delegate)
- Services host business logic
StatsDatabasefacade + repository classes host data access/transform logic
Auth service: apps/collector/src/modules/auth/auth.service.ts
- Token stored as SHA-256 hash in DB
- Runtime checks support:
- cookie (
neko-session) - bearer fallback (
Authorization: Bearer ...)
- cookie (
- Public routes skip auth checks (
/health,/api/auth/state,/api/auth/verify,/api/auth/logout)
Special modes:
FORCE_ACCESS_CONTROL_OFF=true: bypass auth requirementSHOWCASE_SITE_MODE=true: blocks many mutating endpoints and masks sensitive backend URL output
Cookie secret behavior:
- If
COOKIE_SECRETmissing, server auto-generates one - In production this causes sessions to invalidate on restart unless secret is persisted
Server: apps/collector/src/websocket.ts
Highlights:
- Dedicated WS server process inside collector runtime
- Auth via cookie or query token
- Incoming message types include
pingandsubscribe - Subscribe payload supports backend selection, time range, detail flags, pagination/trend options, and min push interval
- Initial data push on connect/subscription
- Broadcast globally throttled; per-client minimum interval also enforced
- Server-side response caching by query shape to avoid recomputation per client
- Base summary cache TTL differs by freshness (near-now short TTL, historical longer TTL)
Realtime merge policy:
- In-memory deltas are merged only for queries whose end-time is near “now” (
REALTIME_RANGE_END_TOLERANCE_MS)
Schema and migrations:
apps/collector/src/database/schema.tsapps/collector/src/db.ts
Design traits:
- SQLite with WAL and performance pragmas
- Backend-scoped analytics (
backend_idacross major tables) - Mix of cumulative aggregate tables + minute/hourly fact tables
Important query optimization behavior:
- Range queries choose minute or hourly fact table based on range span (commonly 6h threshold)
- DB range cache with TTL:
- near-now short TTL
- historical longer TTL
Retention:
- Retention config in DB (
app_config/ retention APIs) - Auto cleanup prunes old minute/hourly and related fact data
Service: apps/collector/src/geo-service.ts
Behavior:
- Private IP short-circuit to local classification
- DB cache first (
geoip_cache) - Dedupes concurrent lookups (
pendingQueries) - Cooldown for failed IPs to avoid hammering provider
- Queue + pacing controls for online lookups
Provider model:
- Configurable online/local provider
- Local uses MaxMind MMDB files (City + ASN required)
- Falls back to online when local unavailable
- Config endpoints expose configured vs effective provider and validate local prerequisites
Test setup:
- Vitest (
apps/collector/vitest.config.ts) - Helper utilities in
apps/collector/src/__tests__/helpers.ts - Temporary SQLite DB created per test suite cycle
Covered:
- auth service behavior
- stats service behavior
- traffic writer repository correctness
- geoip config/normalization behavior
Under-covered areas:
- startup orchestration in
index.ts - Clash/Surge collector runtime edge behavior
- WS protocol integration and auth edge cases
- backend lifecycle reconciliation loop race scenarios
- Manual route validation in controllers (limited centralized schema validation)
- Potential overlap risk: async
manageBackendsinvoked by timer without explicit single-flight guard - Legacy/parallel code paths exist in some module folders vs active top-level runtime files
- Data model contains denormalized comma-joined fields in places, which may degrade precision at scale
Key files:
apps/web/app/[locale]/layout.tsxapps/web/app/[locale]/page.tsxapps/web/app/[locale]/dashboard/page.tsxapps/web/proxy.tsapps/web/i18n/routing.tsapps/web/i18n/request.ts
Behavior:
- Locale-aware App Router (
en,zh; defaultzh) - Locale root page re-exports dashboard component directly
- next-intl middleware (
proxy.ts) handles locale routing with matcher exclusions
Core client: apps/web/lib/api.ts
- One typed API facade for all backend endpoints
- Runtime API base resolution order:
window.__RUNTIME_CONFIG__.API_URL- env (
NEXT_PUBLIC_API_URL/API_URL) /api
- GET inflight dedupe to avoid duplicate concurrent requests
- 401 triggers browser event (
api:unauthorized) for auth state sync - Uses
ApiErrorclass (apps/web/lib/api-error.ts)
React Query defaults:
apps/web/components/providers/query-provider.tsx- staleTime ~5s default, gcTime ~5m, retry 1
- Per-hook overrides in
hooks/api/*+lib/query-config.ts
Main orchestrator hook: apps/web/app/[locale]/dashboard/hooks/use-dashboard.ts
- Coordinates tab state, time range, backend selection, translations, auth awareness, and data fetching strategy
- Uses mixed strategy: HTTP query + websocket updates depending on tab and refresh mode
Composition:
dashboard/page.tsxcomposesSidebar,Header, andContentContentswitches feature modules by tab (overview/domains/countries/proxies/rules/devices/network)
Client implementation: apps/web/lib/websocket.ts
- Candidate endpoint strategy supports runtime/env-configured WS URL and inferred fallbacks
- Environment-sensitive ordering (production tends to try path-based
/_cm_wsfirst) - Heartbeat ping/pong + latency tracking
- Exponential reconnect and fallback endpoint rotation
- Rich
subscribepayload to control server response shape
Integration:
- Several feature modules consume websocket updates and merge into React Query cache for low-latency UX
Core auth state: apps/web/lib/auth.tsx
- Global provider determines whether auth is enabled and whether current session is valid
- Guard (
AuthGuard) displays login dialog when required - Logout clears session and reloads
Notable behavior:
- API unauthorized events trigger auth state downgrade
- Auth verification uses backend auth endpoints and protected probe calls
Key pieces:
- Tailwind v4 (
app/globals.css,postcss.config.mjs) - shadcn/ui config (
apps/web/components.json, stylenew-york) - Primitive UI components in
apps/web/components/ui/* - Theme runtime via next-themes provider and theme-color sync
- Selective framer-motion usage for key interactions
Files:
- Manifest route:
apps/web/app/manifest.ts - SW script:
apps/web/public/sw.js - SW runtime registration:
apps/web/components/common/sw-register.tsx - Next config conditional PWA plugin in production:
apps/web/next.config.ts
Specificity:
- There is both custom service-worker logic and production next-pwa integration, so behavior can differ across environments if not carefully tested.
- Multiple feature modules can establish websocket connections simultaneously depending on view usage
- Strong coupling to backend WS payload shape (
statsmessage optional fields) - Locale switching is path-string based; robust enough now but sensitive to future route shape changes
- Viewport config disables user scaling (accessibility tradeoff)
Primary file: packages/shared/src/index.ts
Provides:
- Shared domain contracts for stats, gateway payloads, websocket updates, auth, surge models
- Utility re-exports:
gateway-utils.tsgeo-ip-utils.ts
Important utilities:
buildGatewayHeaders: type-aware header construction for Clash/SurgeparseSurgeRule+parseGatewayRule: normalization/parsing helpersgetGatewayBaseUrl: normalizes WS/HTTP gateway base URL extractionnormalizeGeoIP: compatibility normalizer for mixed geo payload shapes
Compatibility note:
- Because apps depend on
workspace:*, shared contract changes are immediately global and should be treated as coordinated cross-app changes.
- API calls in browser usually target
/api/* - Next rewrite forwards
/api/:path*toAPI_URLdestination - Runtime config script (
public/runtime-config.js) supports deploy-time override without rebuild
Relevant files:
Dockerfiledocker-compose.ymldocker-start.sh.dockerignore
Notable behavior:
- startup script auto-generates/persists
COOKIE_SECRETin mounted data volume if missing - runtime config is generated/injected at container startup
- image build is multi-stage and multi-arch aware via CI workflow
Pre-push hook (.husky/pre-push):
- only enforced when pushing to
main - runs:
- collector type check
- web production build
GitHub Actions:
- Docker build/publish pipeline (
.github/workflows/docker-build.yml) - dev branch sync guard (
ensure-dev-synced-with-main.yml) - preview branch automation (
dev-preview-branch.yml)
Observation:
- CI gate is stronger on container buildability than lint/test depth.
Architecture docs are useful but partially stale in folder naming/module layout compared to current collector file structure. Use docs for conceptual understanding, but trust code paths for implementation-level decisions.
- Clear monorepo separation with shared contracts
- Good real-time architecture: DB persistence + in-memory delta merge + WS push
- Runtime-config-aware frontend deployment model
- Backend isolation by
backendIdis consistently embedded in data path
- Some runtime-critical paths (collector orchestration + WS protocol) are lightly tested
- Potential race/overlap patterns in periodic backend manager loops
- Mixed/legacy code paths can confuse contributors
- Strong frontend/backend contract coupling (especially WS payload shape)
- Shared types in
packages/sharedare cross-app contracts. - Collector uses ESM +
.jsextension for relative imports. - Web API base and WS endpoints can be runtime-overridden.
- Keep backend isolation (
backendId) intact in any new query/write path. - Validate changes in both realtime and non-realtime (HTTP fallback) modes.
- For auth/security changes, verify cookie + bearer compatibility paths.
- Build an endpoint matrix (HTTP + WS) with exact request/response schemas and source functions.
- Add a collector reliability review focused on reconnection/backoff consistency between Clash and Surge paths.
- Add a test-gap plan prioritizing
index.tsorchestrator andwebsocket.tsprotocol behavior.