⚠️ Legal Notice. This software is intended exclusively for authorized corporate use — with written employee consent or under a company information security policy. The agent captures keystrokes, screenshots, and clipboard data. Unauthorized surveillance is illegal. By using this software you accept full legal responsibility for compliance with applicable laws.
ZavetSec DLP is a self-hosted endpoint activity auditing platform for insider threat monitoring and compliance. A lightweight Windows agent silently monitors endpoint activity and ships data to a central server with a real-time web dashboard. Built for IT security teams who need visibility into endpoints without relying on cloud vendors.
- Fully self-hosted — your data never leaves your infrastructure
- Zero dependencies on endpoints — single self-contained
.exe, no .NET required on workstations - Designed to survive prolonged server outages — persistent disk buffer (50 MB) with exponential backoff
- Production-ready security — brute force protection, rate limiting, HTTPS, PBKDF2 passwords, role-based access
- Remote control — start, stop, restart, or uninstall agents directly from the web dashboard
- Multilingual — English and Russian UI, switchable at runtime
- Quick Start
- Dashboard Preview
- Installing from a Release
- Why ZavetSec DLP?
- Threat Model
- Features
- Architecture
- Requirements
- Step 1 — Build
- Step 2 — Deploy the Server
- Step 3 — Antivirus Exclusions
- Step 4 — Install the Agent
- Configuration
- Privacy Controls
- HTTPS
- Authentication & Security
- Reliable Event Delivery
- Telegram Alerts
- Dashboard
- Remote Agent Management
- Uninstall Agent
- API Reference
- Project Structure
- Troubleshooting
- Security Notes
- Intended Use
- Performance
- Known Limitations
- Duplicate Hostnames
- Roadmap
- Changelog
Get the server running and the first agent connected in under 10 minutes.
1. Build both projects (requires .NET 8 SDK):
cd DlpServer && dotnet publish -c Release -o publish && cd ..
taskkill /IM ZavetSecDlpAgent.exe /F 2>nul
cd DlpAgent && dotnet publish -c Release -o publish && cd ..2. Configure and start the server:
copy DlpServer\appsettings.example.json DlpServer\appsettings.json
:: Edit appsettings.json — set ApiKey and Certificate.Password
notepad DlpServer\appsettings.json
cd DlpServer\publish
dotnet DlpServer.dll
:: Dashboard: https://localhost:5001 Login: admin / admin3. Install the agent on a workstation (run as Administrator):
.\install.ps1 -ServerUrl "https://YOUR-SERVER:5001" -ApiKey "YOUR_KEY"The agent appears in the Agents tab within ~30 seconds.
Default credentials: admin / admin — you will be forced to change the password on first login.
See Step 1 — Build through Step 4 — Install the Agent for the full deployment guide.
📸 Screenshots coming soon. Deploy the server and add your own to
docs/screenshots/.
If you downloaded the pre-built binaries from the Releases page, you do not need the .NET SDK — only .NET 8 Runtime on the server.
:: 1. Extract the archive to any folder, e.g. C:\DlpServer
mkdir C:\DlpServer
:: Extract DlpServer-publish.zip contents into C:\DlpServer
:: 2. Create config from template
copy C:\DlpServerappsettings.example.json C:\DlpServerappsettings.json
notepad C:\DlpServerappsettings.jsonSet these two values in appsettings.json:
"ApiKey": "any-random-string-32+chars",
"Password": "any-password-for-the-certificate":: 3. Install .NET 8 Runtime if not already installed
:: https://dotnet.microsoft.com/download/dotnet/8.0
:: Download: ".NET Runtime 8.x" → Windows x64 Installer
:: 4. Start the server (first run auto-creates HTTPS certificate)
cd C:\DlpServer
dotnet DlpServer.dll
:: Dashboard: https://localhost:5001
:: Login: admin / admin (you will be forced to change password)Note: The database (
events.db) and certificate (server.pfx) are stored inC:\ProgramData\ZavetSec\DLP\— outside the install folder. You can safely replaceDlpServer.dlland other files to upgrade without losing data.
The agent is fully self-contained — no .NET Runtime needed on monitored workstations.
:: Option A — automated install (run as Administrator)
:: Copy install.ps1 from the repo and ZavetSecDlpAgent.exe to the same folder, then:
.\install.ps1 -ServerUrl "https://YOUR-SERVER:5001" -ApiKey "YOUR_KEY":: Option B — manual install (run as Administrator)
:: 1. Create install directory
mkdir C:\ProgramData\ZavetSec\Agent
:: 2. Copy the exe
copy ZavetSecDlpAgent.exe C:\ProgramData\ZavetSec\Agent
:: 3. Generate default config
cd C:\ProgramData\ZavetSec\Agent
ZavetSecDlpAgent.exe --console
:: Press Enter immediately to stop — config.json is now created
:: 4. Edit config — set serverUrl and apiKey
notepad config.json
:: 5. Register scheduled tasks
schtasks /create /tn "ZavetSec DLP Agent" ^
/tr ""C:\ProgramData\ZavetSec\Agent\ZavetSecDlpAgent.exe" --task-mode" ^
/sc ONLOGON /rl HIGHEST /f
schtasks /create /tn "ZavetSec DLP Agent Boot" ^
/tr ""C:\ProgramData\ZavetSec\Agent\ZavetSecDlpAgent.exe" --task-mode" ^
/sc ONSTART /ru SYSTEM /rl HIGHEST /f
:: 6. Start
schtasks /run /tn "ZavetSec DLP Agent"Before deploying the agent: add antivirus exclusions — see Step 3 — Antivirus Exclusions.
If you built the server yourself and want to share it, make sure no secrets are included:
:: Remove secrets before archiving
del "DlpServer\publishappsettings.json"
del "DlpServer\publish\*.pfx"
:: Verify no database files
dir "DlpServer\publish\*.db"
:: Archive (only example config, no real keys)
powershell Compress-Archive -Path "DlpServer\publish\*" ^
-DestinationPath "DlpServer-publish.zip"| Module | Description |
|---|---|
| Keystroke Monitor | Low-level keyboard hook (WH_KEYBOARD_LL), layout-aware, dead keys, special keys |
| Screenshots | Interval capture + window-change trigger, blank screen detection, JPEG quality control |
| Clipboard | Monitors clipboard changes; alert on sensitive data (passwords, card numbers, passports) |
| Network | TCP connection monitoring on suspicious ports; DNS cache tracking |
| USB | WMI-based USB device detection; alert on removable media |
| File Activity | FileSystemWatcher on removable drives; alert on copy operations |
| Processes | Tracks process launches; alert on suspicious names (mimikatz, wireshark, etc.) |
| Encryption | Local logs encrypted with AES-256-CBC; key protected by Windows DPAPI (machine scope) |
| Reliable Delivery | Batched event shipping, 50 MB disk buffer when server is unreachable, exponential backoff |
| HTTPS | Self-signed certificate auto-generated on first run (RSA 2048, SHA-256, 10 years) |
| Remote Control | Start / Stop / Restart / Uninstall agents directly from the dashboard |
| Three-color Status | 🟢 Online (monitoring active) · 🟡 Stopped (process alive, monitoring paused) · ⚫ Offline |
| Multi-monitor Screenshots | Each monitor captured independently; blank screen skipped; files tagged _m1, _m2 |
| Email Alerts | SMTP alerts with rate limiting; configurable modules; managed from Management tab |
| Certificate Fingerprint Pinning | Agent verifies server cert SHA-256; fingerprint printed at server startup |
| Live Feed | Dashboard ⬤ Live button — 5-second refresh instead of 30s |
| Per-agent API Key | Auto-enrollment on first connect; per-agent revocation; stored in config.json |
| Agent Lifecycle Events | AGENT_ONLINE on first connection, AGENT_REMOVED on dashboard removal, AGENT_UNINSTALLED on successful uninstall |
| Unique Agent ID | Each agent generates a persistent 16-char hex ID on first run; stored in config.json, sent as X-Agent-Id header; scoped to events, screenshots, and commands — fully supports multiple machines with identical hostnames |
| Watchdog | Checks every 60 s that internal components are alive; re-registers the scheduled task if deleted by an insider |
| Brute Force Protection | IP blocked for 15 minutes after 5 failed login attempts |
| Rate Limiting | 1,000 requests/minute per IP on agent endpoints |
| Telegram Alerts | Configurable per-module notifications, rate-limited, filter by module type |
| Role-Based Access | admin (full) / viewer (read-only); forced password change on first login |
| Multilingual UI | English and Russian, switchable at runtime |
ASCII diagram — click to expand
┌────────────────────────────────────┐ ┌──────────────────────────────────┐
│ Windows Workstation │ │ Server (Windows / Linux) │
│ │ HTTPS │ │
│ ZavetSecDlpAgent.exe │───────▶│ DlpServer.dll (ASP.NET Core 8) │
│ ├── KeyloggerMonitor │ │ HTTP :5000 ← agents │
│ ├── ClipboardMonitor │ │ HTTPS :5001 ← dashboard+agents │
│ ├── ScreenshotMonitor │ │ │
│ ├── NetworkMonitor │ │ POST /api/ingest │
│ ├── UsbMonitor │◀───────│ GET /api/commands/{host} │
│ ├── FileActivityMonitor │ │ │
│ ├── ProcessMonitor │ │ Database: events.db (SQLite) │
│ ├── LogShipper │ │ Screenshots: ServerScreenshots/ │
│ │ ├── Batched event delivery │ │ Certificate: server.pfx (auto) │
│ │ ├── 50 MB disk buffer │ │ │
│ │ └── Exponential backoff │ │ Dashboard: https://server:5001 │
│ ├── ScreenshotShipper │ │ ├── Events + CSV export │
│ └── CommandPoller (heartbeat) │ │ ├── Screenshots │
│ │ │ ├── Keylogger │
│ C:\ProgramData\ZavetSec\DLP\ │ │ ├── Agents (paginated, control) │
│ ├── Logs\*.log (AES-256) │ │ └── Management (users, TG) │
│ ├── Screenshots\ (temporary) │ │ │
│ ├── shipper_queue.dat (buffer) │ │ Security: │
│ └── agent.key (DPAPI) │ │ ├── Brute force protection │
└────────────────────────────────────┘ │ ├── Rate limiting │
│ └── Session invalidation │
└──────────────────────────────────┘
ZavetSec DLP is designed to detect and audit the following threat categories:
| Threat | Detection Mechanism |
|---|---|
| Insider data theft | USB activity, file copy to removable drives, clipboard exfiltration |
| Credential leakage | Clipboard monitoring with configurable sensitive word detection |
| Shadow IT / unauthorized tools | Process monitoring with suspicious name list |
| Remote access abuse | Network monitoring on alert ports (RDP 3389, VNC 5900, etc.) |
| Suspicious DNS activity | DNS cache tracking, alert on new/unknown domains |
| Malware execution | Process launch auditing (mimikatz, nc, psexec, procdump, etc.) |
| Physical media exfiltration | USB device detection, removable media alerts |
| Unauthorized access | Screenshot capture on window change, keylogger activity timeline |
ZavetSec DLP provides detection and audit capability, not active blocking. It is designed for post-incident investigation, compliance logging, and real-time alerting — not endpoint protection.
Agent:
- Windows 10 / Windows Server 2016+ (x64)
- Administrator privileges
- .NET 8 Runtime — or use the self-contained build (recommended — no .NET required on endpoints)
Server:
- Windows or Linux Ubuntu 22.04+
- .NET 8 Runtime
- Minimum 1 GB RAM, 20+ GB disk
Run once on your development machine.
cd DlpServer
dotnet publish -c Release -o publish:: Stop the agent first if it is running — otherwise the build will fail
:: (the running process locks the .exe file)
taskkill /IM ZavetSecDlpAgent.exe /F 2>nul
cd DlpAgent
dotnet publish -c Release -o publishOutput: DlpAgent\publish\ZavetSecDlpAgent.exe (~60 MB, self-contained — no .NET required on target machines).
When code changes: stop the agent, rebuild, copy the new exe to the install folder, restart.
Step A — Configure before first run:
:: Copy the example config
copy DlpServer\appsettings.example.json DlpServer\appsettings.json
:: Edit it — fill in ApiKey and Certificate Password
notepad DlpServer\appsettings.jsonMandatory values to set:
DlpServer.ApiKey— any random string, 32+ charactersKestrel.Endpoints.Https.Certificate.Password— any password for the auto-generated certificate
Important: Set
Certificate.Passwordinappsettings.jsonbefore running the server for the first time. The password is baked into the.pfxfile at creation time. If you change the password later, deleteC:\ProgramData\ZavetSec\DLP\server.pfxbefore restarting — the server will auto-generate a new certificate with the updated password.
Generate a random key in PowerShell:
[System.Convert]::ToBase64String(
[System.Security.Cryptography.RandomNumberGenerator]::GetBytes(32)
)Step B — Run the server:
cd DlpServer\publish
dotnet DlpServer.dllExpected output on first run:
[HTTPS] Self-signed certificate created: C:\ProgramData\ZavetSec\DLP\server.pfx
[HTTPS] Valid until: 2036-05-19
DLP Server started. DB=C:\ProgramData\ZavetSec\DLP\events.db
HTTP → http://0.0.0.0:5000
HTTPS → https://0.0.0.0:5001 (self-signed cert)
sc create ZavetSecDlpServer ^
binPath="dotnet C:\DlpServer\publish\DlpServer.dll" ^
start=auto DisplayName="ZavetSec DLP Server"
sc start ZavetSecDlpServerLinux (systemd) — click to expand
# Install .NET 8
wget https://dot.net/v1/dotnet-install.sh && chmod +x dotnet-install.sh
./dotnet-install.sh --channel 8.0 --install-dir /usr/share/dotnet
ln -sf /usr/share/dotnet/dotnet /usr/bin/dotnet
# Copy files
mkdir -p /opt/dlpserver
scp -r DlpServer/publish/* user@server:/opt/dlpserver/
# Create systemd service
cat > /etc/systemd/system/dlpserver.service << 'EOF'
[Unit]
Description=ZavetSec DLP Server
After=network.target
[Service]
Type=simple
User=root
WorkingDirectory=/opt/dlpserver
ExecStart=/usr/bin/dotnet /opt/dlpserver/DlpServer.dll
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload && systemctl enable --now dlpserver# Linux
ufw allow 5000/tcp && ufw allow 5001/tcp
# Windows
netsh advfirewall firewall add rule name="ZavetSec HTTP" protocol=TCP dir=in localport=5000 action=allow
netsh advfirewall firewall add rule name="ZavetSec HTTPS" protocol=TCP dir=in localport=5001 action=allow:: Stop server → replace publish\ files → start
:: appsettings.json and events.db are preserved (they live outside publish\)On startup the server automatically applies all pending database schema migrations. Existing data is preserved — upgrades are safe.
The agent uses a keyboard hook and captures screenshots. Antivirus engines detect this behavior as PDM:Trojan. Add exclusions before copying the agent to endpoints.
Add-MpPreference -ExclusionPath "C:\ProgramData\ZavetSec"
Add-MpPreference -ExclusionProcess "ZavetSecDlpAgent.exe"Group Policy Management →
Computer Configuration → Administrative Templates →
Windows Components → Windows Defender Antivirus →
Exclusions → Path Exclusions:
C:\ProgramData\ZavetSec\Agent\ZavetSecDlpAgent.exe
C:\ProgramData\ZavetSec\DLP\
Policies → File Threat Protection → Exclusions:
Path: C:\ProgramData\ZavetSec\Agent\ZavetSecDlpAgent.exe
Scope: All protection components
:: 1. Add antivirus exclusion (Step 3)
:: 2. Copy agent files
mkdir C:\ProgramData\ZavetSec\Agent
xcopy /E /I DlpAgent\publish\* C:\ProgramData\ZavetSec\Agent\
:: 3. First run creates config.json with defaults
cd C:\ProgramData\ZavetSec\Agent
ZavetSecDlpAgent.exe --console
:: Press Enter to stop
:: 4. Edit config.json — fill in the "shipper" section
notepad config.jsonIn config.json set the shipper section:
"shipper": {
"enabled": true,
"serverUrl": "https://YOUR-SERVER-IP:5001",
"apiKey": "YOUR_KEY_FROM_APPSETTINGS",
"batchSize": 50,
"flushSeconds": 30,
"maxQueueSize": 5000,
"deleteLocalScreenshotsAfterUpload": true,
"allowInvalidCertificate": true
}:: 5. Register scheduled tasks
::
:: ONLOGON — runs as the logged-in user (required for screenshot access)
:: Windows Session 0 Isolation prevents SYSTEM from capturing the desktop
schtasks /create /tn "ZavetSec DLP Agent" ^
/tr "\"C:\ProgramData\ZavetSec\Agent\ZavetSecDlpAgent.exe\" --task-mode" ^
/sc ONLOGON /rl HIGHEST /f
:: ONSTART — runs as SYSTEM for boot persistence (no desktop needed at boot)
schtasks /create /tn "ZavetSec DLP Agent Boot" ^
/tr "\"C:\ProgramData\ZavetSec\Agent\ZavetSecDlpAgent.exe\" --task-mode" ^
/sc ONSTART /ru SYSTEM /rl HIGHEST /f
:: 6. Start immediately
schtasks /run /tn "ZavetSec DLP Agent"Important: The ONLOGON task must run as the interactive logged-in user, not as SYSTEM. Windows Vista+ Session 0 Isolation prevents SYSTEM processes from accessing the user's desktop, which causes
Graphics.CopyFromScreento fail with "Invalid Handle". The ONSTART task can run as SYSTEM because no desktop capture happens at boot time.
The agent will appear in the Agents tab within ~30 seconds.
# Run as Administrator. The publish\ folder must be in the same directory as the script.
.\install.ps1 -ServerUrl "https://dlp.company.com:5001" -ApiKey "YOUR_KEY"deploy.ps1 — mass deployment via WinRM (click to expand)
# Enable WinRM on target machines (or via GPO):
winrm quickconfig -force
# Deploy from a list
.\deploy.ps1 -Computers (Get-Content .\machines.txt) `
-ServerUrl "https://dlp.company.com:5001" `
-ApiKey "YOUR_KEY" `
-ConcurrentJobs 10
# Deploy to all machines in an Active Directory OU
$pcs = (Get-ADComputer -Filter * `
-SearchBase "OU=Workstations,DC=company,DC=local").Name
.\deploy.ps1 -Computers $pcs -ServerUrl "https://..." -ApiKey "YOUR_KEY"{
"DlpServer": {
"ApiKey": "REPLACE_WITH_RANDOM_32+_CHAR_KEY",
"DbPath": "C:\\ProgramData\\ZavetSec\\DLP\\events.db",
"ScreenshotDir": "C:\\ProgramData\\ZavetSec\\DLP\\ServerScreenshots",
"MaxEventsReturn": 500
},
"Telegram": {
"BotToken": "",
"ChatId": "",
"SendAllAlerts": false,
"AlertModules": [
"USB_ALERT", "USB_CONNECT", "CLIPBOARD_ALERT",
"PROCESS_ALERT", "DNS_ALERT", "FILE_ALERT"
]
},
"Kestrel": {
"Endpoints": {
"Http": { "Url": "http://0.0.0.0:5000" },
"Https": {
"Url": "https://0.0.0.0:5001",
"Certificate": {
"Path": "C:\\ProgramData\\ZavetSec\\DLP\\server.pfx",
"Password": "REPLACE_WITH_RANDOM_PASSWORD"
}
}
}
}
}{
"screenshot": {
"intervalMinutes": 5,
"jpegQuality": 75,
"onWindowChange": true,
"onStartup": true,
"windowCheckIntervalSeconds": 1,
"blankScreenDetection": true
},
"keylogger": {
"enabled": true,
"bufferChars": 512,
"flushSeconds": 30
},
"clipboard": {
"enabled": true,
"pollIntervalMs": 500,
"maxContentLength": 4096,
"sensitiveWords": [
"password", "passwd", "secret", "token",
"private key", "confidential", "credit", "ssn"
]
},
"network": {
"enabled": true,
"connectionCheckSeconds": 10,
"dnsCheckSeconds": 30,
"alertPorts": [22, 23, 3389, 4444, 5900, 6667]
},
"storage": {
"logDir": "C:\\ProgramData\\ZavetSec\\DLP\\Logs",
"screenshotDir": "C:\\ProgramData\\ZavetSec\\DLP\\Screenshots",
"keyFile": "C:\\ProgramData\\ZavetSec\\DLP\\agent.key",
"retentionLogDays": 30,
"retentionScreenshotDays": 7,
"maxLogMb": 500,
"maxScreenshotMb": 2048
},
"processes": {
"enabled": true,
"checkIntervalSeconds": 10,
"whitelist": ["svchost", "csrss", "lsass", "explorer", "dwm", "winlogon"],
"suspiciousProcesses": ["mimikatz", "wireshark", "nc", "nmap", "psexec", "procdump"]
},
"shipper": {
"enabled": true,
"serverUrl": "https://YOUR-SERVER:5001",
"apiKey": "YOUR_KEY",
"batchSize": 50,
"flushSeconds": 30,
"maxQueueSize": 5000,
"deleteLocalScreenshotsAfterUpload": true,
"allowInvalidCertificate": true
}
}| Parameter | Description |
|---|---|
shipper.enabled |
Enable event delivery to server |
shipper.flushSeconds |
Batch send interval and command poll interval (heartbeat) |
shipper.maxQueueSize |
Max events in memory before spilling to disk |
shipper.deleteLocalScreenshotsAfterUpload |
Delete local screenshot file after successful upload |
shipper.allowInvalidCertificate |
Accept self-signed HTTPS certificates |
shipper.agentId |
Unique agent identifier — auto-generated on first run, do not edit manually |
shipper.agentKey |
Per-agent API key — auto-saved on enrollment, do not edit manually |
shipper.serverFingerprint |
SHA-256 fingerprint of server certificate (hex, no colons); leave empty to skip pinning |
storage.retentionLogDays |
Days to keep local encrypted logs |
clipboard.sensitiveWords |
Words that trigger CLIPBOARD_ALERT |
network.alertPorts |
TCP ports that trigger NETWORK_ALERT |
All monitoring modules are independently configurable per deployment. Administrators can disable any module by editing config.json on the agent before deployment.
| Module | Disable by setting |
|---|---|
| Keystroke monitoring | "keylogger": { "enabled": false } |
| Screenshot capture | "screenshot": { "intervalMinutes": 0, "onWindowChange": false, "onStartup": false } |
| Clipboard monitoring | "clipboard": { "enabled": false } |
| Network monitoring | "network": { "enabled": false } |
| Process monitoring | "processes": { "enabled": false } |
| USB / file monitoring | Modules can be excluded from DlpService initialization |
| Telegram alerts | Leave BotToken empty in appsettings.json |
Data retention is controlled server-side. Screenshots and events can be deleted per-host or globally from the Management tab. Automated retention is configured via retentionLogDays and retentionScreenshotDays in config.json.
Organizations should document which modules are active and communicate this to employees as required by local employment law and privacy regulations.
On first startup the server creates server.pfx:
- RSA 2048 + SHA-256, valid for 10 years
- SAN includes:
localhost, machine hostname,127.0.0.1
The agent connects with "allowInvalidCertificate": true. The browser will show a warning — click "Advanced → Proceed" once.
⚠️ Security note:allowInvalidCertificate: truedisables certificate validation and makes the agent vulnerable to Man-in-the-Middle attacks on untrusted networks. For production deployments on external or untrusted networks, use a trusted certificate from a corporate CA or Let's Encrypt, or implement certificate fingerprint pinning (on the roadmap). For internal corporate LAN deployments the self-signed certificate provides encryption without MITM risk from external attackers.
The server logs a warning if the default certificate password is detected:
[SECURITY] WARNING: Using default certificate password.
Edit appsettings.json → Kestrel.Endpoints.Https.Certificate.Password, then delete the old .pfx file and restart:
del "C:\ProgramData\ZavetSec\DLP\server.pfx"
dotnet DlpServer.dll
:: A new certificate is created with the new passwordBy default the server listens on both HTTP :5000 and HTTPS :5001.
HTTP is included for easier initial setup and fallback troubleshooting,
but transmits data (events, screenshots, API key) in plaintext.
For production deployments, remove the HTTP endpoint from appsettings.json:
"Kestrel": {
"Endpoints": {
"Https": {
"Url": "https://0.0.0.0:5001",
"Certificate": {
"Path": "C:\\ProgramData\\ZavetSec\\DLP\\server.pfx",
"Password": "YOUR_CERT_PASSWORD"
}
}
}
}Then make sure all agents use https:// in config.json:
"serverUrl": "https://YOUR-SERVER:5001"HTTP on port 5000 is acceptable for internal corporate LAN deployments where network traffic is already isolated. Disable it if there is any risk of traffic interception (untrusted VLAN, Wi-Fi, WAN, DMZ).
"Certificate": {
"Path": "C:\\certs\\company.pfx",
"Password": "cert_password"
}No changes needed on the agent side if the certificate is from a trusted CA.
Username: admin
Password: admin
On first login the system forces a password change — the dashboard is blocked until the password is updated.
- Minimum 12 characters
- Must contain uppercase letters (A-Z)
- Must contain lowercase letters (a-z)
- Must contain digits (0-9)
- Must contain special characters (
!@#$%^etc.)
Enforced on both server and client side.
| Role | Permissions |
|---|---|
admin |
Full access: view data, manage agents, manage users, delete data, Telegram settings |
viewer |
Read-only: events, screenshots, keylogger, agent status. No control buttons |
- 5 failed attempts from one IP → 15-minute lockout
- A countdown timer is shown on the login page
- Successful login resets the counter
- Old records are automatically cleaned up every 5 minutes
- Agent endpoints: 1,000 requests/minute per IP
- Exceeded → HTTP 429 with
Retry-After: 60header
- Changing a password (own or another user's via admin) immediately invalidates all sessions for that user
- Active sessions receive HTTP 401 on the next request
PBKDF2-SHA256, 100,000 iterations, 16-byte salt. Session tokens: 32 cryptographically random bytes (hex), 24-hour TTL with sliding expiry.
The agent guarantees data delivery even when the server is unreachable for extended periods:
Server unreachable
│
▼
In-memory queue (maxQueueSize events)
│ queue full
▼
Disk buffer (shipper_queue.dat, up to 50 MB)
│ disk buffer full
▼
Oldest 10% of records trimmed (FIFO)
On server recovery:
1. Agent restarts → LoadPersistedQueue()
2. Buffer file loaded into memory
3. Buffer file deleted
4. Delivery resumes
Exponential backoff on delivery failures:
| Attempt | Wait |
|---|---|
| 1 | 30 s |
| 2 | 60 s |
| 3 | 120 s |
| 4 | 240 s |
| … | … |
| 6+ | 30 min (max) |
- Create a bot via @BotFather →
/newbot→ getBotToken - Message your bot, open
https://api.telegram.org/bot{TOKEN}/getUpdates→ findchat.id - Set in
appsettings.json:
"Telegram": {
"BotToken": "123456789:ABC-DEF...",
"ChatId": "-100123456789"
}Specific modules only (default):
"SendAllAlerts": false,
"AlertModules": ["USB_ALERT", "CLIPBOARD_ALERT", "PROCESS_ALERT", "DNS_ALERT"]All alerts without filter:
"SendAllAlerts": trueRestart the server after changing config. Use the Send test button in the dashboard (Management tab).
Rate limit: at most 1 notification per 30 seconds per host+module pair.
Open at: https://YOUR-SERVER:5001
Language toggle (EN/RU) is in the top-right header. Preference is saved per browser.
Auto-refresh every 30 seconds. Timestamps shown in the browser's local timezone.
- Filters: module, host, date range, full-text search
- Selecting a different host or module in the dropdown auto-applies the filter immediately
- Enter key in any filter field = instant apply
- Date picker change = instant apply
- ⬇ CSV button — exports up to 100,000 rows with current filters; progress shown in KB as it streams; UTF-8 BOM included for correct Excel display
Agent lifecycle events appear in the Events tab with special modules:
AGENT_ONLINE(first heartbeat from a new agent),AGENT_REMOVED(removed from dashboard),AGENT_UNINSTALLED(agent confirmed self-removal). User is shown assystem.
Color coding:
| Color | Modules |
|---|---|
| 🔴 Red | *_ALERT, *_ERROR |
| 🟣 Purple | KEYLOGGER |
| 🔵 Cyan | CLIPBOARD |
| 💙 Blue | SCREENSHOT |
| 🟢 Green | NETWORK_*, DNS_* |
| 🟡 Yellow | USB_*, FILE_* |
| 🩷 Pink | PROCESS_* |
- Grid view with process name and window title
- Case-insensitive search (supports all Unicode)
- Click → full-size modal with metadata
- Delete button in modal (admin only)
- Multi-select mode: checkboxes, select all on page, bulk delete
- Sessions grouped by active window
- Full-text search with match highlighting
- Special keys displayed as:
[BS][TAB][CTRL+C][WIN]
-
Cards showing agent status, 20 agents per page
Indicator Meaning 🟢 Green Online — process alive, monitoring active 🟡 Yellow Stopped — process alive but monitoring paused (after Stop command) ⚫ Grey Offline — no heartbeat for more than 2 minutes -
Status is reported by the agent on every heartbeat poll via
?status=active/stopped -
Admin buttons: Restart / Stop / Uninstall — available for both Online and Offline agents
-
Offline-only: Remove from list — removes from dashboard without uninstalling the agent
-
Per-agent Events/Screenshots buttons filter data by the specific
agent_id— no mixing between agents sharing the same hostname -
Broadcast panel — send commands to all agents at once
-
Server data management panel (admin only)
-
DB size and screenshot disk usage (admin only)
- Chronological log of all admin actions: logins, logouts, data deletions, commands sent, user management
- Color-coded by action type: DELETE (red), CREATE/LOGIN (green), COMMAND (yellow), PASSWORD (purple)
- Full-text search across action, target, and admin username
- Pagination (100 records per page)
- Create / delete users with role assignment (admin / viewer)
- Password status column (OK / Change password)
- Change your own password
- Telegram: connection status, configured alert modules, test notification
- Email (SMTP): configure SMTP host, credentials, recipients, alert modules — saved directly to
appsettings.json; takes effect after server restart - Agent Keys: table of enrolled agents with enrollment date and status; revoke individual agent keys without affecting others
Commands are delivered on the agent's next poll cycle (~flushSeconds seconds).
| Command | Action |
|---|---|
| Start | Start monitoring (if agent process is alive) |
| Stop | Stop monitoring (process stays alive) |
| Restart | Full restart |
| Uninstall | Remove: scheduled tasks, exe, Logs, Screenshots, encryption key |
Uninstall does not delete
events.dbor server screenshots — data is preserved on the server.
Agents tab → ✕ Uninstall → confirm. Agent self-removes in ~30 seconds.
taskkill /IM ZavetSecDlpAgent.exe /F
schtasks /delete /tn "ZavetSec DLP Agent" /f
schtasks /delete /tn "ZavetSec DLP Agent Boot" /f
rd /s /q "C:\ProgramData\ZavetSec\DLP\Logs"
rd /s /q "C:\ProgramData\ZavetSec\DLP\Screenshots"
del /f "C:\ProgramData\ZavetSec\DLP\agent.key"
del /f "C:\ProgramData\ZavetSec\DLP\shipper_queue.dat"
del /f "C:\ProgramData\ZavetSec\Agent\ZavetSecDlpAgent.exe"$computers = Get-Content .\machines.txt
foreach ($pc in $computers) {
Invoke-Command -ComputerName $pc -ScriptBlock {
taskkill /IM ZavetSecDlpAgent.exe /F 2>$null
schtasks /delete /tn "ZavetSec DLP Agent" /f 2>$null
schtasks /delete /tn "ZavetSec DLP Agent Boot" /f 2>$null
Remove-Item "C:\ProgramData\ZavetSec\Agent" -Recurse -Force -EA SilentlyContinue
Remove-Item "C:\ProgramData\ZavetSec\DLP\Logs" -Recurse -Force -EA SilentlyContinue
Write-Host "$env:COMPUTERNAME removed"
} -ErrorAction SilentlyContinue
}Full API reference — click to expand
| Method | Path | Description | Rate Limit |
|---|---|---|---|
| POST | /api/ingest |
Batch event submission (max 1,000) | 1,000/min/IP |
| POST | /api/screenshots/upload |
Upload screenshot | 1,000/min/IP |
| GET | /api/commands/{host} |
Fetch pending commands + heartbeat | 1,000/min/IP |
| POST | /api/commands/result |
Report command execution result | 1,000/min/IP |
| Method | Path | Description | Role |
|---|---|---|---|
| GET | /api/events |
Filtered event list | viewer+ |
| GET | /api/export/events.csv |
CSV export up to 100k rows (UTF-8 BOM) | viewer+ |
| GET | /api/screenshots |
Screenshot list with filters | viewer+ |
| GET | /api/screenshots/{id}/image |
Screenshot image file (public) | — |
| DELETE | /api/screenshots/{id} |
Delete screenshot | admin |
| GET | /api/stats |
Statistics + disk usage | viewer+ |
| GET | /api/hosts |
Known host list | viewer+ |
| GET | /api/agents |
Agent statuses (online/offline) | viewer+ |
| POST | /api/commands |
Send command to agent | admin |
| GET | /api/commands/history |
Command history | viewer+ |
| DELETE | /api/agents/{host} |
Remove agent from list | admin |
| DELETE | /api/data/{host}/screenshots |
Delete host screenshots | admin |
| DELETE | /api/data/{host}/events |
Delete host events | admin |
| DELETE | /api/data/all/screenshots |
Delete all screenshots | admin |
| DELETE | /api/data/all/events |
Delete all events | admin |
| GET | /health |
Health check: status, DB, uptime (public) | — |
| GET | /api/audit |
Admin audit log (delete/command/user actions) | admin |
| Method | Path | Description |
|---|---|---|
| POST | /api/auth/login |
Login → {token, username, role, mustChangePassword} |
| POST | /api/auth/logout |
Logout, delete session |
| GET | /api/auth/me |
Current user info |
| POST | /api/auth/change-password |
Change password + invalidate all sessions |
| Method | Path | Description |
|---|---|---|
| GET | /api/users |
List users |
| POST | /api/users |
Create user |
| DELETE | /api/users/{username} |
Delete user + their sessions |
| Method | Path | Description |
|---|---|---|
| GET | /api/telegram/test |
Send test notification |
| GET | /api/telegram/config |
Current filter configuration |
zavetsec-dlp/
│
├── DlpAgent/
│ ├── DlpAgent.csproj net8.0-windows, self-contained, WinForms
│ ├── Program.cs Entry point: --task-mode / --console / SCM
│ ├── DlpService.cs Module coordinator
│ ├── Config.cs config.json loader, all settings including ShipperConfig
│ ├── Logger.cs AES-256-CBC + DPAPI, Write() / WriteLocal()
│ ├── LogShipper.cs Batch delivery, disk buffer, exponential backoff
│ ├── CommandPoller.cs Remote commands + heartbeat, lazy HttpClient
│ ├── NativeHelpers.cs Win32: windows, user, keyboard layout
│ ├── KeyloggerMonitor.cs WH_KEYBOARD_LL hook
│ ├── ClipboardMonitor.cs Clipboard monitoring + alerts
│ ├── ScreenshotMonitor.cs Screen capture + encryption
│ ├── ScreenshotShipper.cs Upload screenshots, delete local copies
│ ├── NetworkMonitor.cs TCP + DNS monitoring
│ ├── UsbMonitor.cs WMI USB device events
│ ├── FileActivityMonitor.cs FileSystemWatcher on removable drives
│ ├── ProcessMonitor.cs Process launch tracking
│ └── RetentionManager.cs Automatic cleanup of old files
│
├── DlpServer/
│ ├── DlpServer.csproj net8.0, Minimal API, SQLite
│ ├── Program.cs Routes, auth middleware, brute force, rate limit, HTTPS cert
│ ├── EventStore.cs SQLite: all tables, auth, migrations, password validation
│ ├── Models.cs All data models
│ ├── TelegramNotifier.cs Alerts, rate-limit, per-module filters
│ ├── appsettings.json Runtime config (excluded from git — use example)
│ ├── appsettings.example.json Template without secrets
│ └── wwwroot/
│ └── index.html SPA dashboard: 6 tabs, EN/RU i18n, vanilla JS
│
├── install.ps1 Single-machine agent installer
├── deploy.ps1 Mass deployment via WinRM
├── .gitignore Excludes *.pfx, *.db, *.key, publish/, appsettings.json
├── CHANGELOG.md Version history
└── README.md
| Component | Mechanism |
|---|---|
| Passwords | PBKDF2-SHA256, 100,000 iterations, 16-byte salt |
| Sessions | 32-byte random token, 24h TTL with sliding expiry |
| Password change | Immediate invalidation of all user sessions |
| Brute force | 5 failed attempts per IP → 15-minute lockout |
| Rate limiting | 1,000 requests/minute per IP on agent endpoints |
| Transport | HTTPS (port 5001); HTTP (port 5000) open by default — disable in appsettings.json for production |
| Local logs | AES-256-CBC, key in Windows DPAPI machine scope |
| API auth | X-Api-Key (agents) + Bearer token (dashboard) |
| API key model | Shared key for all agents — see Roadmap for per-agent auth |
| Role model | admin / viewer enforced on every endpoint |
| DB migrations | Versioned schema (schema_version table) |
| Admin audit log | All destructive actions logged: delete events/screenshots, commands, user management |
Symptom: SCREENSHOT events appear in the Events tab but nothing in the Screenshots tab.
Cause: The ONLOGON scheduled task is running as SYSTEM instead of the logged-in user.
Fix: Recreate the task without /ru SYSTEM:
taskkill /IM ZavetSecDlpAgent.exe /F
schtasks /delete /tn "ZavetSec DLP Agent" /f
schtasks /create /tn "ZavetSec DLP Agent" ^
/tr "\"C:\ProgramData\ZavetSec\Agent\ZavetSecDlpAgent.exe\" --task-mode" ^
/sc ONLOGON /rl HIGHEST /f
schtasks /run /tn "ZavetSec DLP Agent"Same root cause as above — agent is running as SYSTEM with no access to the user's desktop session (Windows Session 0 Isolation). Apply the fix above.
The certificate server.pfx was created with a different password than what is now in appsettings.json.
This happens when:
- You changed
Certificate.Passwordinappsettings.jsonafter the first run - You copied
appsettings.jsonfrom another machine with a different password - You downloaded a pre-built server release and set a new password
Fix — always the same two steps:
:: Step 1: Delete the old certificate
del "C:\ProgramData\ZavetSec\DLP\server.pfx"
:: Step 2: Restart the server — a new certificate is auto-generated with the current password
dotnet DlpServer.dllExpected output after fix:
[HTTPS] Self-signed certificate created: C:\ProgramData\ZavetSec\DLP\server.pfx
The certificate is always recreated on startup if
server.pfxis missing. Agents do not need any changes — they useallowInvalidCertificate: true.
Verify the agent's config.json has shipper section with all required fields:
"shipper": {
"enabled": true,
"serverUrl": "https://YOUR-SERVER:5001",
"apiKey": "SAME_KEY_AS_APPSETTINGS",
"allowInvalidCertificate": true
}Check allowInvalidCertificate is true when using the self-signed certificate.
The agent is running and locking the exe file. Stop it before rebuilding:
taskkill /IM ZavetSecDlpAgent.exe /F
dotnet publish -c Release -o publish- Verify the server is running:
https://SERVER-IP:5001/healthshould return{"status":"healthy"} - Verify
apiKeyin agentconfig.jsonmatchesDlpServer.ApiKeyinappsettings.json - Verify
serverUrluses the correct IP and port (5001for HTTPS,5000for HTTP) - Check firewall: ports 5000 and 5001 must be open on the server
ZavetSec DLP is a monitoring tool, not a remote access framework. The following boundaries are by design:
- No remote shell — the command system supports only: start, stop, restart, uninstall. No arbitrary code execution.
- No privilege escalation — the agent runs as the logged-in user or SYSTEM (for boot persistence only). It does not attempt to elevate privileges beyond what the scheduled task grants.
- No lateral movement — the agent communicates only with the configured
serverUrl. No peer-to-peer, no broadcast. - No persistence outside scheduled tasks — removal is clean: delete the scheduled task and the install folder.
- Encrypted local logs — all local logs are AES-256-CBC encrypted.
- Watchdog — checks every 60 seconds that event and screenshot shippers are running; re-registers the ONLOGON scheduled task if an insider deletes it. Note: does not protect against a local Administrator killing the process — that requires a kernel-mode component (on the roadmap). Without the machine-specific DPAPI key, captured data is unreadable even if the disk is removed.
- API key required — all agent→server communication requires a pre-shared API key. The server rejects unauthenticated requests with HTTP 401.
- Self-hosted only — no cloud component, no telemetry, no license server.
If you discover a security issue in ZavetSec DLP, please open a private GitHub Security Advisory rather than a public issue. See SECURITY.md for the full security policy.
ZavetSec DLP is not designed to evade antivirus, EDR, or forensic tools. Antivirus exclusions are required because the software uses legitimate OS monitoring APIs (keyboard hooks, screen capture) that behavioral engines flag as suspicious by design. The agent has no obfuscation, packing, or anti-analysis mechanisms.
Recommended configuration for production deployments:
| Recommendation | How |
|---|---|
| Disable HTTP | Remove Http block from appsettings.json |
| VPN-only access | Place server behind VPN; block ports 5000/5001 from the internet |
| Reverse proxy | Put nginx/Caddy in front for TLS termination and access logging |
| Firewall | Allow 5001 only from agent subnet; block all other sources |
| Rotate API key | Update ApiKey in appsettings.json + all agent config.json files periodically |
| Backup | Back up C:\ProgramData\ZavetSec\DLP\ (database + certificate) |
ZavetSec DLP logs personally identifiable information (keystrokes, screenshots, clipboard). Before deploying in your organization:
- GDPR (EU): Requires a lawful basis (legitimate interest or consent), data minimization, retention limits, and employee notification. Configure
retentionLogDaysandretentionScreenshotDaysaccordingly. - Employee notification: In most jurisdictions employees must be informed that their activity is monitored. Document which modules are active.
- Data retention: Set retention periods in
config.json(retentionLogDays,retentionScreenshotDays) to comply with your policy. Server-side data can be deleted per-host from the Management tab. - Access control: Use the
viewerrole for helpdesk staff who only need to view data. Restrictadminrole to security team members only.
The deploying organization is solely responsible for legal compliance. ZavetSec DLP provides the technical controls; the legal framework is your responsibility.
ZavetSec DLP is designed for:
- Corporate insider threat monitoring — detect data exfiltration, policy violations, unauthorized access
- Compliance and auditing — maintain activity records for regulated environments (finance, healthcare, legal)
- Incident response and investigation — reconstruct endpoint activity after a security incident
- IT security operations — visibility into what happens on managed workstations
Not intended for:
- Covert surveillance of individuals without their knowledge in jurisdictions where this is illegal
- Consumer spyware or personal relationship monitoring
- Bypassing OS security mechanisms or endpoint protection
The deploying organization is solely responsible for ensuring compliance with applicable laws, employment agreements, and privacy regulations (GDPR, HIPAA, local labor law, etc.).
Typical resource usage on a monitored workstation (measured on Windows 10, idle desktop):
| Resource | Typical usage |
|---|---|
| RAM | 40–90 MB |
| CPU (idle) | < 1% |
| CPU (screenshot capture) | 2–5% spike, < 1 second |
| Disk I/O | Low burst writes; logs flushed every 30 seconds |
| Network (events) | Batched POST every flushSeconds (default: 30 s); ~1–5 KB per batch |
| Network (screenshots) | 50–300 KB per image depending on JPEG quality and screen content |
Server-side (single server, 50 agents):
- RAM: ~150–300 MB
- Disk: grows with retention settings; ~500 MB/day for 50 active agents with screenshots
- SQLite handles up to ~50 GB comfortably; beyond that, consider archiving old events
Multiple machines with the same hostname are fully supported. Each agent generates a unique 16-character hex agentId on first run, stored in config.json and sent as X-Agent-Id header on every request.
| Component | Behavior with duplicate hostnames |
|---|---|
hosts table |
Primary key is (agent_id, host) — each agent has its own row |
| Events | Every event stores agent_id — dashboard filters by specific agent |
| Screenshots | Every screenshot stores agent_id — per-agent filtering |
| Commands | Stop/Start/Uninstall targets the specific agent_id only |
| Dashboard | Each installation shown as a separate card with id: displayed |
| Remove / Uninstall | Operates on (agent_id, host) pair — does not affect other agents |
Old agent binaries without
agentIdfall back to hostname-only behavior for backward compatibility.
- Shared API key — all agents use the same API key; each agent has a unique
agentIdfor identification, but key-level revocation requires rotating the key for all agents. Per-agent keys are on the roadmap - No certificate pinning —
allowInvalidCertificate: trueaccepts any server certificate; fingerprint pinning is on the roadmap - Agent captures the primary display only (multi-monitor: screen 0 only)
- No code signing certificate — Windows Defender behavioral detection requires an exclusion
- SQLite is not recommended above ~50 GB database size
- Self-signed HTTPS certificate shows a browser warning (expected — click "Proceed")
- HTTP port 5000 is open by default — remove the
Httpendpoint fromappsettings.jsonto disable it in production
Community contributions and pull requests are welcome.
- Per-agent authentication — each agent auto-enrolls on first connect and receives a unique 64-hex key stored in
config.json; revocable per-agent from Management → Agent Keys; falls back to global key for re-enrollment - Certificate fingerprint pinning — agents verify server certificate by SHA-256 fingerprint (
"serverFingerprint"inconfig.json); server prints fingerprint on startup; eliminates MITM risk on untrusted networks - Event integrity chain — HMAC chaining of event batches for tamper-evident forensic logs
- Code signing — signed agent binary to eliminate Windows Defender exclusion requirement
- Explicit consent logging — employee acknowledgement artifact storage for GDPR compliance
- Multi-monitor screenshots — each screen captured separately; files named
HHmmss_trigger_m1.jpg,_m2.jpg; blank monitor detection per screen - Email alerts (SMTP) — configurable from Management tab; rate-limited (1 per host+module per 5 min); supports TLS (587) and SSL (465); background send
- Live feed toggle — ⬤ Live button in dashboard header; switches refresh from 30s to 5s
- PostgreSQL backend — for deployments exceeding SQLite's practical limits (~50 GB)
- WebSocket live feed — true real-time event stream (no polling)
- SIEM integration — Syslog/CEF output for Splunk, QRadar, Elastic SIEM
- Active Directory integration — auto-populate host/user metadata from AD
- Agent auto-update — push new agent binary from server via command channel
- Screenshot compression policies — tiered storage, per-host quotas, archiving
- Agent watchdog — checks every 60s that shippers are alive, re-registers scheduled task if deleted (basic tamper detection)
- Advanced tamper resistance — protected process, kernel callbacks, anti-uninstall (requires driver/kernel-mode component)
See CHANGELOG.md for the full version history.
MIT License — see LICENSE for details.