Windows user guide¶
Install the public Windows payload, enable its service deliberately, and verify acquisition on the actual endpoint. Follow the resulting records through the live ledger, a local dashboard, and an OTLP Collector into Splunk.
Operator edition · 22 October 2026 · ShadowClaw 1.5.1
1. Before you install¶
The Windows implementation targets Windows 10 1809 or later and Windows Server 2019 or later. Use an operating-system release supported by your organization, a 64-bit elevated PowerShell session, and a per-machine CPython installation version 3.9 or later. Install Python for all users from your approved software source. The payload has no third-party Python dependencies and does not bundle Python.
The installer resolves Python from machine registration, not from the first python on PATH. A user-only installation or Store alias is not a substitute for a usable machine runtime. The package is unsigned PowerShell, not an MSI; its installation will not appear as a normal MSI entry in Apps & Features.
Plane A observes process/compute information, Plane B attributes network activity, and Plane C uses a private ETW process stream plus Security event records. Run the service as installed, under LocalSystem, and configure auditing for the events you need. ShadowClaw observes and records; it does not block.
Validate this endpoint
The package's documentation marks Windows acquisition and service behavior as requiring real Windows certification. These steps are an acceptance procedure, not a claim that every Windows build has passed. In particular, a reachable ETW API is not proof that process events are flowing.
This public guide includes operational and auditing configuration. Private detection rules, weights and reporting thresholds are not reproduced.
2. Download and verify¶
Download the Windows ZIP and SHA256SUMS into the same working folder. In PowerShell, change to that folder and run:
Compare all 64 hexadecimal characters with the manifest entry for this exact ZIP. Letter case does not matter. Do not extract or install a mismatched download. The checksum verifies the bytes; it is not an Authenticode signature.
Expand-Archive .\shadowclaw-1.5.1-windows.zip -DestinationPath .\release
Set-Location .\release\shadowclaw-1.5.1-windows
$ReleaseDir = (Get-Location).Path
Keep the extracted folder until acceptance is complete. Its tests directory contains the Windows acquisition test used below; it is not installed as a command on PATH.
3. Install and enable the three planes¶
Install the payload¶
Open PowerShell as Administrator in the extracted release folder. If your organization's policy permits running this verified unsigned installer:
$ReleaseDir = (Get-Location).Path
powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\install.ps1
if ($LASTEXITCODE -ne 0) { throw 'ShadowClaw installation failed' }
The execution-policy option applies to that process. Do not change the machine's policy globally. Domain-enforced policy may still prevent execution; use your organization's approved deployment path in that case.
The installer registers ShadowClawSensor as a manual, stopped service. Starting it is a separate step. In the same elevated 64-bit session, read the installed paths from machine registration:
$SC = Get-ItemProperty 'HKLM:\SOFTWARE\ShadowClaw'
$InstallDir = $SC.InstallDir
$DataDir = $SC.DataDir
$PythonExe = $SC.Python
$ServiceName = $SC.ServiceName
$Claw = Join-Path $InstallDir 'shadowclaw.cmd'
$Config = Join-Path $DataDir 'shadowclaw.json'
& $Claw --version
On a standard installation, the payload is C:\Program Files\ShadowClaw and the machine ledger/configuration is C:\ProgramData\ShadowClaw. The variables above follow the actual registered locations, including a relocated installation. Recreate them in each new PowerShell session before using later commands.
Configure host-event auditing¶
Use your approved Group Policy or endpoint-management policy for fleet deployment. For a local acceptance endpoint, first inspect current settings:
Enable Audit Process Creation - Success and Include command line in process creation events. The latter is under Computer Configuration > Administrative Templates > System > Audit Process Creation. For an English-language local host, the equivalent commands are:
auditpol.exe /set /subcategory:"Process Creation" /success:enable
reg.exe add "HKLM\Software\Microsoft\Windows\CurrentVersion\Policies\System\Audit" `
/v ProcessCreationIncludeCmdLine_Enabled /t REG_DWORD /d 1 /f
Also configure the relevant Advanced Audit Policy subcategories for User Account Management, Security Group Management, Sensitive Privilege Use, Special Logon, Other Object Access Events, and Security System Extension, according to your organization's host-event policy. Use auditpol /list /subcategory:* to find localized names; do not assume English names on a localized OS. Recheck effective policy after Group Policy refresh.
Windows writes command-line audit data into its Security log before ShadowClaw handles it. That log can contain sensitive arguments; restrict access and follow your organization's retention policy. See Microsoft's command-line process auditing guidance.
Enable collection and start the service¶
Preserve the existing policy and change only the host-event switch. Write UTF-8 without a byte-order mark so both Windows PowerShell and Python read the JSON consistently:
Copy-Item $Config ($Config + '.before-guide')
$Settings = Get-Content -Raw $Config | ConvertFrom-Json
$Settings | Add-Member -NotePropertyName enable_esf -NotePropertyValue $true -Force
$Utf8 = New-Object System.Text.UTF8Encoding($false)
[IO.File]::WriteAllText($Config, ($Settings | ConvertTo-Json -Depth 20), $Utf8)
powershell.exe -NoProfile -ExecutionPolicy Bypass `
-File (Join-Path $InstallDir 'enable-service.ps1')
enable_esf is the cross-platform setting name; on Windows it enables Windows event sources. enable-service.ps1 starts the service and sets automatic startup. For later configuration changes, use Restart-Service -Name $ServiceName and verify it returns to Running.
4. Verify installation and service status¶
& $Claw --about
& $Claw --verify
if ($LASTEXITCODE -ne 0) { throw 'Detection matrix failed' }
& $Claw --self-test
Get-Service -Name $ServiceName
Get-CimInstance Win32_Service -Filter "Name='$ServiceName'" |
Select-Object Name, State, StartMode, StartName, ProcessId, PathName
Expect version 1.5.1, every matrix scenario passing, and a Running, automatic service under LocalSystem with a PID. The registration names the installed interpreter and service script. Inspect each self-test plane line even if its final result says PASS; the Windows host-plane probe alone does not prove a live ETW session.
The package creates a logs directory beneath $DataDir. Inspect any files present, and use Event Viewer > Windows Logs > System, filtered to Service Control Manager, for startup or unexpected-exit errors. Do not infer sensor health from an empty log directory. The bounded foreground run in section 5 gives a directly observable startup banner and poll output when service diagnostics are insufficient.
Always view machine records from an elevated session. A normal user run has its own per-user state location; use --ledger path to see which one a command actually reads. Keep the installed ACLs protecting the machine ledger.
5. Test Planes A, B and C¶
What each check establishes¶
| Check | Evidence to keep | What it establishes |
|---|---|---|
--version and --about |
Version and attribution output | The installed command runs the expected payload. |
--verify |
All scenarios pass; command exits successfully | Synthetic inputs exercise the detector logic for Planes A, B, and C, including benign cases. It does not create live ledger findings. |
--self-test |
Per-plane lines, host-plane status, process and socket counts | The current execution context can reach the platform acquisition mechanisms. Read every plane line, even when the final line says PASS. |
| A bounded live run | Startup banner and verbose poll lines | The sensor actually collects processes, connections and host events on this endpoint. |
| A controlled AI session | Time, host, process, provider and matching ledger entry | The real workload reached a recorded finding. |
| Dashboard and destination search | The matching host, time and event | The record reached each configured presentation or central collection path. |
Exercise the three planes¶
Use an endpoint you administer and an approved test account. Before starting, record the host name, time, AI application, provider and expected activity. Use test prompts and disposable files. Keep credentials out of commands and transcripts.
- Plane A - process and compute. During the bounded run below, open your approved local model runtime, if one is available, and run an ordinary inference request. Confirm that the live sample has processes and that verbose polls continue. Look for the runtime in the self-test output and for any corresponding finding. A busy process alone is not a promised detection;
--verifysupplies the repeatable logic check when no model runtime is installed. - Plane B - network. Use an approved AI client to make a real request to its provider while the sensor is running. Record which application and provider you used. Match the process, provider or endpoint, and time in the ledger. A generic website fetch is not an equivalent test of AI-client attribution.
- Plane C - host activity. While the bounded sensor runs, open another terminal and launch a harmless child process with the command shown below. Inspect the host-plane startup line and verbose
esf=event counts. The nameesfis used in the CLI and telemetry across all three platforms. Nonzero counts demonstrate host-event intake; they do not certify every subscription or tactic. Keep anyDEGRADEDorUNAVAILABLEresult as an unresolved acceptance item.
A harmless shell command is not expected to become an agent-tactic finding. The synthetic matrix tests the detection decisions without creating accounts, changing persistence, or touching credentials. To validate a real host-plane alert, use your organization's approved lab exercise and correlate its resulting shadowclaw.agent.activity and finding events. Do not declare that alert path verified solely because the service is running.
Acceptance record¶
Record a separate outcome for each plane: verified, degraded, failed, or not exercised. Retain the version, host, execution identity, test time window, matrix result, per-plane status, ledger verification result, and matching destination event. An empty ledger on an idle host is possible; it is not evidence of full coverage. If a declared workload produces no expected finding, keep that test unresolved and use the troubleshooting section rather than changing detection settings to manufacture a pass.
Prove Windows process-event acquisition¶
With the audit policy in section 3 applied, run the acquisition test from the retained archive under the installed interpreter. Stop the service for this controlled test window:
Stop-Service -Name $ServiceName
& $PythonExe (Join-Path $ReleaseDir 'tests\test-windows-argv-acquisition.py')
$AcquisitionExit = $LASTEXITCODE
Start-Service -Name $ServiceName
if ($AcquisitionExit -ne 0) { throw 'Windows acquisition test did not pass' }
This test launches a harmless fixed-marker child and requires its command line in both the private ETW stream and Security event 4688. Keep the output and exit status. It does not certify every Security event subscription or every tactic, and it does not intentionally produce an attack finding.
Run the sensor visibly¶
In an elevated PowerShell session with the variables from section 3:
Stop-Service -Name $ServiceName
try {
& $Claw --config $Config --ledger-dir $DataDir `
--esf --verbose --duration 120
} finally {
Start-Service -Name $ServiceName
}
Get-Service -Name $ServiceName
While it runs, use the approved AI workload and start a harmless child from another terminal:
Read the startup host-plane subscriptions and the processes=, connections= and esf= poll values. Preserve any partial-coverage warnings. The final OTLP sent/failed summary measures HTTP sends; confirm actual Splunk indexing separately. Until section 7 or 8 provides a receiver, export failures may be expected while the local ledger remains functional.
6. View the live ledger and built-in dashboard¶
Match time, process, provider or endpoint, and severity to your test. Ctrl+C stops this reader, not the service. The reader can run while the service writes. Repeated observations may be grouped into episodes; use --ledger events when you need raw events.
& $Claw --ledger verify
$Report = Join-Path $DataDir 'shadowclaw-report.html'
& $Claw --ledger export --since 24h --format html --out $Report
& $Claw --dashboard
Copy the complete token-bearing loopback URL printed by the dashboard into a browser on this endpoint. Keep the terminal open; Ctrl+C stops the viewer. Add --dashboard-port 0 if its default port is already in use. The built-in view reads the ledger directly and needs neither Docker, Grafana nor a Collector. Keep exported reports in an access-controlled location. A valid hash chain proves integrity of stored events, not full endpoint visibility.
7. View detections in Grafana¶
Start the optional local stack¶
The Windows ZIP does not install Grafana or Loki. On a supported Windows desktop, install Docker Desktop with Linux containers and start it. On Windows Server or a machine without Docker Desktop support, use an organization-managed Grafana/Loki deployment instead; the built-in dashboard remains available locally.
In a separate working directory, under the account that runs Docker Desktop:
docker compose version
Invoke-WebRequest 'https://shadowclaw.tech/assets/user-guides/observability.zip' `
-OutFile .\observability.zip
Expand-Archive .\observability.zip -DestinationPath .\dashboard
Set-Location .\dashboard
$Password = Read-Host 'New Grafana admin password' -AsSecureString
$env:GRAFANA_ADMIN_PASSWORD = [Net.NetworkCredential]::new('', $Password).Password
docker compose config --quiet
docker compose up -d
docker compose ps
Remove-Item Env:\GRAFANA_ADMIN_PASSWORD
Use a strong password. Re-supply the environment variable for later Compose commands that require configuration interpolation. The stack provisions the public operator dashboard and datasource. Check docker compose logs --tail=80 loki grafana if a component fails to start.
In the elevated sensor-administration session, merge the export keys into the existing policy:
$Settings = Get-Content -Raw $Config | ConvertFrom-Json
$Settings | Add-Member -NotePropertyName otlp_endpoint `
-NotePropertyValue 'http://127.0.0.1:3100/otlp' -Force
$Settings | Add-Member -NotePropertyName emit_otlp_logs -NotePropertyValue $true -Force
$Settings | Add-Member -NotePropertyName emit_otlp_metrics -NotePropertyValue $false -Force
$Utf8 = New-Object System.Text.UTF8Encoding($false)
[IO.File]::WriteAllText($Config, ($Settings | ConvertTo-Json -Depth 20), $Utf8)
Restart-Service -Name $ServiceName
Repeat the approved workload and inspect the fresh dashboard records.
Read the Grafana dashboard¶
The optional local stack opens at http://127.0.0.1:3000. Sign in as admin with the password you supplied, then open Dashboards > ShadowClaw > ShadowClaw - operator overview. Its direct path is /d/shadowclaw-operator. The macOS app's native setup installs its own, larger ShadowClaw dashboard; use the URL printed by that setup instead.
Set the time range to Last 15 minutes and refresh. The public operator dashboard shows findings, provider observations, agent activity, and coverage records in separate panels. Expand a log row to inspect its host and process. Use Explore, select the ShadowClaw Loki datasource, and query:
For host-plane observations, change the event filter to shadowclaw.agent.activity. For periodic coverage records, use shadowclaw.ktp.risk_factors and inspect degraded_inputs. An empty agent-activity panel is not a health check. A fresh coverage record shows that logs are arriving; it does not establish full host-plane coverage.
The datasource connects to http://loki:3100 inside the Compose network. The endpoint sensor connects to http://127.0.0.1:3100/otlp on the host. Those addresses serve different callers. The sensor appends /v1/logs. Loki does not accept sensor metrics at that path, so direct-to-Loki configuration must set emit_otlp_metrics to false.
The downloadable stack is for local evaluation, with ports published only on loopback and data in named Docker volumes. It is not a production fleet service. docker compose down stops it and preserves those volumes; adding --volumes deletes the stored dashboard and log data. For a central deployment, use your managed Grafana/Loki service with authentication, TLS, retention and backup policies. Loki's OTLP documentation covers ingestion.
8. Deliver detections centrally with OTLP and Splunk¶
The delivery path¶
Use this path for a central destination such as Splunk:
ShadowClaw sends OTLP/HTTP JSON. Splunk HEC uses a different protocol, so a Collector performs the conversion. Do not point otlp_endpoint at a HEC URL. Use one sensor and one Collector to fan out to multiple destinations; do not start a second sensor against the same ledger.
The sensor accepts plaintext HTTP only on loopback. For a remote OTLP receiver, use an HTTPS base URL with a trusted certificate. Put backend tokens, custom authentication and client-certificate configuration in the Collector. The sensor does not expose those Collector authentication settings.
Prepare Splunk and the Collector¶
- Ask your Splunk administrator for an enabled HEC token permitted to write to a dedicated events index, its exact HTTPS HEC URL, and the appropriate CA trust chain. Use the endpoint supplied for your Splunk Enterprise or Cloud deployment; do not assume a Cloud port or hostname.
- Install the OpenTelemetry Collector Contrib distribution for the host, following its official installation instructions. The
splunk_hecexporter is in Contrib. The example below targets the repository's Collector baseline, 0.158.0; validate it with the version you deploy. - Download collector-splunk.yaml, or save the following complete configuration as that filename. This logs-only path carries all ShadowClaw log event types. Configure the sensor with
emit_otlp_logs: trueandemit_otlp_metrics: falsefor this example.
# ShadowClaw public operator example: logs to a Splunk events index.
# OpenTelemetry Collector Contrib 0.158.0. No detection rules or secrets.
receivers:
otlp:
protocols:
http:
endpoint: 127.0.0.1:4318
processors:
batch:
timeout: 5s
exporters:
splunk_hec:
endpoint: ${env:SPLUNK_HEC_ENDPOINT}
token: ${env:SPLUNK_HEC_TOKEN}
index: ${env:SPLUNK_HEC_INDEX}
source: shadowclaw
sourcetype: shadowclaw:otel
tls:
insecure_skip_verify: false
sending_queue:
enabled: true
queue_size: 1000
retry_on_failure:
enabled: true
initial_interval: 5s
max_interval: 30s
max_elapsed_time: 300s
service:
pipelines:
logs:
receivers: [otlp]
processors: [batch]
exporters: [splunk_hec]
Set SPLUNK_HEC_ENDPOINT, SPLUNK_HEC_INDEX and SPLUNK_HEC_TOKEN in the Collector's environment using the platform commands below. The token stays out of the YAML file. A shell environment belongs to that shell: a Collector installed as a service needs the same values supplied through its service configuration or secret manager.
TLS verification remains enabled. If your HEC certificate uses a private CA, add ca_file: /absolute/path/to/ca.pem under the exporter's tls mapping (use the real local path on Windows). A certificate error is a trust or hostname problem to fix, not a reason to set insecure_skip_verify: true.
See the Contrib HEC exporter reference for supported settings.
Keep Grafana while also sending to Splunk¶
If the local Loki stack is running, add this second exporter to the same Collector file:
exporters:
# Keep the existing splunk_hec exporter here too.
otlp_http/loki:
endpoint: http://127.0.0.1:3100/otlp
Then change service.pipelines.logs.exporters to [splunk_hec, otlp_http/loki]. Merge into the existing exporters mapping; do not create two YAML keys named exporters. Run the Collector on the endpoint host for these loopback addresses to work. The sensor now points only to http://127.0.0.1:4318, and the Collector sends the same logs to both backends. If that port is already owned by DefenseClaw or another Collector, extend the existing pipeline or choose another port and update the sensor to match.
For metrics, add a metrics pipeline and an appropriate metrics backend before setting emit_otlp_metrics back to true. In Splunk, use a separately configured metrics index/exporter. Do not send metrics to the events index in this example or to Loki.
Verify receipt in Splunk¶
First search the events index you configured, over the last 15 minutes:
index=YOUR_EVENTS_INDEX sourcetype="shadowclaw:otel"
| spath
| table _time host event_name process_name pid providers severity
The example exporter sends the sensor's flat JSON body as the event. Find a fresh shadowclaw.ktp.risk_factors record to check the log route even on an idle host. Then perform the approved Plane B workload again and narrow the search:
index=YOUR_EVENTS_INDEX sourcetype="shadowclaw:otel"
| spath
| search event_name="shadowclaw.finding.recorded"
| table _time host process_name pid providers endpoints severity
Match the host, process, provider and time against the local ledger. Use event_name="shadowclaw.agent.activity" for tactics and event_name="shadowclaw.provider.reached" for provider observations. Keep those event types separate when counting detections. If an existing organization pipeline wraps the body, inspect a raw event and adapt the spath input to that wrapper before applying these searches.
The local ledger is written before export. Failed delivery does not automatically replay old ledger entries when connectivity returns. The example Collector has a bounded in-memory queue and retries; it is not durable across Collector restarts. For production, configure persistent Collector storage and an independently tested recovery/backfill procedure. A fresh matching event at the destination is the acceptance criterion, not just a reachable port or a running Collector.
Start the local Collector on Windows¶
After installing Collector Contrib, open PowerShell in the folder containing otelcol-contrib.exe, or use its full installed path. Supply the endpoint and index from your Splunk administrator:
Invoke-WebRequest 'https://shadowclaw.tech/assets/user-guides/collector-splunk.yaml' `
-OutFile .\collector-splunk.yaml
$env:SPLUNK_HEC_ENDPOINT = 'https://splunk.example.com:8088/services/collector'
$env:SPLUNK_HEC_INDEX = 'YOUR_EVENTS_INDEX'
$Token = Read-Host 'Splunk HEC token' -AsSecureString
$env:SPLUNK_HEC_TOKEN = [Net.NetworkCredential]::new('', $Token).Password
.\otelcol-contrib.exe validate --config=collector-splunk.yaml
if ($LASTEXITCODE -ne 0) { throw 'Collector configuration is invalid' }
.\otelcol-contrib.exe --config=collector-splunk.yaml
Leave the Collector running for evaluation. In the elevated sensor session, use the JSON merge from section 7 with otlp_endpoint changed to http://127.0.0.1:4318. Keep logs enabled and metrics disabled for this logs-only pipeline. Restart the sensor, repeat the workload, and verify the matching event in Splunk.
When the foreground Collector stops, remove its shell token with Remove-Item Env:\SPLUNK_HEC_TOKEN. A permanent Collector service needs its own configuration and secret environment; variables in an interactive PowerShell session are not automatically inherited by an existing service. Confirm service startup and fresh destination events after reboot.
9. Troubleshoot by following the record¶
| Symptom | Check and recovery |
|---|---|
| Python cannot be found | Install per-machine CPython 3.9+ and rerun installation. Inspect the interpreter selected by the installer. |
| Scripts blocked | Verify the download and use your organization's execution-policy deployment path. Do not weaken global policy. |
| Service remains Stopped | Installation does not start it. Run enable-service.ps1, then inspect SCM events and the registered image path. |
| Plane C reachable but unproven | Run the shipped acquisition test. Check elevation, ETW errors, effective audit policy and command-line inclusion. |
| Policy changes seem ignored | Inspect the foreground startup banner and loaded file; restart the service after edits. Use UTF-8 without a BOM. |
| Ledger access denied or empty | Use an elevated session and inspect --ledger path. Preserve machine-directory ACLs. |
| Grafana No data | Check containers, datasource, time range and fresh coverage records; make sure Loki metrics are disabled on the sensor. |
| No Splunk event | Read Collector errors, confirm HEC authorization/index and CA trust, and search the correct time window. |
10. Routine operation, upgrade and removal¶
Regularly check the service, fresh coverage records, ledger integrity and central receipt. Monitor free space. Re-run acquisition acceptance after Windows updates, Python replacement, or audit-policy changes. Keep unresolved plane results visible to operators.
Before upgrading, verify the new archive and back up policy and evidence. Use a SQLite-aware backup or stop the sensor before copying the complete machine data directory. Stop the service, run the new installer, enable the service again, and repeat the acceptance checks. The installer preserves existing policy. A Python runtime move also requires reinstalling/re-registering ShadowClaw so the wrapper and service point to the intended interpreter.
To stop monitoring and return the service to manual startup:
powershell.exe -NoProfile -ExecutionPolicy Bypass `
-File (Join-Path $InstallDir 'disable-service.ps1')
To remove the payload and service while preserving evidence:
Removal keeps machine and per-user evidence, including machine policy and logs. Manage Docker storage and the Collector separately. Do not use -PurgeEvidence for routine cleanup. Read more about the ledger, event schemas, and OTLP export.