# CSM - Enterprise Terminal Management System Design > **Date:** 2026-04-03 > **Status:** Draft > **Scope:** MVP (50-500 Windows terminals, on-premise deployment) ## 1. Overview CSM (Corporate System Manager) is a locally deployable enterprise terminal management system inspired by Tencent PC Manager Small Team Edition. It provides IT operations teams with centralized visibility and control over company Windows computers. ### Key Design Decisions | Decision | Choice | Rationale | | ------------------ | ------------------------------------ | ----------------------------------------------------------------------- | | System positioning | IT operations management | Compliance-friendly, employee-visible client, focused on ops efficiency | | Deployment scale | 50-500 terminals | Single-server deployment, monolithic architecture | | Target OS | Windows 7+ only | Simplifies client development (single platform API) | | Database | SQLite | Zero-ops, single-file, sufficient for small-scale | | Management UI | Web (embedded SPA) | Browser-based access from any device, no desktop app needed | | Notification | Email (SMTP) + Webhook | Fully local, no third-party dependencies | | Architecture | Axum monolith with embedded frontend | Single binary deployment, zero configuration | ### MVP Features (Phase 1) 1. **Terminal Status Monitoring** - CPU/memory/disk/process real-time monitoring, online/offline tracking 2. **USB Device Control** - USB event logging, whitelist/blacklist policies, read-only enforcement 3. **Asset Management** - Hardware/software inventory auto-collection, change tracking ### Plugin Modules (Phase 2 — Tencent Manager Parity) These modules are modeled after Tencent PC Manager Small Team Edition's plugin marketplace. Each is an independent module that can be enabled/disabled per device group. | # | Plugin | Description | Windows | Complexity | | - | ----------------------------- | --------------------------------------------------------------------------------- | ------- | ---------- | | 1 | **Web Filter (上网拦截)** | URL blacklist/whitelist, category-based filtering, browsing time control | ✅ | Medium | | 2 | **Usage Timer (时长记录)** | Track computer usage duration, application usage statistics, idle detection | ✅ | Low | | 3 | **Software Blocker (软件禁止安装)** | Blacklist-based software install prevention, unauthorized software auto-uninstall | ✅ | Medium | | 4 | **Popup Blocker (弹窗拦截)** | Intercept popup ads and notifications, configurable allow-list | ✅ | Low | | 5 | **USB File Audit (U盘文件操作记录)** | Log all file copy/delete/rename operations on USB storage devices | ✅ | Medium | | 6 | **Screen Watermark (水印管理)** | Display customizable screen watermark (company name, username, timestamp) | ✅ | Medium | ### Future Features (Phase 3+) - Software distribution (remote install/uninstall) - Scheduled antivirus scanning - Vulnerability patching - Remote desktop assistance - System announcements - Scheduled shutdown ## 2. System Architecture ### 2.1 Cargo Workspace Structure ``` csm/ ├── Cargo.toml # Workspace root ├── crates/ │ ├── protocol/ # Shared protocol definitions │ │ └── src/ │ │ ├── lib.rs │ │ ├── message.rs # Message types (enum-based) │ │ └── device.rs # Device data structures │ │ │ ├── client/ # Windows client (Service + System Tray) │ │ └── src/ │ │ ├── main.rs # Entry point + Windows Service registration │ │ ├── tray.rs # System tray icon │ │ ├── monitor/ # Status monitoring │ │ │ ├── mod.rs │ │ │ ├── system.rs # CPU/memory/disk/network via Windows API │ │ │ └── process.rs # Process enumeration │ │ ├── asset/ # Asset collection │ │ │ ├── mod.rs │ │ │ ├── hardware.rs # Hardware info (WMI queries) │ │ │ └── software.rs # Software list (registry reading) │ │ ├── usb/ # USB control │ │ │ ├── mod.rs │ │ │ ├── monitor.rs # WM_DEVICECHANGE listener │ │ │ └── policy.rs # Policy enforcement │ │ └── network/ # Communication │ │ ├── mod.rs │ │ ├── connection.rs # TCP + TLS connection │ │ └── heartbeat.rs # Keep-alive with exponential backoff │ │ │ └── server/ # Server (Axum + embedded frontend) │ └── src/ │ ├── main.rs # Entry point │ ├── api/ # REST API handlers │ │ ├── mod.rs │ │ ├── auth.rs # JWT authentication │ │ ├── devices.rs # Device CRUD + status queries │ │ ├── assets.rs # Asset queries + change history │ │ ├── usb.rs # USB policy management │ │ └── alerts.rs # Alert rules + records │ ├── ws/ # WebSocket real-time push │ │ ├── mod.rs │ │ └── handler.rs # Device status updates, USB events │ ├── tcp/ # Client connection management │ │ ├── mod.rs │ │ └── session.rs # Per-client session handler │ ├── db/ # Database layer │ │ ├── mod.rs │ │ ├── schema.rs # Refdb migration definitions │ │ └── repository.rs # Query functions │ ├── alert/ # Alert engine │ │ ├── mod.rs │ │ ├── engine.rs # Rule matching against incoming events │ │ └── notify.rs # Email (lettre) + Webhook dispatch │ └── config.rs # TOML-based configuration │ ├── web/ # Vue.js management frontend │ ├── package.json │ ├── vite.config.ts │ └── src/ │ ├── views/ │ │ ├── Dashboard.vue # Overview: online stats, alerts, recent events │ │ ├── Devices.vue # Device list with real-time status │ │ ├── DeviceDetail.vue # Single device detail (hardware, software, history) │ │ ├── Assets.vue # Asset inventory and change log │ │ ├── UsbPolicy.vue # USB policy configuration │ │ ├── UsbEvents.vue # USB event timeline │ │ ├── Alerts.vue # Alert center │ │ ├── AlertRules.vue # Alert rule configuration │ │ ├── Login.vue # Authentication │ │ └── Settings.vue # System settings (notifications, users) │ ├── components/ │ │ ├── DeviceStatusBadge.vue │ │ ├── CpuChart.vue │ │ ├── MemoryChart.vue │ │ └── AlertCard.vue │ ├── stores/ # Pinia state management │ │ ├── auth.ts │ │ ├── devices.ts │ │ └── alerts.ts │ └── router/ │ └── index.ts │ ├── migrations/ # SQLite migration SQL files │ ├── 001_init.sql # Users, devices, device_status │ ├── 002_assets.sql # Hardware/software assets, asset_changes │ ├── 003_usb.sql # USB events, USB policies │ ├── 004_alerts.sql # Alert rules, alert records │ ├── 005_plugins_web_filter.sql # Web filter rules + access log │ ├── 006_plugins_usage_timer.sql # Daily usage + app usage │ ├── 007_plugins_software_blocker.sql # Software blacklist + violations │ ├── 008_plugins_popup_blocker.sql # Popup filter rules │ ├── 009_plugins_usb_file_audit.sql # USB file operations log │ └── 010_plugins_watermark.sql # Watermark config │ └── docs/ ``` ### 2.2 Communication Architecture ``` Client (Windows Service, system tray visible) │ ├─ TCP+TLS persistent ──→ Server :9999 (client gateway) │ ├─ Heartbeat (30s interval, ~50 bytes each) │ ├─ StatusReport (every 60s, ~500 bytes JSON) │ ├─ AssetReport (on change or forced collection) │ ├─ UsbEvent (on device change notification) │ └─ Receives: PolicyUpdate, ConfigUpdate, TaskExecute │ └─ Initial registration ──→ POST /api/register (over HTTPS) Admin (Browser) │ ├─ HTTPS ──→ Server :8080 (web management) │ ├─ REST API (CRUD operations) │ └─ Static file serving (Vue.js SPA from embedded dist/) │ └─ WSS ──→ Server :8080/ws (real-time push) ├─ Device status updates (online/offline, metric changes) ├─ USB alerts (unauthorized device, policy violations) └─ New device registration notifications ``` ### 2.3 Single Binary Deployment The server binary embeds: - Vue.js SPA (compiled `dist/` via `include_dir!` macro from `include_dir` crate) - SQLite database (auto-created on first run) - Default configuration (overridable via `config.toml`) Deployment process: ```bash # 1. Copy single binary scp csm-server target-server:/usr/local/bin/ # 2. Create config cat > config.toml << EOF [server] http_addr = "0.0.0.0:8080" tcp_addr = "0.0.0.0:9999" [database] path = "./csm.db" [auth] jwt_secret = "change-me-in-production" [notify.smtp] host = "smtp.company.com" port = 587 username = "alerts@company.com" password = "secret" from = "CSM Alerts " EOF # 3. Run ./csm-server --config config.toml ``` ## 3. Communication Protocol ### 3.1 Binary Frame Format ``` ┌──────────┬──────────┬──────────┬──────────────┐ │ Magic(4B)│ Ver(1B) │ Type(1B) │ Length(4B) │ │ 0x43534D │ 0x01 │ │ payload size │ ├──────────┴──────────┴──────────┴──────────────┤ │ Payload (variable) │ │ JSON-encoded message body │ └─────────────────────────────────────────────────┘ ``` - Magic bytes: `0x43 0x53 0x4D` = "CSM" in ASCII - Protocol version: `0x01` (current). Both sides verify version during registration handshake. If versions differ, the server sends a `ConfigUpdate(ProtocolVersion)` with the highest common version, or rejects the connection if incompatible. ### 3.2 Message Types ```rust #[repr(u8)] pub enum MessageType { // Client → Server Heartbeat = 0x01, Register = 0x02, StatusReport = 0x03, AssetReport = 0x04, AssetChange = 0x05, UsbEvent = 0x06, AlertAck = 0x07, // Server → Client PolicyUpdate = 0x10, ConfigUpdate = 0x11, TaskExecute = 0x12, } ``` ### 3.3 Bandwidth Estimation (500 terminals) | Message | Frequency | Size | Bandwidth | | ------------------------ | --------- | ------ | ------------- | | Heartbeat | 30s | \~50B | \~8 KB/s | | StatusReport | 60s | \~500B | \~4 KB/s | | UsbEvent | On event | \~300B | Negligible | | AssetReport | On change | \~5KB | Negligible | | **Total (steady state)** |
|
| **\~12 KB/s** | ## 4. Database Schema (SQLite) ### 4.1 Core Tables **users** - Administrator accounts ```sql CREATE TABLE users ( id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT NOT NULL UNIQUE, password TEXT NOT NULL, -- bcrypt hash role TEXT NOT NULL DEFAULT 'admin', created_at TEXT NOT NULL DEFAULT (datetime('now')) ); ``` **devices** - Registered terminals ```sql CREATE TABLE devices ( id INTEGER PRIMARY KEY AUTOINCREMENT, device_uid TEXT NOT NULL UNIQUE, -- Client-generated UUID hostname TEXT NOT NULL, ip_address TEXT NOT NULL, mac_address TEXT, os_version TEXT, client_version TEXT, status TEXT NOT NULL DEFAULT 'offline', last_heartbeat TEXT, registered_at TEXT NOT NULL DEFAULT (datetime('now')), group_name TEXT DEFAULT 'default' ); ``` **device\_status** - Latest status snapshot (upsert on each heartbeat) ```sql CREATE TABLE device_status ( id INTEGER PRIMARY KEY AUTOINCREMENT, device_uid TEXT NOT NULL REFERENCES devices(device_uid), cpu_usage REAL, memory_usage REAL, memory_total INTEGER, disk_usage REAL, disk_total INTEGER, network_rx_rate INTEGER, network_tx_rate INTEGER, running_procs INTEGER, top_processes TEXT, -- JSON array updated_at TEXT NOT NULL DEFAULT (datetime('now')), FOREIGN KEY (device_uid) REFERENCES devices(device_uid) ); ``` ### 4.2 Asset Tables **hardware\_assets** - Hardware inventory (one row per device) ```sql CREATE TABLE hardware_assets ( id INTEGER PRIMARY KEY AUTOINCREMENT, device_uid TEXT NOT NULL REFERENCES devices(device_uid), cpu_model TEXT, cpu_cores INTEGER, memory_total INTEGER, disk_model TEXT, disk_total INTEGER, gpu_model TEXT, motherboard TEXT, serial_number TEXT, updated_at TEXT NOT NULL DEFAULT (datetime('now')), UNIQUE(device_uid) ); ``` **software\_assets** - Software inventory (many rows per device) ```sql CREATE TABLE software_assets ( id INTEGER PRIMARY KEY AUTOINCREMENT, device_uid TEXT NOT NULL REFERENCES devices(device_uid), name TEXT NOT NULL, version TEXT, publisher TEXT, install_date TEXT, install_path TEXT, updated_at TEXT NOT NULL DEFAULT (datetime('now')), UNIQUE(device_uid, name, version) ); ``` **asset\_changes** - Change audit log ```sql CREATE TABLE asset_changes ( id INTEGER PRIMARY KEY AUTOINCREMENT, device_uid TEXT NOT NULL REFERENCES devices(device_uid), change_type TEXT NOT NULL, -- 'hardware' | 'software_added' | 'software_removed' change_detail TEXT NOT NULL, -- JSON: {field, old_value, new_value} changed_at TEXT NOT NULL DEFAULT (datetime('now')) ); ``` ### 4.3 USB Control Tables **usb\_events** - USB device activity log ```sql CREATE TABLE usb_events ( id INTEGER PRIMARY KEY AUTOINCREMENT, device_uid TEXT NOT NULL REFERENCES devices(device_uid), usb_vendor_id TEXT, usb_product_id TEXT, usb_serial TEXT, usb_name TEXT, event_type TEXT NOT NULL, -- 'inserted' | 'removed' | 'blocked' event_time TEXT NOT NULL DEFAULT (datetime('now')) ); ``` **usb\_policies** - USB access policies ```sql CREATE TABLE usb_policies ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, policy_type TEXT NOT NULL, -- 'whitelist' | 'blacklist' | 'readonly' | 'all_block' target_type TEXT NOT NULL, -- 'global' | 'group' | 'device' target_id TEXT, -- Group name or device UID (NULL for global) rules TEXT NOT NULL, -- JSON: vendor_id/product_id/serial patterns enabled INTEGER NOT NULL DEFAULT 1, created_at TEXT NOT NULL DEFAULT (datetime('now')) ); ``` ### 4.4 Alert Tables **alert\_rules** - Alert rule definitions ```sql CREATE TABLE alert_rules ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, rule_type TEXT NOT NULL, -- 'device_offline' | 'cpu_high' | 'memory_high' | 'disk_high' | 'usb_unauthorized' | 'asset_change' condition TEXT NOT NULL, -- JSON: threshold, duration, etc. notify_method TEXT NOT NULL, -- 'email' | 'webhook' | 'both' notify_target TEXT NOT NULL, -- Email address or webhook URL enabled INTEGER NOT NULL DEFAULT 1, created_at TEXT NOT NULL DEFAULT (datetime('now')) ); ``` **alert\_records** - Alert history ```sql CREATE TABLE alert_records ( id INTEGER PRIMARY KEY AUTOINCREMENT, rule_id INTEGER REFERENCES alert_rules(id), device_uid TEXT, alert_type TEXT NOT NULL, severity TEXT NOT NULL DEFAULT 'warning', content TEXT NOT NULL, notified INTEGER NOT NULL DEFAULT 0, handled INTEGER NOT NULL DEFAULT 0, handler TEXT, handle_remark TEXT, created_at TEXT NOT NULL DEFAULT (datetime('now')) ); ``` ### 4.5 Indexes ```sql CREATE INDEX idx_devices_status ON devices(status); CREATE INDEX idx_device_status_uid ON device_status(device_uid); CREATE INDEX idx_software_device ON software_assets(device_uid); CREATE INDEX idx_usb_events_device_time ON usb_events(device_uid, event_time); CREATE INDEX idx_usb_policies_target ON usb_policies(target_type, target_id); CREATE INDEX idx_alert_records_time ON alert_records(created_at); CREATE INDEX idx_asset_changes_time ON asset_changes(changed_at); CREATE INDEX idx_alert_records_device ON alert_records(device_uid, created_at); CREATE INDEX idx_asset_changes_device ON asset_changes(device_uid); ``` ### 4.6 Status History (Time-Series) The `device_status` table only holds the latest snapshot. For history charts, a separate table stores periodic samples with automatic pruning. ```sql -- Status history samples (insert every 60s per device, auto-pruned after 7 days) CREATE TABLE device_status_history ( id INTEGER PRIMARY KEY AUTOINCREMENT, device_uid TEXT NOT NULL REFERENCES devices(device_uid), cpu_usage REAL, memory_usage REAL, disk_usage REAL, network_rx_rate INTEGER, network_tx_rate INTEGER, running_procs INTEGER, sampled_at TEXT NOT NULL DEFAULT (datetime('now')) ); CREATE INDEX idx_status_history_device_time ON device_status_history(device_uid, sampled_at); ``` **Retention policy**: A background task runs every hour and deletes rows where `sampled_at < datetime('now', '-7 days')`. The 7-day retention window is configurable via `config.toml`. ### 4.7 SQLite Configuration SQLite requires specific configuration for concurrent server workloads: ```sql -- Applied on every connection via SQLx setup PRAGMA journal_mode = WAL; -- Write-Ahead Logging: allows concurrent reads during writes PRAGMA synchronous = NORMAL; -- Balance between safety and performance (WAL handles durability) PRAGMA busy_timeout = 5000; -- Wait up to 5s for locked database instead of immediate SQLITE_BUSY PRAGMA wal_autocheckpoint = 1000; -- Auto-checkpoint WAL every 1000 pages PRAGMA cache_size = -64000; -- 64MB page cache PRAGMA foreign_keys = ON; -- Enforce foreign key constraints ``` **Connection pooling (sqlx)**: - Max connections: 8 (sufficient for 500 devices — SQLite serializes writes anyway) - Write operations are funneled through a single `tokio::sync::Mutex` to prevent write contention. Reads are unrestricted (WAL allows concurrent reads). - Status report writes (the highest-frequency write) use batch INSERT with `INSERT OR REPLACE` to minimize lock duration. ## 5. Client Design (Windows Service) ### 5.1 Runtime Model ``` main.rs ├─ windows_service::start() # Register as Windows Service ├─ tray_icon::show() # System tray with status icon ├─ tokio::spawn(monitor_task) # Collect system metrics every 60s ├─ tokio::spawn(asset_task) # Collect assets on startup + schedule ├─ tokio::spawn(usb_task) # Listen for WM_DEVICECHANGE ├─ tokio::spawn(network_task) # TCP connection + message loop └─ tokio::spawn(config_watcher) # Watch for config/policy updates ``` ### 5.2 Status Monitoring (monitor/system.rs) - **CPU**: `GetSystemTimes()` or `NtQuerySystemInformation()` for per-CPU usage - **Memory**: `GlobalMemoryStatusEx()` for total/available - **Disk**: `GetDiskFreeSpaceExW()` for each drive - **Network**: `GetIfTable2()` from `iphlpapi` for bytes sent/received - **Processes**: `CreateToolhelp32Snapshot()` for enumeration, top 10 by CPU/memory - **Collection interval**: 60 seconds (configurable) - **Payload**: \~500 bytes JSON per report ### 5.3 Asset Collection (asset/) - **Hardware**: WMI queries (`Win32_Processor`, `Win32_PhysicalMemory`, `Win32_DiskDrive`, `Win32_VideoController`, `Win32_BaseBoard`) - **Software**: Registry enumeration (`HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall`) - **Change detection**: Hash comparison on collected data; only send deltas after initial full report - **Collection trigger**: On startup, daily scheduled, or server-initiated ### 5.4 USB Control (usb/) - **Detection**: `RegisterDeviceNotification()` for `WM_DEVICECHANGE` messages - **Policy enforcement**: - `all_block`: Set `HKLM\SYSTEM\CurrentControlSet\Services\USBSTOR\Start = 4` (disabled) - `whitelist`: Allow only specific vendor\_id/product\_id combinations - `blacklist`: Block specific devices - `readonly`: **Deferred to Phase 3** — enforcing USB read-only at the OS level requires a kernel-mode storage filter driver, which is a significant development effort (WHQL signing, driver testing). For MVP, only block/allow modes are supported. - **Event logging**: Every insert/remove event sent to server ### 5.5 Client Lifecycle 1. **Installation**: MSI installer or standalone exe with `--install` flag 2. **First run**: Generate `device_uid` (UUID v4), register with server (using registration token), full asset report 3. **Normal operation**: - TCP+TLS connection to server (auto-reconnect with exponential backoff: 1s, 2s, 4s, ..., max 60s) - Heartbeat every 30s - Status report every 60s - USB events immediately - Asset changes on detection 4. **Policy sync**: Server pushes policy updates; client acknowledges and applies 5. **Update**: Server can trigger client self-update (download new binary, verify Ed25519 signature, atomic replace) 6. **Offline resilience**: Client buffers up to 1000 events locally (sled embedded database) when server is unreachable. Buffered events are sent on reconnection. If buffer overflows, oldest events are dropped. Alert events are prioritized over status reports. 7. **De-provisioning**: When admin deletes a device via `DELETE /api/devices/:uid`: - Server adds device\_uid to revocation list - If client is connected, server sends `ConfigUpdate(SelfDestruct)` → client stops its Windows Service and removes its credentials from the Windows Credential Store - If client is offline, it is rejected on next reconnection attempt (HMAC verification fails because device record is deleted) ### 5.6 Device Groups Device groups are managed as a first-class entity (not ad-hoc strings): ```sql CREATE TABLE device_groups ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL UNIQUE, description TEXT, parent_id INTEGER REFERENCES device_groups(id), -- Optional hierarchy created_at TEXT NOT NULL DEFAULT (datetime('now')) ); ``` The `devices.group_name` column references `device_groups.name`. This enables proper group management, hierarchical grouping, and group-level policy application in the web UI. ## 6. Server Design (Axum) ### 6.1 HTTP API Routes **Authentication model**: All routes except `/api/auth/*` require a valid JWT in the `Authorization: Bearer ` header. The JWT middleware (`axum::middleware::from_fn(auth_guard)`) validates the token and injects the user's role into request extensions. Routes marked `[viewer]` are accessible to both `admin` and `viewer` roles; unmarked routes require `admin` role. ``` # Public (no auth required) POST /api/auth/login # JWT token generation POST /api/auth/refresh # Refresh access token POST /api/register # Client registration (uses registration token, not JWT) # Devices [viewer for GET, admin for DELETE] GET /api/devices # List all devices (pagination, filters) GET /api/devices/:uid # Device detail GET /api/devices/:uid/status # Current status metrics GET /api/devices/:uid/history # Status history (time range query) DELETE /api/devices/:uid # Remove device (de-provision) # Assets [viewer] GET /api/assets/hardware # Hardware inventory (filters) GET /api/assets/software # Software inventory (filters) GET /api/assets/changes # Asset change log # USB Control [viewer for GET, admin for CUD] GET /api/usb/events # USB event log GET /api/usb/policies # List USB policies POST /api/usb/policies # Create policy PUT /api/usb/policies/:id # Update policy DELETE /api/usb/policies/:id # Delete policy # Alerts [viewer for GET, admin for CUD] GET /api/alerts/rules # List alert rules POST /api/alerts/rules # Create rule PUT /api/alerts/rules/:id # Update rule DELETE /api/alerts/rules/:id # Delete rule GET /api/alerts/records # Alert history (filters) PUT /api/alerts/records/:id/handle # Handle an alert # Settings [admin only] GET /api/settings # System settings PUT /api/settings # Update settings POST /api/settings/registration-token # Generate client registration token PUT /api/settings/tls # Upload new TLS certificate # Admin [admin only] GET /api/admin/audit-log # Admin audit trail POST /api/admin/users # Create user PUT /api/admin/users/:id # Update user # WebSocket (auth via first message) GET /ws # WebSocket real-time updates # Health & Operations (no auth, for load balancer / monitoring) GET /health # Returns { status: "ok", uptime, connected_clients, db_size } ``` **Server operational monitoring**: The `/health` endpoint returns JSON with the server's own metrics: ```json { "status": "ok", "uptime_seconds": 86400, "connected_clients": 342, "db_size_bytes": 52428800, "pending_alerts": 3, "version": "1.0.0" } ``` ### 6.2 WebSocket Events (Server → Browser) ```typescript // Device status update { type: "device_status", device_uid: "...", cpu: 45.2, memory: 62.1, ... } // Device online/offline { type: "device_state", device_uid: "...", status: "online" | "offline" } // USB event { type: "usb_event", device_uid: "...", event: "inserted", usb_name: "..." } // New alert { type: "alert", alert_id: 123, severity: "warning", content: "..." } // New device registered { type: "device_new", device_uid: "...", hostname: "..." } ``` ### 6.3 Alert Engine **Rule evaluation** happens in the TCP session handler, immediately when data arrives: ``` Incoming StatusReport → parse metrics → check alert rules (in-memory cache) → if threshold breached: 1. INSERT into alert_records 2. dispatch notification (email/webhook) asynchronously 3. push WebSocket event to connected admins Incoming UsbEvent → check USB policies → if unauthorized: 1. Send PolicyUpdate to client (block device) 2. INSERT into alert_records 3. dispatch notification 4. push WebSocket event ``` **Alert cooldown**: Same rule + same device triggers at most once per 5 minutes (configurable) to prevent alert storms. ## 7. Web Frontend Design ### 7.1 Technology Stack - **Vue.js 3** with Composition API + TypeScript - **Vite** for development and build - **Vue Router 4** for SPA routing - **Pinia** for state management - **Element Plus** for UI components - **ECharts 5** for charts (CPU/memory/disk history) - **Axios** for HTTP requests ### 7.2 Key Pages **Dashboard**: Overview cards (device count, online/offline, alerts), recent USB events timeline, top alert devices, real-time updates via WebSocket. **Devices**: Table with hostname, IP, group, status badge (green/red), CPU/memory mini-sparklines. Click row → DeviceDetail with hardware info, software list, metric history charts. **Assets**: Two tabs - hardware inventory (filterable table) and software inventory (searchable). Change log view with diff highlighting. **USB Control**: Policy editor (drag-and-drop rule ordering), event timeline with device/USB details, policy test simulator. **Alerts**: Split view - rules on left, alert list on right. Severity badges (info/warning/critical). Inline handling with notes. **Settings**: SMTP config, Webhook URLs, user management, system info. ### 7.3 Build Integration The Vue.js SPA builds to `web/dist/` and is embedded into the server binary at compile time: ```rust // server/src/main.rs use include_dir::{include_dir, Dir}; static FRONTEND: Dir = include_dir!("$CARGO_MANIFEST_DIR/../../web/dist"); // Serve embedded SPA app.route("/", get(|| async { /* serve index.html */ })) .route("/assets/{*path}", get(|| async { /* serve static files */ })) .fallback_service(ServeDir::from(FRONTEND)) ``` ## 8. Security Design ### 8.1 Communication Security - **Client ↔ Server**: TLS 1.3 over TCP (rustls, no OpenSSL dependency) - **Browser ↔ Server**: HTTPS (rustls), JWT tokens in HTTP-only cookies - **Certificate lifecycle**: - **Initial setup**: Server generates a self-signed CA + server certificate on first run, stored in `config/cert.pem` + `config/key.pem` - **Client trust**: The CA certificate fingerprint is embedded in the client installer at build time. On first connection, the client verifies the server cert against this known CA. This prevents MITM attacks without requiring a public CA. - **Certificate rotation**: Admin uploads new cert via `PUT /api/settings/tls`. Server signals all connected clients via `ConfigUpdate(TlsCertRotate)`. Clients download the new CA cert over the existing trusted TLS connection before reconnecting with the new cert. Old cert remains valid for a 24-hour overlap window. - **Custom CA**: Admin can replace the self-signed CA with a company PKI cert at any time via the settings API or config file (PEM format). ### 8.2 Authentication & Authorization #### Admin Authentication (Browser → Server) - **Login**: Username + bcrypt password → JWT access token (30min expiry) + refresh token (7 days, stored in HTTP-only Secure cookie) - **JWT transport**: Access token in `Authorization: Bearer ` header - **Token refresh**: `POST /api/auth/refresh` with refresh token cookie → new access token - **RBAC**: Two roles - `admin` (full access) and `viewer` (read-only) - **Rate limiting**: Login endpoint limited to 5 attempts per minute per IP - **WebSocket auth**: Client sends `{ type: "auth", token: "" }` as the first message after connection. Server closes the connection if auth fails within 5 seconds. #### Client Authentication (Agent → Server) - **Registration flow**: 1. Admin generates a registration token via `POST /api/settings/registration-token` (returns a single-use token valid for 24h) 2. Token is embedded in the client installer or provided during manual setup 3. Client sends `Register` message with payload: `{ device_uid, hostname, registration_token, os_info }` 4. Server validates the registration token, creates the device record, and returns a device-specific secret (random 256-bit key) 5. Client stores the secret in the Windows Credential Store (encrypted with DPAPI) 6. All subsequent connections include a HMAC-SHA256 signature of the message using this device secret - **Session auth**: After registration, every message from the client includes `{ device_uid, timestamp, hmac }` where `hmac = HMAC-SHA256(device_secret, timestamp + payload)`. Server verifies HMAC before processing. - **De-provisioning**: `DELETE /api/devices/:uid` revokes the device's record. Server adds the device\_uid to a revocation list (in-memory + SQLite). If the decommissioned client reconnects, the server sends a `ConfigUpdate(SelfDestruct)` command that causes the client to stop its service and delete its local credentials. ### 8.3 Data Security - **Passwords**: bcrypt with cost factor 12 - **JWT secrets**: Configurable, random if not set - **Database**: SQLite file permissions 0600 (owner read/write only) - **Logs**: No sensitive data in logs (no passwords, no full content) - **HTTPS enforced**: HTTP requests redirect to HTTPS ### 8.4 Client Integrity - **Code signing**: Recommended to sign client binary with company certificate - **Anti-tampering**: Client verifies its own SHA-256 checksum on startup against the value stored in the Windows Credential Store (set during installation) - **Secure update**: Server provides update with Ed25519 signature. Client verifies signature before replacing binary. Update is atomic: download to temp file → verify → rename (crash-safe on Windows via `MoveFileEx` with `MOVEFILE_REPLACE_EXISTING`) ### 8.5 Admin Audit Trail All admin actions are logged for compliance: ```sql CREATE TABLE admin_audit_log ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL REFERENCES users(id), action TEXT NOT NULL, -- 'login' | 'create_policy' | 'delete_device' | etc. target_type TEXT, -- 'device' | 'policy' | 'rule' | 'user' | 'setting' target_id TEXT, detail TEXT, -- JSON: what changed ip_address TEXT NOT NULL, created_at TEXT NOT NULL DEFAULT (datetime('now')) ); CREATE INDEX idx_audit_log_user_time ON admin_audit_log(user_id, created_at); CREATE INDEX idx_audit_log_time ON admin_audit_log(created_at); ``` ## 9. Technology Stack Summary ### 9.1 Shared (protocol crate) | Dependency | Version | Purpose | | ----------- | ------- | ----------------------- | | serde | 1 | Serialization framework | | serde\_json | 1 | JSON serialization | | thiserror | 1 | Error types | ### 9.2 Client (Windows) | Dependency | Version | Purpose | | --------------- | -------- | ---------------------------------------------- | | tokio | 1 (full) | Async runtime | | windows | 0.54 | Windows API bindings (modern, replaces winapi) | | wmi | 0.14 | WMI queries for hardware info | | rustls | 0.23 | TLS (no OpenSSL) | | sysinfo | 0.30 | Cross-platform system info (fallback) | | serde | 1 | Message serialization | | tracing | 0.1 | Structured logging | | anyhow | 1 | Error handling | | uuid | 1 | Device UID generation | | windows-service | 0.7 | Windows Service integration | ### 9.3 Server | Dependency | Version | Purpose | | ------------------ | ------------ | --------------------------------------- | | axum | 0.7 | Web framework | | tokio | 1 (full) | Async runtime | | sqlx | 0.7 (sqlite) | Database (compile-time checked queries) | | rustls | 0.23 | TLS | | tower-http | 0.5 | CORS, compression, static file serving | | serde | 1 | JSON serialization | | tracing | 0.1 | Structured logging | | tracing-subscriber | 0.3 | Log formatting | | anyhow | 1 | Error handling | | jsonwebtoken | 9 | JWT token handling | | bcrypt | 0.15 | Password hashing | | lettre | 0.11 | Email sending (SMTP) | | reqwest | 0.12 | Webhook HTTP calls | | include\_dir | 0.7 | Embed frontend into binary | | toml | 0.8 | Configuration file parsing | | uuid | 1 | ID generation | ### 9.4 Web Frontend | Package | Version | Purpose | | ------------ | ------- | ------------------------- | | vue | ^3.4 | UI framework | | vue-router | ^4.2 | SPA routing | | pinia | ^2.1 | State management | | element-plus | ^2.5 | UI components | | echarts | ^5.4 | Charts and visualizations | | axios | ^1.6 | HTTP client | | typescript | ^5.3 | Type safety | | vite | ^5.0 | Build tool | | @vueuse/core | ^10.7 | Composable utilities | ## 10. Plugin Modules (Phase 2) — Detailed Design Each plugin is an independent client-side module with its own server API and database tables. Plugins can be enabled/disabled per device group via the admin web UI. ### 10.1 Plugin Architecture ``` Client Plugin System ├── plugin_manager.rs # Load/start/stop plugins based on server policy ├── plugins/ │ ├── web_filter/ # 上网拦截 │ │ ├── mod.rs │ │ ├── hosts.rs # Modify hosts file for DNS blocking │ │ └── proxy.rs # Local proxy for URL filtering │ ├── usage_timer/ # 时长记录 │ │ ├── mod.rs │ │ ├── tracker.rs # Track active/idle time │ │ └── reporter.rs # Aggregate and report │ ├── software_blocker/ # 软件禁止安装 │ │ ├── mod.rs │ │ ├── registry_watch.rs # Monitor registry for new installations │ │ └── process_kill.rs # Kill blacklisted processes │ ├── popup_blocker/ # 弹窗拦截 │ │ ├── mod.rs │ │ └── window_filter.rs # Enumerate windows, hide known popup patterns │ ├── usb_file_audit/ # U盘文件操作记录 │ │ ├── mod.rs │ │ └── file_watcher.rs # Monitor file ops on removable drives │ └── screen_watermark/ # 水印管理 │ ├── mod.rs │ └── overlay.rs # Transparent overlay window ``` ### 10.2 Plugin: Web Filter (上网拦截) **Purpose**: Block or allow specific URLs/websites based on admin-defined rules. **Client Implementation**: - Method 1: **DNS-level blocking** — Modify `C:\Windows\System32\drivers\etc\hosts` to redirect blocked domains to `127.0.0.1` - Method 2: **Windows Filtering Platform (WFP)** — Use WFP API to intercept and block HTTP/HTTPS connections at the network level - Preferred: WFP approach (more reliable, doesn't interfere with hosts file) **Server-side**: ```sql -- Web filter rules CREATE TABLE web_filter_rules ( id INTEGER PRIMARY KEY AUTOINCREMENT, rule_type TEXT NOT NULL, -- 'blacklist' | 'whitelist' | 'category' pattern TEXT NOT NULL, -- Domain, URL pattern, or category name target_type TEXT NOT NULL, -- 'global' | 'group' | 'device' target_id TEXT, enabled INTEGER NOT NULL DEFAULT 1, created_at TEXT NOT NULL DEFAULT (datetime('now')) ); -- Web access log (for reporting) CREATE TABLE web_access_log ( id INTEGER PRIMARY KEY AUTOINCREMENT, device_uid TEXT NOT NULL REFERENCES devices(device_uid), url TEXT NOT NULL, action TEXT NOT NULL, -- 'allowed' | 'blocked' timestamp TEXT NOT NULL DEFAULT (datetime('now')) ); CREATE INDEX idx_web_access_log_device_time ON web_access_log(device_uid, timestamp); CREATE INDEX idx_web_access_log_time ON web_access_log(timestamp); ``` **API**: ``` GET/POST/PUT/DELETE /api/plugins/web-filter/rules GET /api/plugins/web-filter/log ``` ### 10.3 Plugin: Usage Timer (时长记录) **Purpose**: Track when computers are actively used vs idle, and which applications are used. **Client Implementation**: - **Idle detection**: `GetLastInputInfo()` to detect user idle time - **Active window tracking**: `GetForegroundWindow()` + `GetWindowText()` every 5 seconds - **Session tracking**: Log login/logoff events via Windows Session Change notifications - **Report**: Aggregate daily usage data (total active time, per-app time) sent to server every 10 minutes **Server-side**: ```sql -- Daily usage summary per device CREATE TABLE usage_daily ( id INTEGER PRIMARY KEY AUTOINCREMENT, device_uid TEXT NOT NULL REFERENCES devices(device_uid), date TEXT NOT NULL, -- 'YYYY-MM-DD' total_active_minutes INTEGER, total_idle_minutes INTEGER, first_active_at TEXT, last_active_at TEXT, UNIQUE(device_uid, date) ); -- Per-app usage (aggregated daily) CREATE TABLE app_usage_daily ( id INTEGER PRIMARY KEY AUTOINCREMENT, device_uid TEXT NOT NULL REFERENCES devices(device_uid), date TEXT NOT NULL, app_name TEXT NOT NULL, usage_minutes INTEGER, UNIQUE(device_uid, date, app_name) ); ``` **API**: ``` GET /api/plugins/usage-timer/daily # Daily usage summary GET /api/plugins/usage-timer/app-usage # Per-app usage breakdown GET /api/plugins/usage-timer/leaderboard # Usage ranking across devices ``` ### 10.4 Plugin: Software Blocker (软件禁止安装) **Purpose**: Prevent installation of blacklisted software; auto-uninstall if detected. **Client Implementation**: - **Registry monitoring**: Watch `HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall` for new entries - **Process monitoring**: Periodically scan running processes against blacklist - **Enforcement**: 1. Detect blacklisted software installation → terminate installer process 2. Report to server → admin gets alert 3. Optionally auto-uninstall via `msiexec /x` or `UninstallString` from registry **Server-side**: ```sql -- Software blacklist CREATE TABLE software_blacklist ( id INTEGER PRIMARY KEY AUTOINCREMENT, name_pattern TEXT NOT NULL, -- Regex or glob pattern to match software name category TEXT, -- 'game', 'social', 'vpn', 'custom' action TEXT NOT NULL DEFAULT 'block', -- 'block' | 'alert' target_type TEXT NOT NULL, -- 'global' | 'group' | 'device' target_id TEXT, enabled INTEGER NOT NULL DEFAULT 1, created_at TEXT NOT NULL DEFAULT (datetime('now')) ); -- Software violation log CREATE TABLE software_violations ( id INTEGER PRIMARY KEY AUTOINCREMENT, device_uid TEXT NOT NULL REFERENCES devices(device_uid), software_name TEXT NOT NULL, action_taken TEXT NOT NULL, -- 'blocked_install' | 'auto_uninstalled' | 'alerted' timestamp TEXT NOT NULL DEFAULT (datetime('now')) ); ``` **API**: ``` GET/POST/PUT/DELETE /api/plugins/software-blocker/blacklist GET /api/plugins/software-blocker/violations ``` ### 10.5 Plugin: Popup Blocker (弹窗拦截) **Purpose**: Suppress popup ads and unwanted notification windows. **Client Implementation**: - **Window enumeration**: `EnumWindows()` to list all visible windows - **Pattern matching**: Match window title/class against known popup patterns (configurable from server) - **Suppression**: `ShowWindow(hwnd, SW_HIDE)` or `PostMessage(hwnd, WM_CLOSE, 0, 0)` for matched popups - **Built-in list**: Common popup patterns (Flash ads, browser notifications, update nags) - **Allow list**: Admin can specify windows that should never be blocked **Server-side**: ```sql -- Popup filter rules CREATE TABLE popup_filter_rules ( id INTEGER PRIMARY KEY AUTOINCREMENT, rule_type TEXT NOT NULL, -- 'block' | 'allow' window_title TEXT, -- Pattern for window title window_class TEXT, -- Pattern for window class name process_name TEXT, -- Pattern for process name target_type TEXT NOT NULL, target_id TEXT, enabled INTEGER NOT NULL DEFAULT 1, created_at TEXT NOT NULL DEFAULT (datetime('now')) ); ``` **API**: ``` GET/POST/PUT/DELETE /api/plugins/popup-blocker/rules GET /api/plugins/popup-blocker/stats # Block count statistics ``` ### 10.6 Plugin: USB File Audit (U盘文件操作记录) **Purpose**: Log all file operations (copy, delete, rename, move) on USB storage devices. **Client Implementation**: - **Drive detection**: Monitor for new removable drives via `WM_DEVICECHANGE` + `DBT_DEVICEARRIVAL` - **File monitoring**: `ReadDirectoryChangesW()` on the detected USB drive letter - **Logged operations**: File name, operation type (create/delete/rename), file size, timestamp - **Report**: Batch send to server every 30 seconds while USB is mounted **Server-side**: ```sql -- USB file operation log CREATE TABLE usb_file_operations ( id INTEGER PRIMARY KEY AUTOINCREMENT, device_uid TEXT NOT NULL REFERENCES devices(device_uid), usb_serial TEXT, -- Identify which USB device operation TEXT NOT NULL, -- 'create' | 'delete' | 'rename' | 'modify' file_path TEXT NOT NULL, -- Path on USB drive file_size INTEGER, timestamp TEXT NOT NULL DEFAULT (datetime('now')) ); ``` **API**: ``` GET /api/plugins/usb-file-audit/log # File operation log GET /api/plugins/usb-file-audit/summary # Per-device USB file activity summary ``` ### 10.7 Plugin: Screen Watermark (水印管理) **Purpose**: Display a transparent, non-removable watermark overlay on the screen showing company name, username, and timestamp. **Client Implementation**: - **Overlay window**: Create a layered, transparent, click-through window (`WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_TOPMOST`) covering the entire screen - **Rendering**: GDI+ or Direct2D to draw semi-transparent text (company name, `{username}`, `{hostname}`, `{date}`, `{time}`) - **Persistence**: Re-create overlay on display change events (`WM_DISPLAYCHANGE`) - **Tamper resistance**: Monitor overlay window existence, re-create if closed **Server-side**: ```sql -- Watermark configuration CREATE TABLE watermark_config ( id INTEGER PRIMARY KEY AUTOINCREMENT, target_type TEXT NOT NULL, -- 'global' | 'group' | 'device' target_id TEXT, content TEXT NOT NULL, -- Template: "Company: {company} | User: {username} | {date}" font_size INTEGER DEFAULT 14, opacity REAL DEFAULT 0.15, -- 0.0 to 1.0 color TEXT DEFAULT '#808080', angle INTEGER DEFAULT -30, -- Rotation angle in degrees enabled INTEGER NOT NULL DEFAULT 1, updated_at TEXT NOT NULL DEFAULT (datetime('now')) ); ``` **API**: ``` GET/POST/PUT/DELETE /api/plugins/watermark/config ``` ### 10.8 Plugin Message Types (Protocol Extension) ```rust // Additional message types for plugins #[repr(u8)] pub enum PluginMessageType { // Web Filter WebFilterRuleUpdate = 0x20, WebAccessLog = 0x21, // Usage Timer UsageReport = 0x30, AppUsageReport = 0x31, // Software Blocker SoftwareBlacklist = 0x40, SoftwareViolation = 0x41, // Popup Blocker PopupRules = 0x50, // USB File Audit UsbFileOp = 0x60, // Screen Watermark WatermarkConfig = 0x70, // Generic plugin control PluginEnable = 0x80, PluginDisable = 0x81, } ``` ## 11. Build & Deploy ### 11.1 Build Process ```bash # Build frontend cd web && npm install && npm run build # Build server (embeds frontend) cd crates/server && cargo build --release # Build client cd crates/client && cargo build --release --target x86_64-pc-windows-msvc ``` ### 11.2 Deployment Checklist **Server:** - [ ] Copy `csm-server.exe` to target directory - [ ] Create `config.toml` with production values - [ ] Generate JWT secret: `openssl rand -hex 32` - [ ] Configure SMTP credentials for alert emails - [ ] Set up TLS certificate (or use self-signed for testing) - [ ] Create initial admin user: `csm-server --init-admin` - [ ] Start as Windows Service or use `nssm` to register **Client:** - [ ] Build MSI installer (via `cargo-wix` or WiX Toolset) - [ ] Configure server address in installer - [ ] Deploy via GPO, SCCM, or manual installation - [ ] Verify client appears in web dashboard ## 12. Performance Targets | Metric | Target | Notes | | ----------------- | ------------ | ---------------------------------------- | | Client CPU usage | < 2% | Steady state, excluding asset collection | | Client memory | < 50MB | Including all monitoring modules | | Client disk | < 10MB | Binary + local cache | | Server memory | < 200MB | 500 connected clients | | Server CPU | < 5% | Steady state with 500 clients | | API response time | < 100ms | Typical CRUD operations | | WebSocket latency | < 500ms | Alert to browser | | Database size | \~500MB/year | 500 devices, 90-day retention | | Deployment size | \~20MB | Server binary with embedded frontend | ## 13. Data Retention & Cleanup A background task (`tokio::spawn(cleanup_task)`) runs every hour and prunes data based on configurable retention windows: | Table | Default Retention | Cleanup Method | | -------------------------- | ----------------------------- | ----------------------------------------------------------------------- | | `device_status` | Current only (upsert) | N/A | | `device_status_history` | 7 days | `DELETE WHERE sampled_at < datetime('now', '-7 days')` | | `usb_events` | 90 days | `DELETE WHERE event_time < datetime('now', '-90 days')` | | `asset_changes` | 365 days (forever by default) | `DELETE WHERE changed_at < datetime('now', '-365 days')` | | `alert_records` | 90 days (unhandled kept) | `DELETE WHERE handled = 1 AND created_at < datetime('now', '-90 days')` | | `admin_audit_log` | 365 days | `DELETE WHERE created_at < datetime('now', '-365 days')` | | `web_access_log` (P2) | 30 days | High-volume, aggressive pruning | | `usb_file_operations` (P2) | 90 days | Standard retention | Retention windows are configurable via `config.toml`: ```toml [retention] status_history_days = 7 usb_events_days = 90 asset_changes_days = 365 alert_records_days = 90 audit_log_days = 365 ``` **SQLite backup**: Admin can trigger a manual backup via `POST /api/settings/backup`, which creates a timestamped copy of the SQLite file using `sqlite3 .backup` command (via sqlx `VACUUM INTO`). Auto-backup can be configured to run daily. ## 14. Error Handling & Resilience ### Client-Side Failure Modes | Failure | Detection | Recovery | | ------------------------------ | ----------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | | Server unreachable | TCP connect timeout (10s) | Exponential backoff reconnect (1s → 60s max). Buffer events locally (up to 1000). | | TLS certificate changed | Cert mismatch on handshake | If new cert is signed by known CA, accept and update local cache. If unknown, alert admin via tray notification and retry in 5 minutes. | | Client binary update fails | Hash/signature verification failure | Discard update, keep running version, report failure to server. | | Local sled database corruption | sled auto-recovery on open | If unrecoverable, clear local buffer and re-register with server. | | Service crash | Windows Service recovery options | Configured to auto-restart after 30 seconds (via Windows Service recovery settings). | ### Server-Side Failure Modes | Failure | Detection | Recovery | | ---------------------------- | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | | SQLite database lock | `SQLITE_BUSY` error | `busy_timeout = 5000ms` handles most cases. If still fails after 5s, log error and return 503 to API client. Client data is buffered and retried. | | SQLite database corruption | sqlx connection error on startup | Attempt `PRAGMA integrity_check`. If failed, restore from latest backup. If no backup, reinitialize schema (data loss). | | Email notification failure | SMTP connection error | Retry 3 times with 30s interval. Log failure. Alert is still stored in DB (just not delivered). Admin can see undelivered alerts in the UI. | | Webhook notification failure | HTTP timeout or non-2xx response | Retry 3 times with exponential backoff (1s, 5s, 30s). Log failure. Same as email — alert is stored regardless. | | WebSocket disconnect | Client heartbeat timeout (60s) | Remove session from active viewers list. Client reconnects automatically. No data loss — missed events are fetched via REST API on reconnect. |