- 添加项目基础结构:Cargo.toml、.gitignore、设备UID和密钥文件 - 实现前端Vue3项目结构:路由、登录页面、设备管理页面 - 添加核心协议定义(crates/protocol):设备状态、资产、USB事件等 - 实现客户端监控模块:系统状态收集、资产收集 - 实现服务端基础API和插件系统 - 添加数据库迁移脚本:设备管理、资产跟踪、告警系统等 - 实现前端设备状态展示和基本交互 - 添加使用时长统计和水印功能插件
55 KiB
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)
- Terminal Status Monitoring - CPU/memory/disk/process real-time monitoring, online/offline tracking
- USB Device Control - USB event logging, whitelist/blacklist policies, read-only enforcement
- 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/viainclude_dir!macro frominclude_dircrate) - SQLite database (auto-created on first run)
- Default configuration (overridable via
config.toml)
Deployment process:
# 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 <alerts@company.com>"
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 aConfigUpdate(ProtocolVersion)with the highest common version, or rejects the connection if incompatible.
3.2 Message Types
#[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
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
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)
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)
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)
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
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
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
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
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
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
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.
-- 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:
-- 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<SqlitePool>to prevent write contention. Reads are unrestricted (WAL allows concurrent reads). - Status report writes (the highest-frequency write) use batch INSERT with
INSERT OR REPLACEto 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()orNtQuerySystemInformation()for per-CPU usage - Memory:
GlobalMemoryStatusEx()for total/available - Disk:
GetDiskFreeSpaceExW()for each drive - Network:
GetIfTable2()fromiphlpapifor 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()forWM_DEVICECHANGEmessages - Policy enforcement:
all_block: SetHKLM\SYSTEM\CurrentControlSet\Services\USBSTOR\Start = 4(disabled)whitelist: Allow only specific vendor_id/product_id combinationsblacklist: Block specific devicesreadonly: 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
- Installation: MSI installer or standalone exe with
--installflag - First run: Generate
device_uid(UUID v4), register with server (using registration token), full asset report - 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
- Policy sync: Server pushes policy updates; client acknowledges and applies
- Update: Server can trigger client self-update (download new binary, verify Ed25519 signature, atomic replace)
- 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.
- 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):
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 <token> 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:
{
"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)
// 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:
// 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 viaConfigUpdate(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).
- Initial setup: Server generates a self-signed CA + server certificate on first run, stored in
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 <token>header - Token refresh:
POST /api/auth/refreshwith refresh token cookie → new access token - RBAC: Two roles -
admin(full access) andviewer(read-only) - Rate limiting: Login endpoint limited to 5 attempts per minute per IP
- WebSocket auth: Client sends
{ type: "auth", token: "<jwt>" }as the first message after connection. Server closes the connection if auth fails within 5 seconds.
Client Authentication (Agent → Server)
- Registration flow:
- Admin generates a registration token via
POST /api/settings/registration-token(returns a single-use token valid for 24h) - Token is embedded in the client installer or provided during manual setup
- Client sends
Registermessage with payload:{ device_uid, hostname, registration_token, os_info } - Server validates the registration token, creates the device record, and returns a device-specific secret (random 256-bit key)
- Client stores the secret in the Windows Credential Store (encrypted with DPAPI)
- All subsequent connections include a HMAC-SHA256 signature of the message using this device secret
- Admin generates a registration token via
- Session auth: After registration, every message from the client includes
{ device_uid, timestamp, hmac }wherehmac = HMAC-SHA256(device_secret, timestamp + payload). Server verifies HMAC before processing. - De-provisioning:
DELETE /api/devices/:uidrevokes the device's record. Server adds the device_uid to a revocation list (in-memory + SQLite). If the decommissioned client reconnects, the server sends aConfigUpdate(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
MoveFileExwithMOVEFILE_REPLACE_EXISTING)
8.5 Admin Audit Trail
All admin actions are logged for compliance:
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\hoststo redirect blocked domains to127.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:
-- 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:
-- 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\Uninstallfor new entries - Process monitoring: Periodically scan running processes against blacklist
- Enforcement:
- Detect blacklisted software installation → terminate installer process
- Report to server → admin gets alert
- Optionally auto-uninstall via
msiexec /xorUninstallStringfrom registry
Server-side:
-- 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)orPostMessage(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:
-- 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:
-- 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:
-- 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)
// 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
# 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.exeto target directory - Create
config.tomlwith 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
nssmto register
Client:
- Build MSI installer (via
cargo-wixor 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:
[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. |